blob: 36759c999c3f19af5989c66b8573c69b21e219d8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2009 Andrea Bittau, University College London, and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrea Bittau - initial API and implementation from the PsychoPath XPath 2.0
* Jesper Steen Moeller - bug 285145 - don't silently allow empty sequences always
* Jesper Steen Moeller - bug 297707 - Missing the empty-sequence() type
* David Carver - bug 298267 - Correctly handle instof with elements.
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor.internal;
import org.eclipse.wst.xml.xpath2.processor.DynamicError;
import org.eclipse.wst.xml.xpath2.processor.ResultSequence;
import org.eclipse.wst.xml.xpath2.processor.StaticContext;
import org.eclipse.wst.xml.xpath2.processor.internal.ast.*;
import org.eclipse.wst.xml.xpath2.processor.internal.types.*;
import org.w3c.dom.Node;
import java.util.*;
/**
* represents a Sequence types used for matching expected arguments of functions
*/
public class SeqType {
public static final int OCC_NONE = 0;
public static final int OCC_STAR = 1;
public static final int OCC_PLUS = 2;
public static final int OCC_QMARK = 3;
public static final int OCC_EMPTY = 4;
/**
* Path to w3.org XML Schema specification.
*/
public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
private static final QName ANY_ATOMIC_TYPE = new QName("xs",
"anyAtomicType", XML_SCHEMA_NS);
private transient AnyType anytype = null;
private transient int occ;
private transient Class typeClass = null;
private transient QName nodeName = null;
private transient boolean wild = false;
/**
* sequence type
*
* @param t
* is any type
* @param occ
* is an integer in the sequence.
*/
public SeqType(AnyType t, int occ) {
anytype = t;
this.occ = occ;
if (t != null)
typeClass = t.getClass();
else
typeClass = null;
}
/**
* @param occ
* is an integer in the sequence.
*/
// XXX hack to represent AnyNode...
public SeqType(int occ) {
this((AnyType) null, occ);
typeClass = NodeType.class;
}
/**
* @param type_class
* is a class which represents the expected type
* @param occ
* is an integer in the sequence.
*/
public SeqType(Class type_class, int occ) {
this((AnyType) null, occ);
this.typeClass = type_class;
}
/**
* @param st
* is a sequence type.
* @param sc
* is a static context.
*/
public SeqType(SequenceType st, StaticContext sc, ResultSequence rs) {
// convert occurrence
switch (st.occurrence()) {
case SequenceType.EMPTY:
occ = OCC_EMPTY;
return;
case SequenceType.NONE:
occ = OCC_NONE;
break;
case SequenceType.QUESTION:
occ = OCC_QMARK;
break;
case SequenceType.STAR:
occ = OCC_STAR;
break;
case SequenceType.PLUS:
occ = OCC_PLUS;
break;
default:
assert false;
}
// figure out the item is
final ItemType item = st.item_type();
KindTest ktest = null;
switch (item.type()) {
case ItemType.ITEM:
typeClass = AnyType.class;
return;
// XXX IMPLEMENT THIS
case ItemType.QNAME:
final AnyAtomicType aat = sc.make_atomic(item.qname());
assert aat != null;
anytype = aat;
if (item.qname().equals(ANY_ATOMIC_TYPE)) {
typeClass = AnyAtomicType.class;
} else {
typeClass = anytype.getClass();
}
return;
case ItemType.KINDTEST:
ktest = item.kind_test();
break;
}
if (ktest == null) {
return;
}
typeClass = ktest.getXDMClassType();
anytype = ktest.createTestType(rs);
nodeName = ktest.name();
wild = ktest.isWild();
}
/**
* @param t
* is an any type.
*/
public SeqType(AnyType t) {
this(t, OCC_NONE);
}
/**
* @return an integer.
*/
public int occurence() {
return occ;
}
/**
* @return a type.
*/
public AnyType type() {
return anytype;
}
/**
* matches args
*
* @param args
* is a result sequence
* @throws a
* dynamic error
* @return a result sequence
*/
public ResultSequence match(ResultSequence args) throws DynamicError {
int occurrence = occurence();
// Check for empty sequence first
if (occurrence == OCC_EMPTY && !args.empty()) {
throw new DynamicError(TypeError.invalid_type(null));
}
int arg_count = 0;
for (Iterator i = args.iterator(); i.hasNext();) {
AnyType arg = (AnyType) i.next();
// make sure all args are the same type as expected type
if (!(typeClass.isInstance(arg))) {
throw new DynamicError(TypeError.invalid_type(null));
}
if (anytype != null) {
if ((nodeName != null || wild) && arg instanceof NodeType) {
NodeType nodeType = (NodeType) arg;
Node node = nodeType.node_value();
Node lnode = ((NodeType) anytype).node_value();
if (lnode == null) {
//throw new DynamicError(TypeError.invalid_type(null));
continue;
}
if (!lnode.isEqualNode(node)) {
//throw new DynamicError(TypeError.invalid_type(null));
continue;
}
}
}
arg_count++;
}
switch (occurrence) {
case OCC_NONE:
if (arg_count != 1) {
throw new DynamicError(TypeError.invalid_type(null));
}
break;
case OCC_PLUS:
if (arg_count == 0) {
throw new DynamicError(TypeError.invalid_type(null));
}
break;
case OCC_STAR:
break;
case OCC_QMARK:
if (arg_count > 1) {
throw new DynamicError(TypeError.invalid_type(null));
}
break;
default:
assert false;
}
return args;
}
}