/** * Copyright (C) 2010 Orbeon, Inc. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.dom.saxon; import org.orbeon.dom.Document; import org.orbeon.dom.Node; import org.orbeon.dom.QName; import org.orbeon.oxf.xforms.model.InstanceData; import org.orbeon.oxf.xforms.XFormsConstants; import org.orbeon.oxf.xforms.analysis.model.Model; import org.orbeon.oxf.xml.XMLConstants; import org.orbeon.saxon.om.*; import org.orbeon.saxon.trans.Err; import org.orbeon.saxon.trans.XPathException; import org.orbeon.saxon.type.SchemaType; import org.orbeon.saxon.type.Type; import org.orbeon.saxon.value.UntypedAtomicValue; import org.orbeon.saxon.value.Value; /** * This wrapper is an extension of the Saxon node wrapper which is aware of XForms type annotations. */ public class TypedNodeWrapper extends org.orbeon.dom.saxon.NodeWrapper { private TypedNodeWrapper(Node node, org.orbeon.dom.saxon.NodeWrapper parent, int index) { super(node, parent, index); } @Override protected org.orbeon.dom.saxon.NodeWrapper makeWrapper(Node node, org.orbeon.dom.saxon.DocumentWrapper docWrapper, org.orbeon.dom.saxon.NodeWrapper parent, int index) { return makeTypedWrapper(node, docWrapper, parent, index); } static org.orbeon.dom.saxon.NodeWrapper makeTypedWrapper(Node node, org.orbeon.dom.saxon.DocumentWrapper docWrapper, org.orbeon.dom.saxon.NodeWrapper parent, int index) { if (node instanceof Document) { return docWrapper; } else { final org.orbeon.dom.saxon.NodeWrapper wrapper = new TypedNodeWrapper(node, parent, index); wrapper.docWrapper = docWrapper; return wrapper; } } @Override public SequenceIterator getTypedValue() throws XPathException { int annotation = getTypeAnnotation(); if ((annotation & NodeInfo.IS_DTD_TYPE) != 0) { annotation = StandardNames.XS_UNTYPED_ATOMIC; } annotation &= NamePool.FP_MASK; if (annotation == -1 || annotation == StandardNames.XS_UNTYPED_ATOMIC || annotation == StandardNames.XS_UNTYPED) { return SingletonIterator.makeIterator(new UntypedAtomicValue(getStringValueCS())); } else { final SchemaType stype = getConfiguration().getSchemaType(annotation); if (stype == null) { throw new XPathException("Unknown type annotation " + Err.wrap(getAnnotationTypeName(annotation)) + " in document instance"); } else { try { return stype.getTypedValue(this); } catch (Exception err) { throw new TypedValueException(getDisplayName(), getAnnotationTypeName(annotation), getStringValue()); } } } } private String getAnnotationTypeName(int annotation) { try { return getNamePool().getDisplayName(annotation); } catch (Exception err) { return Integer.toString(annotation); } } public static class TypedValueException extends RuntimeException { public final String nodeName; public final String typeName; public final String nodeValue; public TypedValueException(String nodeName, String typeName, String nodeValue) { this.nodeName = nodeName; this.typeName = typeName; this.nodeValue = nodeValue; } } // FIXME: This is almost 100% duplicated from getTypedValue above. @Override public Value atomize() throws XPathException { int annotation = getTypeAnnotation(); if ((annotation & NodeInfo.IS_DTD_TYPE) != 0) { annotation = StandardNames.XS_UNTYPED_ATOMIC; } annotation &= NamePool.FP_MASK; if (annotation == -1 || annotation == StandardNames.XS_UNTYPED_ATOMIC || annotation == StandardNames.XS_UNTYPED) { return new UntypedAtomicValue(getStringValueCS()); } else { final SchemaType stype = getConfiguration().getSchemaType(annotation); if (stype == null) { throw new XPathException("Unknown type annotation " + Err.wrap(getAnnotationTypeName(annotation)) + " in document instance"); } else { try { return stype.atomize(this); } catch (Exception err) { // TODO: Would be good to pass err.getMessage() throw new TypedValueException(getDisplayName(), getAnnotationTypeName(annotation), getStringValue()); } } } } @Override public int getTypeAnnotation() { final QName nodeType = InstanceData.getType((Node) node); if (nodeType == null) { return getUntypedType(); } else { // Extract QName String uri = nodeType.getNamespaceURI(); final String localname = nodeType.getName(); // For type annotation purposes, xforms:integer is translated into xs:integer. This is because XPath has no // knowledge of the XForms union types. if (uri.equals(XFormsConstants.XFORMS_NAMESPACE_URI) && Model.jXFormsVariationTypeNames().contains(localname)) uri = XMLConstants.XSD_URI; final int requestedTypeFingerprint = StandardNames.getFingerprint(uri, localname); if (requestedTypeFingerprint == -1) { // Back to default case return getUntypedType(); } else { // Return identified type return requestedTypeFingerprint; } } } private int getUntypedType() { if (getNodeKind() == Type.ATTRIBUTE) { return StandardNames.XS_UNTYPED_ATOMIC; } return StandardNames.XS_UNTYPED; } }