All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.tree.tiny.TinyElementImpl Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2022 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.tree.tiny;

import net.sf.saxon.Configuration;
import net.sf.saxon.event.CopyNamespaceSensitiveException;
import net.sf.saxon.event.Receiver;
import net.sf.saxon.event.ReceiverOption;
import net.sf.saxon.expr.parser.Loc;
import net.sf.saxon.om.*;
import net.sf.saxon.s9api.Location;
import net.sf.saxon.str.CompressedWhitespace;
import net.sf.saxon.str.EmptyUnicodeString;
import net.sf.saxon.str.UnicodeString;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.util.Navigator;
import net.sf.saxon.type.*;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.z.IntHashMap;


/**
 * A node in the XML parse tree representing an XML element.
 * 

This class is an implementation of NodeInfo. The object is a wrapper around * one entry in the arrays maintained by the TinyTree. Note that the same node * might be represented by different TinyElementImpl objects at different times.

*/ public class TinyElementImpl extends TinyParentNodeImpl { /** * Constructor - create a tiny element node * * @param tree the Tinytree containing the node * @param nodeNr the node number */ public TinyElementImpl(TinyTree tree, int nodeNr) { this.tree = tree; this.nodeNr = nodeNr; } /** * Return the type of node. * * @return Type.ELEMENT */ @Override public final int getNodeKind() { return Type.ELEMENT; } /** * Get the base URI of this element node. This will be the same as the System ID unless * xml:base has been used. */ @Override public String getBaseURI() { if (tree.getUniformBaseUri() != null) { return tree.getUniformBaseUri(); } synchronized (tree) { if (tree.knownBaseUris == null) { tree.knownBaseUris = new IntHashMap<>(); } String uri = tree.knownBaseUris.get(nodeNr); if (uri == null) { uri = Navigator.getBaseURI(this, n -> tree.isTopWithinEntity(((TinyElementImpl) n).getNodeNumber())); tree.knownBaseUris.put(nodeNr, uri); } return uri; } } /** * Get the type annotation of this node, if any. The type annotation is represented as * SchemaType object. *

Types derived from a DTD are not reflected in the result of this method.

* * @return For element and attribute nodes: the type annotation derived from schema * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema * validation). For comments, text nodes, processing instructions, and namespaces: null. * For document nodes, either xs:untyped if the document has not been validated, or * xs:anyType if it has. * @since 9.4 */ @Override public SchemaType getSchemaType() { return tree.getSchemaType(nodeNr); } /** * Get the typed value. * * @return the typed value. It will be a Value representing a sequence whose items are atomic * values. */ @Override public AtomicSequence atomize() throws XPathException { return tree.getTypedValueOfElement(this); } /** * Get all namespace undeclarations and undeclarations defined on this element. * * @param buffer If this is non-null, and the result array fits in this buffer, then the result * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap. * @return An array of objects representing the namespace declarations and undeclarations present on * this element. For a node other than an element, return null. Otherwise, the returned array is a * sequence of namespace binding objects (essentially prefix/uri pairs) * If the URI is null, then this is a namespace undeclaration rather than a declaration. * The XML namespace is never included in the list. If the supplied array is larger than required, * then the first unused entry will be set to null. */ /*@Nullable*/ @Override public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) { TinyNodeImpl parent = getParent(); if (parent != null && parent.getNodeKind() == Type.ELEMENT) { return getAllNamespaces().getDifferences(parent.getAllNamespaces(), false); } else { return getAllNamespaces().getNamespaceBindings(); } } @Override public NamespaceMap getAllNamespaces() { return tree.namespaceMaps[tree.beta[nodeNr]]; } /** * Ask whether the element is the root of a subtree in which no descendant element * has any local namespace declarations or undeclarations; that is, all elements * in the subtree have the same in-scope namespace bindings. * @return true if it is known that no descendant elements have in-scope namespaces * different from those of this element. */ public boolean hasUniformNamespaces() { return false; // TODO: temporarily // int nr = nodeNr; // int ns = tree.beta[nr]; // TinyElementImpl anc = this; // while (ns == -1) { // TinyNodeImpl parent = anc.getParent(); // if (parent instanceof TinyDocumentImpl) { // return !tree.usesNamespaces; // } else { // nr = parent.nodeNr; // ns = tree.beta[nr]; // anc = (TinyElementImpl)parent; // } // } // // Find the first namespace binding with a different parent // while (ns < tree.numberOfNamespaces && tree.namespaceParent[ns] == nr) { // ns++; // } // // Return true if none is found, or if the element owning this namespace binding // // is outside the subtree // return ns >= tree.numberOfNamespaces || !isAncestorOrSelf(tree.getNode(tree.namespaceParent[ns])); } /** * Get the string value of a given attribute of this node * * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute * that is in no namespace * @param local the local part of the attribute name. * @return the attribute value if it exists, or null if it does not exist. Always returns null * if this node is not an element. * @since 9.4 */ @Override public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) { int a = tree.alpha[nodeNr]; if (a < 0) { return null; } NamePool pool = getNamePool(); while (a < tree.numberOfAttributes && tree.attParent[a] == nodeNr) { int nc = tree.attCode[a]; // Avoid allocating a name code for an ad-hoc request StructuredQName name = pool.getUnprefixedQName(nc); if (name.getLocalPart().equals(local) && name.hasURI(uri)) { return tree.attValue[a].toString(); } a++; } return null; } /** * Get the value of the attribute with a given fingerprint. * * @param fp the fingerprint of the required attribute * @return the string value of the attribute if present, or null if absent */ public String getAttributeValue(int fp) { // NB: Used from generated bytecode int a = tree.alpha[nodeNr]; if (a < 0) { return null; } while (a < tree.numberOfAttributes && tree.attParent[a] == nodeNr) { if (fp == (tree.attCode[a] & NamePool.FP_MASK)) { return tree.attValue[a].toString(); } a++; } return null; } private int subtreeSize() { int next = tree.next[nodeNr]; while (next < nodeNr) { if (next < 0) { return tree.numberOfNodes - nodeNr; } next = tree.next[next]; } return nodeNr - next; } /** * Copy this node to a given receiver * * @param receiver the destination of the copy * @param copyOptions determines handling of namespaces, etc * @param location location information associated with the event */ @Override public void copy(/*@NotNull*/ Receiver receiver, int copyOptions, Location location) throws XPathException { boolean copyTypes = CopyOptions.includes(copyOptions, CopyOptions.TYPE_ANNOTATIONS); short level = -1; boolean closePending = false; short startLevel = tree.depth[nodeNr]; boolean disallowNamespaceSensitiveContent = ((copyOptions & CopyOptions.TYPE_ANNOTATIONS) != 0) && ((copyOptions & CopyOptions.ALL_NAMESPACES) == 0); Configuration config = tree.getConfiguration(); NamePool pool = config.getNamePool(); int next = nodeNr; java.util.function.Function informee = receiver.getPipelineConfiguration().getCopyInformee(); SchemaType elementType = Untyped.getInstance(); SimpleType attributeType = BuiltInAtomicType.UNTYPED_ATOMIC; do { // determine node depth short nodeLevel = tree.depth[next]; // extra close required? if (closePending) { level++; } // close former elements for (; level > nodeLevel; level--) { receiver.endElement(); } // new node level level = nodeLevel; // output depends on node kind int kind = tree.nodeKind[next]; switch (kind) { case Type.ELEMENT: case Type.TEXTUAL_ELEMENT: { // start element if (copyTypes) { elementType = tree.getSchemaType(next); if (disallowNamespaceSensitiveContent) { try { checkNotNamespaceSensitiveElement(elementType, next); } catch (CopyNamespaceSensitiveException e) { e.setErrorCode(receiver.getPipelineConfiguration().isXSLT() ? "XTTE0950" : "XQTY0086"); throw e; } } } if (informee != null) { Location loc = (Location)informee.apply(tree.getNode(next)); if (loc != null) { location = loc; } } int nameCode = tree.nameCode[next]; int fp = nameCode & NamePool.FP_MASK; String prefix = tree.getPrefix(next); if (location.getLineNumber() < tree.getLineNumber(next)) { String systemId = location.getSystemId() == null ? getSystemId() : location.getSystemId(); location = new Loc(systemId, tree.getLineNumber(next), getColumnNumber()); } // decide on namespaces NamespaceMap namespaces = NamespaceMap.emptyMap(); boolean addAttributeNamespaces = false; if (tree.usesNamespaces) { if ((copyOptions & CopyOptions.ALL_NAMESPACES) != 0) { if (kind == Type.TEXTUAL_ELEMENT) { int parent = getParentNodeNr(tree, next); namespaces = tree.namespaceMaps[tree.beta[parent]]; } else { namespaces = tree.namespaceMaps[tree.beta[next]]; } } else { addAttributeNamespaces = true; String uri = pool.getURI(nameCode); if (!uri.isEmpty()) { namespaces = NamespaceMap.of(prefix, uri); } } } if (kind == Type.TEXTUAL_ELEMENT) { closePending = false; receiver.startElement(new CodedName(fp, prefix, pool), elementType, EmptyAttributeMap.getInstance(), namespaces, location, ReceiverOption.BEQUEATH_INHERITED_NAMESPACES_ONLY); // output characters final UnicodeString value = TinyTextImpl.getStringValue(tree, next); receiver.characters(value, location, ReceiverOption.WHOLE_TEXT_NODE); receiver.endElement(); } else { // there is an element to close closePending = true; // output attributes AttributeMap attributes = EmptyAttributeMap.getInstance(); int att = tree.alpha[next]; if (att >= 0) { while (att < tree.numberOfAttributes && tree.attParent[att] == next) { int attCode = tree.attCode[att]; int attfp = attCode & NamePool.FP_MASK; if (copyTypes) { attributeType = tree.getAttributeType(att); if (disallowNamespaceSensitiveContent) { try { checkNotNamespaceSensitiveAttribute(attributeType, att); } catch (CopyNamespaceSensitiveException e) { e.setErrorCode(receiver.getPipelineConfiguration().isXSLT() ? "XTTE0950" : "XQTY0086"); throw e; } } } String attPrefix = tree.prefixPool.getPrefix(attCode >> 20); int attProps = ReceiverOption.NOT_A_DUPLICATE; if (tree.isIdAttribute(att)) { attProps |= ReceiverOption.IS_ID; } if (tree.isIdrefAttribute(att)) { attProps |= ReceiverOption.IS_IDREF; } attributes = attributes.put(new AttributeInfo(new CodedName(attfp, attPrefix, pool), attributeType, tree.attValue[att].toString(), location, attProps)); if (addAttributeNamespaces && !attPrefix.isEmpty()) { namespaces = namespaces.put(attPrefix,pool.getURI(attCode)); } att++; } } // see bug 2209 receiver.startElement(new CodedName(fp, prefix, pool), elementType, attributes, namespaces, location, ReceiverOption.BEQUEATH_INHERITED_NAMESPACES_ONLY); } break; } case Type.TEXT: { // don't close text nodes closePending = false; // output characters final UnicodeString value = TinyTextImpl.getStringValue(tree, next); receiver.characters(value, location, ReceiverOption.WHOLE_TEXT_NODE); break; } case Type.WHITESPACE_TEXT: { // don't close text nodes closePending = false; // output characters long compressedValue = ((long) tree.alpha[next] << 32) | ((long) tree.beta[next] & 0xffffffffL); final UnicodeString value = new CompressedWhitespace(compressedValue); receiver.characters(value, location, ReceiverOption.WHOLE_TEXT_NODE); break; } case Type.COMMENT: { // don't close text nodes closePending = false; // output copy of comment int start = tree.alpha[next]; int len = tree.beta[next]; if (len > 0) { receiver.comment(tree.commentBuffer.substring(start, start + len), location, ReceiverOption.NONE); } else { receiver.comment(EmptyUnicodeString.getInstance(), Loc.NONE, ReceiverOption.NONE); } break; } case Type.PROCESSING_INSTRUCTION: { // don't close text nodes closePending = false; // output copy of PI NodeInfo pi = tree.getNode(next); receiver.processingInstruction(pi.getLocalPart(), pi.getUnicodeStringValue(), location, ReceiverOption.NONE); break; } case Type.PARENT_POINTER: { closePending = false; break; } } next++; } while (next < tree.numberOfNodes && tree.depth[next] > startLevel); // close all remaining elements if (closePending) { level++; } for (; level > startLevel; level--) { receiver.endElement(); } } /** * Check whether the content of an element is namespace-sensitive * * * @param type the type annotation of the node * @param nodeNr the the node number of the elemente * @throws XPathException if an error occurs */ protected void checkNotNamespaceSensitiveElement(SchemaType type, int nodeNr) throws XPathException { if (type instanceof SimpleType && ((SimpleType) type).isNamespaceSensitive()) { if (type.isAtomicType()) { throw new CopyNamespaceSensitiveException( "Cannot copy QName or NOTATION values without copying namespaces"); } else { // For a union or list type, we need to check whether the actual value is namespace-sensitive AtomicSequence value = tree.getTypedValueOfElement(nodeNr); for (AtomicValue val : value) { if (val.getPrimitiveType().isNamespaceSensitive()) { throw new CopyNamespaceSensitiveException( "Cannot copy QName or NOTATION values without copying namespaces"); } } } } } /** * Check whether the content of an attribute is namespace-sensitive * * * @param type the type annotation of the node * @param nodeNr the the node number of the elemente * @throws XPathException if an error occurs */ private void checkNotNamespaceSensitiveAttribute(SimpleType type, int nodeNr) throws XPathException { if (type.isNamespaceSensitive()) { if (type.isAtomicType()) { throw new CopyNamespaceSensitiveException( "Cannot copy QName or NOTATION values without copying namespaces"); } else { // For a union or list type, we need to check whether the actual value is namespace-sensitive AtomicSequence value = tree.getTypedValueOfAttribute(null, nodeNr); for (AtomicValue val : value) { if (val.getPrimitiveType().isNamespaceSensitive()) { throw new CopyNamespaceSensitiveException( "Cannot copy QName or NOTATION values without copying namespaces"); } } } } } /** * Get the namespace URI corresponding to a given prefix. Return null * if the prefix is not in scope. * * @param prefix the namespace prefix. May be the zero-length string, indicating * that there is no prefix. This indicates either the default namespace or the * null namespace, depending on the value of useDefault. * @param useDefault true if the default namespace is to be used when the * prefix is "". If false, the method returns "" when the prefix is "". * @return the uri for the namespace, or null if the prefix is not in scope. * The "null namespace" is represented by the pseudo-URI "". */ /*@Nullable*/ public String getURIForPrefix(/*@Nullable*/ String prefix, boolean useDefault) { if (!useDefault && (prefix == null || prefix.isEmpty())) { return ""; } int ns = tree.beta[nodeNr]; // by convention NamespaceMap map = tree.namespaceMaps[ns]; return map.getURIForPrefix(prefix, useDefault); // if (ns > 0) { // while (ns < tree.numberOfNamespaces && // tree.namespaceParent[ns] == nodeNr) { // NamespaceBinding nscode = tree.namespaceBinding[ns]; // if (nscode.getPrefix().equals(prefix)) { // String uri = nscode.getURI(); // if (uri.isEmpty()) { // // this is a namespace undeclaration, so the prefix is not in scope // if (prefix.isEmpty()) { // // the namespace xmlns="" is always in scope // return ""; // } else { // return null; // } // } else { // return uri; // } // } // ns++; // } // } // // // now search the namespaces defined on the ancestor nodes. // // NodeInfo parent = getParent(); // if (parent instanceof NamespaceResolver) { // return ((NamespaceResolver) parent).getURIForPrefix(prefix, useDefault); // } // return null; } /** * Determine whether this node has the is-id property * * @return true if the node is an ID */ @Override public boolean isId() { // this looks very inefficient, but the method isn't actually used... return tree.isIdElement(nodeNr); } /** * Determine whether this node has the is-idref property * * @return true if the node is an IDREF or IDREFS element or attribute */ @Override public boolean isIdref() { return tree.isIdrefElement(nodeNr); } private boolean isSkipValidator(Receiver r) { return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy