org.xmlbeam.util.intern.duplex.DuplexExpression Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmlprojector Show documentation
Show all versions of xmlprojector Show documentation
The coolest XML library for Java around. Define typesafe views (projections) to xml. Use XPath to read and write XML. Bind XML to Java collections. Requires at least Java6, supports Java8 features and has no further runtime dependencies.
/**
* Copyright 2014 Sven Ewald
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xmlbeam.util.intern.duplex;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.xml.xpath.XPathVariableResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmlbeam.util.intern.DOMHelper;
import org.xmlbeam.util.intern.duplex.BuildDocumentVisitor.MODE;
import org.xmlbeam.util.intern.duplex.INodeEvaluationVisitor.VisitorClosure;
import org.xmlbeam.util.intern.duplex.SimpleNode.StepListFilter;
/**
* @author sven
*/
public class DuplexExpression {
private final static StepListFilter ALL_BUT_LAST = new StepListFilter() {
@Override
public List filter(final SimpleNode[] children) {
if (children.length < 2) {
return Collections.emptyList();
}
final List list = Arrays.asList(children).subList(0, children.length - 1);
assert list.size() == (children.length - 1);
return list;
}
};
private final static StepListFilter ONLY_LAST_STEP = new StepListFilter() {
@Override
public List filter(final SimpleNode[] children) {
if (children.length < 1) {
return Collections.emptyList();
}
assert children[children.length - 1] != null;
return Collections.singletonList(children[children.length - 1]);
}
};
private XPathVariableResolver variableResolver = null;
/**
* @param resolver
* @return this for convenience
*/
public DuplexExpression setXPathVariableResolver(final XPathVariableResolver resolver) {
this.variableResolver = resolver;
return this;
}
@Override
public String toString() {
return "DuplexExpression [xpath=" + xpath + "]";
}
private final SimpleNode node;
private final String xpath;
private final Map variableFormatPatterns = new HashMap();
private final String strippedXPath;
private final String expressionFormatPattern;
private final Map userDefinedMapping;
/**
* @param node
*/
DuplexExpression(final SimpleNode node, final CharSequence xpath, final Map userDefinedMapping) {
this.node = node;
this.xpath = xpath.toString();
this.userDefinedMapping = userDefinedMapping;
final Deque removeStartPositions = new LinkedList();
final Deque removeEndPositions = new LinkedList();
node.getFirstChildWithId(XParserTreeConstants.JJTXPATH).eachChild(new VisitorClosure() {
@Override
public void apply(final SimpleNode node, final Node data) {
if (node.getID() != XParserTreeConstants.JJTVARNAME) {
return;
}
SimpleNode qnameNode = node.getFirstChildWithId(XParserTreeConstants.JJTQNAME);
if (qnameNode == null) {
return;
}
SimpleNode formatNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
if (formatNode == null) {
variableFormatPatterns.put(qnameNode.getValue(), null);
return;
}
variableFormatPatterns.put(qnameNode.getValue(), stripFormatMarkers(formatNode.getValue()));
removeStartPositions.push(formatNode.getStartColumn());
removeEndPositions.push(formatNode.getEndColumn());
}
}, null);
SimpleNode formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
if (formatPatternNode != null) {
this.expressionFormatPattern = formatPatternNode.getValue();
removeStartPositions.push(formatPatternNode.getStartColumn());
removeEndPositions.push(formatPatternNode.getEndColumn());
} else {
formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
if (formatPatternNode != null) {
this.expressionFormatPattern = stripFormatMarkers(formatPatternNode.getValue());
removeStartPositions.push(formatPatternNode.getStartColumn());
removeEndPositions.push(formatPatternNode.getEndColumn());
} else {
this.expressionFormatPattern = null;
}
}
StringBuilder stringBuilder = new StringBuilder(xpath);
while (!removeEndPositions.isEmpty()) {
stringBuilder.delete(removeStartPositions.pop(), removeEndPositions.pop() + 1);
}
strippedXPath = stringBuilder.toString();
}
/**
* @return true if expression makes use of XPath variables
*/
public boolean isUsingVariables() {
return !variableFormatPatterns.keySet().isEmpty();
}
/**
* @param value
* @return
*/
private String stripFormatMarkers(String value) {
value = value.substring(2, value.length() - 2);
return value.startsWith("using ") ? value.substring("using ".length()) : value;
}
/**
* Calculates the return type of the expression.
*
* @return ExpressionType
*/
public ExpressionType getExpressionType() {
try { //TODO: cache expression type ?
final ExpressionType expressionType = node.firstChildAccept(new ExpressionTypeEvaluationVisitor(), null);
return expressionType;
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Please report this bug: Can not determine type of XPath:" + xpath, e);
}
}
/**
* Creates nodes until selecting such a path would return something.
*
* @param contextNode
* @return the node that this expression would select.
*/
@SuppressWarnings("unchecked")
public org.w3c.dom.Node ensureExistence(final org.w3c.dom.Node contextNode) {
final Document document = DOMHelper.getOwnerDocumentFor(contextNode);
final Map namespaceMapping = new HashMap(userDefinedMapping);
namespaceMapping.putAll(DOMHelper.getNamespaceMapping(document));
//node.dump("");
return ((List) node.firstChildAccept(new BuildDocumentVisitor(variableResolver, namespaceMapping), contextNode)).get(0);
}
/**
* @param contextNode
* @return the parent element
*/
@SuppressWarnings("unchecked")
// due to JCC-API
public Element ensureParentExistence(final Node contextNode) {
final Document document = DOMHelper.getOwnerDocumentFor(contextNode);
final Map namespaceMapping = new HashMap(userDefinedMapping);
namespaceMapping.putAll(DOMHelper.getNamespaceMapping(document));
//node.dump("");
return (Element) ((List) node.firstChildAccept(new BuildDocumentVisitor(variableResolver, namespaceMapping, ALL_BUT_LAST, MODE.CREATE_IF_NOT_EXISTS), contextNode)).get(0);
}
/**
* @param parentNode
*/
public void deleteAllMatchingChildren(final Node parentNode) {
final Document document = DOMHelper.getOwnerDocumentFor(parentNode);
final Map namespaceMapping = DOMHelper.getNamespaceMapping(document);
BuildDocumentVisitor visitor = new BuildDocumentVisitor(variableResolver, namespaceMapping, ONLY_LAST_STEP, MODE.DELETE);
List> result;
int lastLength = -1;
do {
result = (List>) node.firstChildAccept(visitor, parentNode);
if (result.size() == lastLength) {
throw new IllegalStateException("Infinite loop detected. Please report issue with example.");
}
lastLength = result.size();
} while ((!result.isEmpty()) && (null != result.get(0)));
}
/**
* @param parentNode
* @return fresh new node
*/
@SuppressWarnings("unchecked")
public Node createChildWithPredicate(final Node parentNode) {
final Document document = DOMHelper.getOwnerDocumentFor(parentNode);
//final Map namespaceMapping = DOMHelper.getNamespaceMapping(document);
final Map namespaceMapping = new HashMap(userDefinedMapping);
namespaceMapping.putAll(DOMHelper.getNamespaceMapping(document));
BuildDocumentVisitor visitor = new BuildDocumentVisitor(variableResolver, namespaceMapping, ONLY_LAST_STEP, MODE.JUST_CREATE);
List nodes = (List) node.firstChildAccept(visitor, parentNode);
assert nodes.size() == 1;
return nodes.get(0);
}
/**
* Dumps the tree behind the expression for debugging purpose.
*/
public void dump() {
this.node.dump("");
}
/**
* @return String representation of expressions pattern
*/
public String getExpressionFormatPattern() {
return expressionFormatPattern;
// SimpleNode formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
// if (formatPatternNode != null) {
// return formatPatternNode.getValue();
// }
// formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
// if (formatPatternNode == null) {
// return null;
// }
// String value = formatPatternNode.getValue();
// value = value.substring(2, value.length() - 2);
// return value.startsWith("using ") ? value.substring("using ".length()) : value;
// //return (String) node.secondChildAccept(new GetExpressionFormatPatternVisitor(),null);
// //node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
}
/**
* @return xpath compileable with regular xpath engine.
*/
public String getExpressionAsStringWithoutFormatPatterns() {
return strippedXPath;
// SimpleNode formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTEXPRESSIONFORMAT);
// if (formatPatternNode != null) {
// return removeStringPart(xpath, formatPatternNode.getStartColumn(), formatPatternNode.getEndColumn() + 1);
// };
//
// formatPatternNode = node.getFirstChildWithId(XParserTreeConstants.JJTVARIABLEFORMAT);
// if (formatPatternNode != null) {
// return removeStringPart(xpath, formatPatternNode.getStartColumn(), formatPatternNode.getEndColumn() + 1);
// };
//
// return this.xpath;
}
// /**
// * @param xpath2
// * @param begin
// * @param end
// * @return
// */
// private final static String removeStringPart(final String string, final int begin, final int end) {
// return string.substring(0, begin) + (end > string.length() ? "" : string.substring(end, string.length()));
// }
/**
* @param name
* @return pattern for variable or null if no format is defined.
*/
public String getVariableFormatPattern(final String name) {
return variableFormatPatterns.get(name);
}
// Method may be obsolete
// /**
// * @return true if getExpressionFormat is not null.
// */
// public boolean hasExpressionFormatPattern() {
// return expressionFormatPattern != null;
// }
}