| /* |
| * Copyright (c) 1998, 2019 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 v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // 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 char getNamespaceSeparator() { |
| return this.namespaceSeparator; |
| } |
| |
| 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; |
| } |
| |
| @Override |
| 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; |
| } |
| |
| @Override |
| 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<>(); |
| return attributeCollisionSet; |
| } |
| |
| private Set<String> getNonAttributeCollisionSet() { |
| if (nonAttributeCollisionSet == null) |
| nonAttributeCollisionSet = new HashSet<>(); |
| return nonAttributeCollisionSet; |
| } |
| |
| } |