/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.internal.oxm; import java.nio.charset.Charset; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; import javax.xml.namespace.QName; import org.eclipse.persistence.internal.oxm.mappings.Field; /** * INTERNAL: * <p><b>Purpose</b>: Represents a token from an XPath statement.</p> * <p>For example the following XPath statment a/b[2]/text() corresponds to three * XPathFragments: "a", "b[2]", and "text()".</p> * <p><b>Responsibilities</b>:<ul> * <li>Maintain name, namespace, and prefix information.</li> * <li>Maintain information about the corresponding node type.</li> * <li>Maintain index information if any. The XPathFragment corresponding to * b[2] would have an index value of 2.</li> * </ul> */ public class XPathFragment < XML_FIELD extends Field >{ public static final XPathFragment TEXT_FRAGMENT = new XPathFragment(Constants.TEXT); public static final String SELF_XPATH = "."; public static final XPathFragment SELF_FRAGMENT = new XPathFragment(SELF_XPATH); public static final XPathFragment ANY_FRAGMENT = null; public static final Charset CHARSET = Charset.forName(Constants.DEFAULT_XML_ENCODING); private XPathFragment nextFragment; private XML_FIELD xmlField; private String xpath; protected boolean hasAttribute = false; private boolean hasText = false; private boolean hasNamespace = false; private boolean containsIndex = false; private int indexValue = -1;//if containsIndex, then this is the value of the index. private boolean shouldExecuteSelectNodes = false; private String shortName; private String prefix; private byte[] prefixBytes; private String localName; private byte[] localNameBytes; private String namespaceURI; protected boolean nameIsText = false; protected boolean isSelfFragment = false; private QName leafElementType; private boolean generatedPrefix = false; private XPathPredicate predicate; private boolean namespaceAware; private char namespaceSeparator; private Set<String> attributeCollisionSet; private Set<String> nonAttributeCollisionSet; public XPathFragment() { setNamespaceAware(true); namespaceSeparator = Constants.COLON; } public XPathFragment(String xpathString) { this(xpathString, Constants.COLON, true); } public XPathFragment(String xpathString, char namespaceSeparator, boolean namespaceAware) { this.namespaceSeparator = namespaceSeparator; setNamespaceAware(namespaceAware); setXPath(xpathString); } public void setPredicate(XPathPredicate condition) { this.predicate = condition; } public boolean isNamespaceAware() { return namespaceAware; } public void setNamespaceAware(boolean isNamespaceAware) { this.namespaceAware = isNamespaceAware; } public void setNamespaceSeparator(char namespaceSeparator) { this.namespaceSeparator = namespaceSeparator; } public XPathPredicate getPredicate() { return predicate; } public XPathFragment getNextFragment() { return nextFragment; } public void setNextFragment(XPathFragment nextFragment) { this.nextFragment = nextFragment; } public void setXPath(String xpathString) { xpath = xpathString; shortName = xpathString; // handle case: company[name/text()="Oracle"] if(xpathString.length() > 0){ if ((xpath.indexOf('[') != -1) && (xpath.indexOf(']') == -1)) { setShouldExecuteSelectNodes(true); return; } // handle case: ancestor::*/jaxb:class/@name if (xpath.indexOf("::") != -1) { setShouldExecuteSelectNodes(true); return; } if (xpathString.charAt(0) == '@') { hasAttribute = true; shortName = xpathString.substring(1).intern(); indexValue = hasIndex(xpathString); setupNamespaceInformation(shortName); return; } if (xpathString.charAt(0) == '/') { setShouldExecuteSelectNodes(true); shortName = xpathString.substring(xpathString.lastIndexOf('/') + 1).intern(); indexValue = hasIndex(xpathString); setupNamespaceInformation(shortName); return; } } if (xpathString.equals(Constants.TEXT)) { nameIsText = true; shortName = xpathString.intern(); return; } else { nameIsText = false; } // handle "self" xpath if (xpathString.equals(SELF_XPATH)) { isSelfFragment = true; shortName = xpathString.intern(); return; } indexValue = hasIndex(xpathString); setupNamespaceInformation(shortName); } private void setupNamespaceInformation(String xpathString) { int nsindex = xpathString.indexOf(namespaceSeparator); if (nsindex != -1) { hasNamespace = true; localName = xpathString.substring(nsindex + 1).intern(); prefix = xpathString.substring(0, nsindex).intern(); } else { localName = xpathString.intern(); } } public boolean isAttribute() { return hasAttribute; } public void setAttribute(boolean isAttribute) { hasAttribute = isAttribute; } public String getShortName() { if(shortName == null){ if(prefix !=null && prefix.length() >0){ shortName = prefix + Constants.COLON + localName; }else{ shortName = localName; } } return shortName; } public String getPrefix() { return prefix; } public byte[] getPrefixBytes() { if(null == prefixBytes && null != prefix) { prefixBytes = prefix.getBytes(CHARSET); } return prefixBytes; } public void setPrefix(String prefix) { this.prefix = prefix; resetShortName(); } public String getLocalName() { return localName; } public byte[] getLocalNameBytes() { if(null == localNameBytes && null != localName) { localNameBytes = localName.getBytes(CHARSET); } return localNameBytes; } public void setLocalName(String localName) { this.localName = localName; resetShortName(); } public String getNamespaceURI() { return namespaceURI; } public void setNamespaceURI(String namespaceURI) { if (isSelfFragment || namespaceURI !=null && namespaceURI.length() == 0) { this.namespaceURI = null; } else { this.namespaceURI = namespaceURI; } } private int hasIndex(String xpathString) { int index = -1; int startindex = xpathString.lastIndexOf('['); if ((startindex != -1) && (xpathString.lastIndexOf(']') != -1)) { StringTokenizer st = new StringTokenizer(xpathString, "[]"); String element = st.nextToken(); while(st.hasMoreTokens()) { String indexString = st.nextToken(); try { index = Integer.parseInt(indexString); setContainsIndex(true); } catch (NumberFormatException e) { int equalsOffset = indexString.indexOf('='); if (equalsOffset >= 0) { XPathFragment xPathFragment = new XPathFragment(indexString.substring(0, equalsOffset)); String value = indexString.substring(equalsOffset + 2, indexString.length() - 1); predicate = new XPathPredicate(xPathFragment, value); } else { setContainsIndex(true); } setShouldExecuteSelectNodes(true); } } shortName = element; } else { index = -1; } return index; } public int getIndexValue() { return indexValue; } public void setIndexValue(int indexValue) { this.indexValue = indexValue; } public String getXPath() { return xpath; } public boolean hasNamespace() { return hasNamespace; } /** * INTERNAL: * Indicates if the xpath is "." * * @return true if the xpath is ".", false otherwise */ public boolean isSelfFragment() { return isSelfFragment; } public boolean nameIsText() { return nameIsText; } public void setHasText(boolean hasText) { this.hasText = hasText; } public boolean getHasText() { return hasText; } public void setContainsIndex(boolean containsIndex) { this.containsIndex = containsIndex; } public boolean containsIndex() { return containsIndex; } public void setShouldExecuteSelectNodes(boolean newShouldExecuteSelectNodes) { this.shouldExecuteSelectNodes = newShouldExecuteSelectNodes; } public boolean shouldExecuteSelectNodes() { return shouldExecuteSelectNodes; } public boolean equals(Object object) { return equals(object, false); } public boolean equals(Object object, boolean ignorePredicate) { if (null == object) { return false; } else if (this == object) { return true; } try { XPathFragment xPathFragment = (XPathFragment) object; if (hasAttribute && !xPathFragment.hasAttribute) { return false; } if (nameIsText && xPathFragment.nameIsText) { return true; } if (nameIsText != xPathFragment.nameIsText) { return false; } if ((null == localName && null != xPathFragment.localName) || (null != localName && null == xPathFragment.localName)) { return false; } if (null != localName && !localName.equals(xPathFragment.localName)) { return false; } if (namespaceAware && xPathFragment.isNamespaceAware()) { if ((null == namespaceURI && null != xPathFragment.namespaceURI) || (null != namespaceURI && null == xPathFragment.namespaceURI)) { return false; } if (null != namespaceURI && !namespaceURI.equals(xPathFragment.namespaceURI)) { return false; } } if (indexValue != xPathFragment.indexValue) { return false; } if(!ignorePredicate) { if (null == predicate && null != xPathFragment.predicate) { return false; } if (null != predicate && !predicate.equals(xPathFragment.predicate)) { return false; } } } catch (ClassCastException e) { return false; } return true; } public int hashCode() { if(null == localName) { return 1; } else { return localName.hashCode(); } } public QName getLeafElementType() { return leafElementType; } public boolean hasLeafElementType() { return leafElementType != null; } public void setLeafElementType(QName type) { leafElementType = type; } public void setGeneratedPrefix(boolean isGenerated) { generatedPrefix = isGenerated; } public boolean isGeneratedPrefix() { return generatedPrefix; } public XML_FIELD getXMLField() { return this.xmlField; } public void setXMLField(XML_FIELD field) { this.xmlField = field; } private void resetShortName(){ shortName = null; prefixBytes = null; localNameBytes = null; } /** * INTERNAL: * Gets auxiliary set for determining collisions during case insensitive unmarshalling. * * @param isAttribute Determine if retrieving an element or an attribute collision set. * @return * Set containing localNames of attributes or elements of an XPathFragment. */ public Set<String> getChildrenCollisionSet(boolean isAttribute) { return isAttribute ? getAttributeCollisionSet() : getNonAttributeCollisionSet(); } private Set<String> getAttributeCollisionSet() { if (attributeCollisionSet == null) attributeCollisionSet = new HashSet<String>(); return attributeCollisionSet; } private Set<String> getNonAttributeCollisionSet() { if (nonAttributeCollisionSet == null) nonAttributeCollisionSet = new HashSet<String>(); return nonAttributeCollisionSet; } }