blob: 9ae81a6b2a2f141add8fb33681d105fa84c877dd [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
* Mukul Gandhi - bug 276134 - improvements to schema aware primitive type support
* for attribute/element nodes
* David Carver (STAR)- bug 277774 - XSDecimal returning wrong values.
* Jesper Moller - bug 275610 - Avoid big time and memory overhead for externals
* David Carver (STAR) - bug 281186 - implementation of fn:id and fn:idref
* David Carver (STAR) - bug 289304 - fixed schema awareness on elements
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor.internal.types;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import org.apache.xerces.xs.XSTypeDefinition;
import org.eclipse.wst.xml.xpath2.processor.ResultSequence;
import org.eclipse.wst.xml.xpath2.processor.ResultSequenceFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import org.w3c.dom.TypeInfo;
/**
* A representation of a Node datatype
*/
public abstract class NodeType extends AnyType {
protected static final String SCHEMA_TYPE_IDREF = "IDREF";
protected static final String SCHEMA_TYPE_ID = "ID";
private Node _node;
/**
* Initialises according to the supplied parameters
*
* @param node
* The Node being represented
* @param document_order
* The document order
*/
public NodeType(Node node) {
_node = node;
}
/**
* Retrieves the actual node being represented
*
* @return Actual node being represented
*/
public Node node_value() {
return _node;
}
// Accessors defined in XPath Data model
// http://www.w3.org/TR/xpath-datamodel/
/**
* Retrieves the actual node being represented
*
* @return Actual node being represented
*/
public abstract ResultSequence typed_value();
/**
* Retrieves the name of the node
*
* @return QName representation of the name of the node
*/
public abstract QName node_name(); // may return null ["empty sequence"]
// XXX element should override
public ResultSequence nilled() {
return ResultSequenceFactory.create_new();
}
// a little factory for converting from DOM to our representation
public static NodeType dom_to_xpath(Node node) {
assert node != null;
switch (node.getNodeType()) {
case Node.ELEMENT_NODE:
return new ElementType((Element) node);
case Node.COMMENT_NODE:
return new CommentType((Comment) node);
case Node.ATTRIBUTE_NODE:
return new AttrType((Attr) node);
case Node.TEXT_NODE:
return new TextType((Text) node);
case Node.DOCUMENT_NODE:
return new DocType((Document) node);
case Node.PROCESSING_INSTRUCTION_NODE:
return new PIType((ProcessingInstruction) node);
// XXX
default:
assert false;
}
// unreach... hopefully
return null;
}
public static ResultSequence eliminate_dups(ResultSequence rs) {
Hashtable added = new Hashtable(rs.size());
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType node = (NodeType) i.next();
Node n = node.node_value();
if (added.containsKey(n))
i.remove();
else
added.put(n, Boolean.TRUE);
}
return rs;
}
public static ResultSequence sort_document_order(ResultSequence rs) {
ArrayList res = new ArrayList(rs.size());
for (Iterator i = rs.iterator(); i.hasNext();) {
NodeType node = (NodeType) i.next();
boolean added = false;
for (int j = 0; j < res.size(); j++) {
NodeType x = (NodeType) res.get(j);
if (before(node, x)) {
res.add(j, node);
added = true;
break;
}
}
if (!added)
res.add(node);
}
rs = ResultSequenceFactory.create_new();
for (Iterator i = res.iterator(); i.hasNext();) {
NodeType node = (NodeType) i.next();
rs.add(node);
}
return rs;
}
public static boolean same(NodeType a, NodeType b) {
return (a.node_value().isSameNode(b.node_value()));
// While compare_node(a, b) == 0 is tempting, it is also expensive
}
public boolean before(NodeType two) {
return before(this, two);
}
public static boolean before(NodeType a, NodeType b) {
return compare_node(a, b) < 0;
}
public boolean after(NodeType two) {
return after(this, two);
}
public static boolean after(NodeType a, NodeType b) {
return compare_node(a, b) > 0;
}
private static int compare_node(NodeType a, NodeType b) {
Node nodeA = a.node_value();
Node nodeB = b.node_value();
if (nodeA == nodeB || nodeA.isSameNode(nodeB)) return 0;
Document docA = getDocument(nodeA);
Document docB = getDocument(nodeB);
if (docA != docB && ! docA.isSameNode(docB)) {
return compareDocuments(docA, docB);
}
short relation = nodeA.compareDocumentPosition(nodeB);
if ((relation & Node.DOCUMENT_POSITION_PRECEDING) != 0)
return 1;
if ((relation & Node.DOCUMENT_POSITION_FOLLOWING) != 0)
return -1;
throw new RuntimeException("Unexpected result from node comparison: " + relation);
}
private static int compareDocuments(Document docA, Document docB) {
// Arbitrary but fulfills the spec (provided documenURI is always set)
if (docB.getDocumentURI() == null && docA.getDocumentURI() == null) {
// Best guess
return 0;
}
return docB.getDocumentURI().compareTo(docA.getDocumentURI());
}
private static Document getDocument(Node nodeA) {
return nodeA instanceof Document ? (Document)nodeA : nodeA.getOwnerDocument();
}
protected Object getTypedValueForPrimitiveType(XSTypeDefinition typeDef) {
Object schemaTypeValue = null;
if (typeDef == null) {
return new XSUntypedAtomic(string_value());
}
String typeName = typeDef.getName();
if ("date".equals(typeName)) {
schemaTypeValue = XSDate.parse_date(string_value());
}
else if ("int".equals(typeName)) {
schemaTypeValue = new XSInt(new BigInteger(string_value()));
}
else if ("long".equals(typeName)) {
schemaTypeValue = new XSLong(new BigInteger(string_value()));
}
else if ("integer".equals(typeName)) {
schemaTypeValue = new XSInteger(new BigInteger(string_value()));
}
else if ("double".equals(typeName)) {
schemaTypeValue = new XSDouble(Double.parseDouble(string_value()));
}
else if ("float".equals(typeName)) {
schemaTypeValue = new XSFloat(Float.parseFloat(string_value()));
}
else if ("decimal".equals(typeName)) {
schemaTypeValue = new XSDecimal(new BigDecimal(string_value()));
} else if ("dateTime".equals(typeName)) {
schemaTypeValue = XSDateTime.parseDateTime(string_value());
} else if ("time".equals(typeName)) {
schemaTypeValue = XSTime.parse_time(string_value());
} else if ("date".equals(typeName)) {
schemaTypeValue = XSDate.parse_date(string_value());
} else if ("boolean".equals(typeName)) {
schemaTypeValue = new XSBoolean(Boolean.valueOf(string_value()));
} else if ("NOTATION".equals(typeName)) {
schemaTypeValue = new XSString(string_value());
}
return schemaTypeValue;
}
public abstract boolean isID();
public abstract boolean isIDREF();
/**
* Utility method to check to see if a particular TypeInfo matches.
* @param typeInfo
* @param typeName
* @return
*/
protected boolean isType(TypeInfo typeInfo, String typeName) {
if (typeInfo != null) {
String typeInfoName = typeInfo.getTypeName();
if (typeInfoName != null) {
if (typeInfo.getTypeName().equalsIgnoreCase(typeName)) {
return true;
}
}
}
return false;
}
}