/*******************************************************************************
* 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.tools.workbench.mappingsmodel.xml;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWAttributeDeclaration;
import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWElementDeclaration;
import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWNamespace;
import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWSchemaContextComponent;
import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWXpathableSchemaComponent;
final class MWXpathStep
extends MWModel
{
// **************** Variables *********************************************
/** The entire string of the step (includes namespace prefix) */
private transient String stepString;
/** The resolved namespace used for prefixing this step (may be null) */
private transient MWNamespace namespace;
/** The resolved component (may be null) */
private MWXpathableSchemaComponent xpathComponent;
/** This step points to a text node */
private boolean isText;
/** This step points to an attribute node */
private boolean isAttribute;
/** The local name of the node this step points to (no prefix) */
private String localName = "";
/** The position of this step (if applicable) */
private int position = ANY_POSITION;
/** The default position */
private static int ANY_POSITION = 0;
/** Return true if this xpath step has been resolved to a valid schema location */
private transient boolean resolved;
/** Whether this xpath step is valid */
private transient boolean valid;
// **************** Constructors ******************************************
private MWXpathStep(MWXmlField parent) {
super(parent);
}
MWXpathStep(MWXmlField parent, String stepString) {
this(parent);
this.stepString = stepString;
}
// **************** "Exposed" *********************************************
boolean isText() {
return this.isText;
}
boolean isAttribute() {
return this.xpathComponent instanceof MWAttributeDeclaration;
}
boolean isElement() {
return this.xpathComponent instanceof MWElementDeclaration;
}
boolean isPositional() {
return this.position != ANY_POSITION;
}
/**
* Return true if my position is not ANY_POSITION
* or if my component has a max occurs of 1 or less
*/
boolean isSingular() {
if (this.xpathComponent != null && this.xpathComponent.getMaxOccurs() == -1) {
return false;
} else if (this.isText
|| this.position != ANY_POSITION
|| this.xpathComponent.getMaxOccurs() <= 1) {
return true;
}
return false;
}
MWXpathableSchemaComponent xpathComponent() {
return this.xpathComponent;
}
// **************** Step string *******************************************
String getStepString() {
return this.stepString;
}
void updateStepString() {
if (this.isText) {
this.stepString = this.textStepString();
}
else {
this.stepString = this.componentStepString();
}
}
private String textStepString() {
return MWXmlField.TEXT;
}
private String componentStepString() {
String stepString = "";
if (this.isAttribute) {
stepString += "@";
}
stepString += this.namespacePrefix();
stepString += this.localName;
stepString += this.positionString();
return stepString;
}
private String namespacePrefix() {
if (this.namespace != null
&& ! "".equals(this.namespace.getNamespacePrefix())
&& !this.namespace.getSchema().getDefaultNamespaceUrl().equals(this.namespace.getNamespaceUrl())) {
return this.namespace.getNamespacePrefix() + ":";
}
else {
return "";
}
}
private String positionString() {
if (this.position != ANY_POSITION) {
return "[" + this.position + "]";
}
else {
return "";
}
}
// **************** Resolution/validation *********************************
boolean isResolved() {
return this.resolved;
}
boolean isValid() {
return this.valid;
}
MWSchemaContextComponent resolveContext(MWSchemaContextComponent parentContext) {
if (parentContext == null) {
this.unresolve();
return null;
}
else if (MWXmlField.TEXT.equals(this.stepString)) {
return this.resolveTextContext(parentContext);
}
else if (this.stepString.startsWith("@")) {
return this.resolveAttributeContext(parentContext);
}
else {
return this.resolveElementContext(parentContext);
}
}
private void unresolve() {
this.namespace = null;
this.xpathComponent = null;
this.localName = "";
this.isAttribute = false;
this.isText = false;
this.position = ANY_POSITION;
this.resolved = false;
this.valid = false;
}
private MWSchemaContextComponent resolveTextContext(MWSchemaContextComponent parentContext) {
this.namespace = null;
this.isText = true;
this.isAttribute = false;
this.localName = "";
this.position = ANY_POSITION;
this.resolved = true;
this.valid = parentContext.containsText();
return null;
}
private MWSchemaContextComponent resolveAttributeContext(MWSchemaContextComponent parentContext) {
this.isAttribute = true;
this.position = ANY_POSITION;
String qName = this.stepString.substring(1);
return this.resolveComponentContext(parentContext, qName);
}
private MWSchemaContextComponent resolveElementContext(MWSchemaContextComponent parentContext) {
this.isAttribute = false;
this.position = ANY_POSITION; // to be possibly changed below
int openBraceIndex = this.stepString.indexOf('[');
int closeBraceIndex = this.stepString.indexOf(']');
String qName = this.stepString;
if (openBraceIndex != -1 && closeBraceIndex > (openBraceIndex + 1)) {
try {
this.position = Integer.valueOf(this.stepString.substring(openBraceIndex + 1, closeBraceIndex)).intValue();
qName = this.stepString.substring(0, openBraceIndex);
}
catch (NumberFormatException nfe) {
// do nothing - if exception is thrown, qname is not set above
}
}
return this.resolveComponentContext(parentContext, qName);
}
private MWSchemaContextComponent resolveComponentContext(MWSchemaContextComponent parentContext, String qName) {
String namespacePrefix = "";
String namespaceUrl = "";
String localName = qName;
int colonIndex = qName.indexOf(':');
if (colonIndex != -1) {
namespacePrefix = qName.substring(0, colonIndex);
namespaceUrl = parentContext.getSchema().namespaceUrlForPrefix(namespacePrefix);
localName = localName.substring(colonIndex + 1);
}
this.localName = localName;
this.namespace = parentContext.getSchema().namespaceForUrl(namespaceUrl);
if (this.isAttribute) {
this.xpathComponent = parentContext.nestedAttribute(namespaceUrl, localName);
}
else {
this.xpathComponent = parentContext.nestedElement(namespaceUrl, localName);
}
this.resolved = (this.xpathComponent != null);
this.valid = this.xpathComponent != null && this.position <= this.xpathComponent.getMaxOccurs();
return this.xpathComponent;
}
// **************** Used for runtime conversion ***************************
static int compareSchemaOrder(MWSchemaContextComponent contextComponent, MWXpathStep step1, MWXpathStep step2) {
if (step1.isAttribute()) {
// attributes come equal to other attributes ...
if (step2.isAttribute()) {
return 0;
}
// ... and before text and elements
else {
return -1;
}
}
else if (step1.isText()) {
// text come after attributes ...
if (step2.isAttribute()) {
return +1;
}
// ... equal to other text ...
else if (step2.isText()) {
return 0;
}
// ... and before elements
else {
return -1;
}
}
else {
// elements come after attributes and text ...
if (! step2.isElement()) {
return +1;
}
else {
MWElementDeclaration element1 = (MWElementDeclaration) step1.xpathComponent();
MWElementDeclaration element2 = (MWElementDeclaration) step2.xpathComponent();
int comparison = contextComponent.compareSchemaOrder(element1, element2);
if (comparison == 0) {
return new Integer(step1.position).compareTo(new Integer(step2.position));
}
else {
return comparison;
}
}
}
}
}