/*
* Copyright (c) 2012 Fraunhofer IGD
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Fraunhofer IGD
*/
package eu.esdihumboldt.hale.io.xslt.xpath;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import org.opengis.filter.And;
import org.opengis.filter.ExcludeFilter;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.Id;
import org.opengis.filter.IncludeFilter;
import org.opengis.filter.Not;
import org.opengis.filter.Or;
import org.opengis.filter.PropertyIsBetween;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.PropertyIsGreaterThan;
import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
import org.opengis.filter.PropertyIsLessThan;
import org.opengis.filter.PropertyIsLessThanOrEqualTo;
import org.opengis.filter.PropertyIsLike;
import org.opengis.filter.PropertyIsNil;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.opengis.filter.PropertyIsNull;
import org.opengis.filter.expression.Add;
import org.opengis.filter.expression.Divide;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.Multiply;
import org.opengis.filter.expression.NilExpression;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.expression.Subtract;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.Beyond;
import org.opengis.filter.spatial.Contains;
import org.opengis.filter.spatial.Crosses;
import org.opengis.filter.spatial.DWithin;
import org.opengis.filter.spatial.Disjoint;
import org.opengis.filter.spatial.Equals;
import org.opengis.filter.spatial.Intersects;
import org.opengis.filter.spatial.Overlaps;
import org.opengis.filter.spatial.Touches;
import org.opengis.filter.spatial.Within;
import org.opengis.filter.temporal.After;
import org.opengis.filter.temporal.AnyInteracts;
import org.opengis.filter.temporal.Before;
import org.opengis.filter.temporal.Begins;
import org.opengis.filter.temporal.BegunBy;
import org.opengis.filter.temporal.During;
import org.opengis.filter.temporal.EndedBy;
import org.opengis.filter.temporal.Ends;
import org.opengis.filter.temporal.Meets;
import org.opengis.filter.temporal.MetBy;
import org.opengis.filter.temporal.OverlappedBy;
import org.opengis.filter.temporal.TContains;
import org.opengis.filter.temporal.TEquals;
import org.opengis.filter.temporal.TOverlaps;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.instance.helper.PropertyResolver;
import eu.esdihumboldt.hale.common.instance.model.DataSet;
import eu.esdihumboldt.hale.common.schema.model.ChildDefinition;
import eu.esdihumboldt.hale.common.schema.model.Definition;
import eu.esdihumboldt.hale.common.schema.model.DefinitionUtil;
import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
import eu.esdihumboldt.hale.io.xsd.constraint.XmlAttributeFlag;
/**
* Filter to XPath visitor.
*
* @author Kai Schwierczek
*/
public class GeotoolsFilterToXPath implements ExpressionVisitor, FilterVisitor {
private static ALogger log = ALoggerFactory.getLogger(GeotoolsFilterToXPath.class);
/**
* The main type definition.
*/
private final TypeDefinition typeDef;
/**
* The parent type definition for property filters.
*/
private final TypeDefinition parentType;
private final NamespaceContext namespaceContext;
private final boolean propertyFilter;
/**
* Converts type filters to XPath.
*
* @param definition the base type
* @param namespaceContext the namespace context
*/
private GeotoolsFilterToXPath(TypeDefinition definition, NamespaceContext namespaceContext) {
this.typeDef = definition;
this.namespaceContext = namespaceContext;
propertyFilter = false;
parentType = null;
}
/**
* Converts property filters to XPath.
*
* @param definition the base type
* @param namespaceContext the namespace context
*/
private GeotoolsFilterToXPath(PropertyDefinition definition, NamespaceContext namespaceContext) {
this.typeDef = definition.getPropertyType();
this.namespaceContext = namespaceContext;
propertyFilter = true;
parentType = definition.getParentType();
}
/**
* Transforms the given filter of the given type to a XPath query.
* Namespaces are transformed with the given mapping.
*
* @param definition the type
* @param namespaceContext the namespace context
* @param filter the filter to transform
* @return the XPath query representing the given filter
*/
public static String toXPath(TypeDefinition definition, NamespaceContext namespaceContext,
Filter filter) {
return ((StringBuffer) filter.accept(
new GeotoolsFilterToXPath(definition, namespaceContext), null)).toString();
}
/**
* Transforms the given filter of the given type to a XPath query.
* Namespaces are transformed with the given mapping.
*
* @param definition the property definition
* @param namespaceContext the namespace context
* @param filter the property filter to transform
* @return the XPath query representing the given filter
*/
public static String toXPath(PropertyDefinition definition, NamespaceContext namespaceContext,
Filter filter) {
return ((StringBuffer) filter.accept(
new GeotoolsFilterToXPath(definition, namespaceContext), null)).toString();
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.ExcludeFilter,
* java.lang.Object)
*/
@Override
public Object visit(ExcludeFilter filter, Object buffer) {
// System.out.println("ExcludeFilter");
StringBuffer output = asStringBuffer(buffer);
output.append("false()");
return output;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.IncludeFilter,
* java.lang.Object)
*/
@Override
public Object visit(IncludeFilter arg0, Object buffer) {
// System.out.println("IncludeFilter");
StringBuffer output = asStringBuffer(buffer);
output.append("true()");
return output;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.And,
* java.lang.Object)
*/
@Override
public Object visit(And filter, Object buffer) {
// System.out.println("And");
StringBuffer output = asStringBuffer(buffer);
output.append("(");
for (Iterator<Filter> i = filter.getChildren().iterator(); i.hasNext();) {
Filter child = i.next();
child.accept(this, output);
if (i.hasNext())
output.append(" and ");
}
output.append(")");
return output;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.Id,
* java.lang.Object)
*/
@Override
public Object visit(Id arg0, Object buffer) {
throw new UnsupportedOperationException("Id filter not implemented");
// TODO is there a way to implement this in XPath?
// It's about FeatureIds/GeometricIds/...
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.Not,
* java.lang.Object)
*/
@Override
public Object visit(Not filter, Object buffer) {
// System.out.println("Not");
StringBuffer output = asStringBuffer(buffer);
output.append("not(");
filter.getFilter().accept(this, output);
output.append(')');
return output;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.Or,
* java.lang.Object)
*/
@Override
public Object visit(Or filter, Object buffer) {
// System.out.println("Or");
StringBuffer output = asStringBuffer(buffer);
output.append("(");
for (Iterator<Filter> i = filter.getChildren().iterator(); i.hasNext();) {
Filter child = i.next();
child.accept(this, output);
if (i.hasNext())
output.append(" or ");
}
output.append(")");
return output;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsBetween,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsBetween filter, Object buffer) {
// System.out.println("PropertyIsBetween");
StringBuffer output = asStringBuffer(buffer);
output.append("(").append(filter.getExpression().accept(this, output)).append(" >= ")
.append(filter.getLowerBoundary().accept(this, output)).append(" and ")
.append(filter.getExpression().accept(this, output)).append(" <= ")
.append(filter.getUpperBoundary().accept(this, output))
.append(filter.accept(this, output)).append(')');
return output;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsEqualTo,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsEqualTo filter, Object buffer) {
// System.out.println("PropertyIsEqualTo");
// XXX isMatchingCase is currently ignored.
// Would somehow need to determine whether a number or string is
// compared? (And then use lower-case(string) on both sides.)
StringBuffer result = asStringBuffer(buffer);
filter.getExpression1().accept(this, result);
result.append(" = ");
filter.getExpression2().accept(this, result);
return result;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsNotEqualTo,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsNotEqualTo filter, Object buffer) {
// System.out.println("PropertyIsNotEqualTo");
// XXX isMatchingCase is currently ignored. See IsEqual.
StringBuffer result = asStringBuffer(buffer);
filter.getExpression1().accept(this, result);
result.append(" != ");
filter.getExpression2().accept(this, result);
return result;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsGreaterThan,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsGreaterThan filter, Object buffer) {
// System.out.println("PropertyIsGreaterThan");
StringBuffer result = asStringBuffer(buffer);
filter.getExpression1().accept(this, result);
result.append(" > ");
filter.getExpression2().accept(this, result);
return result;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsGreaterThanOrEqualTo,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsGreaterThanOrEqualTo filter, Object buffer) {
// System.out.println("PropertyIsGreaterThanOrEqualTo");
StringBuffer result = asStringBuffer(buffer);
filter.getExpression1().accept(this, result);
result.append(" >= ");
filter.getExpression2().accept(this, result);
return result;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsLessThan,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsLessThan filter, Object buffer) {
// System.out.println("PropertyIsLessThan");
StringBuffer result = asStringBuffer(buffer);
filter.getExpression1().accept(this, result);
result.append(" < ");
filter.getExpression2().accept(this, result);
return result;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsLessThanOrEqualTo,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsLessThanOrEqualTo filter, Object buffer) {
// System.out.println("PropertyIsLessThanOrEqualTo");
StringBuffer result = asStringBuffer(buffer);
filter.getExpression1().accept(this, result);
result.append(" <= ");
filter.getExpression2().accept(this, result);
return result;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsLike,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsLike filter, Object buffer) {
StringBuffer result = asStringBuffer(buffer);
String escape = filter.getEscape();
String single = filter.getSingleChar();
String multi = filter.getWildCard();
String escapedEscape = escapeXPathRegexString(escape);
String escapedSingle = escapeXPathRegexString(single);
String escapedMulti = escapeXPathRegexString(multi);
String pattern = filter.getLiteral();
boolean caseSensitive = filter.isMatchingCase();
result.append("matches(");
filter.getExpression().accept(this, result);
result.append(", '^");
int pos = 0;
boolean escaped = false;
while (pos < pattern.length()) {
if (pattern.regionMatches(pos, escape, 0, escape.length())) {
// escape char
if (escaped) {
result.append(escapedEscape);
escaped = false;
}
else
escaped = true;
pos += escape.length();
}
else if (pattern.regionMatches(pos, single, 0, single.length())) {
// single wild card
if (escaped) {
result.append(escapedSingle);
escaped = false;
}
else
result.append('.');
pos += single.length();
}
else if (pattern.regionMatches(pos, multi, 0, multi.length())) {
// multi wild card
if (escaped) {
result.append(escapedMulti);
escaped = false;
}
else
result.append(".*");
pos += multi.length();
}
else {
// nothing special
if (escaped)
throw new IllegalArgumentException(
"After the escape symbol of a LIKE pattern does not follow a special character.");
char c = pattern.charAt(pos);
if (isXPathRegexEscapeChar(c))
result.append('\\');
result.append(c);
pos++;
}
}
result.append("$', 's"); // s "dot-all" mode (. matches \n, too)
if (!caseSensitive)
result.append('i');
result.append("')");
return result;
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.PropertyIsNull,
* java.lang.Object)
*/
@Override
public Object visit(PropertyIsNull filter, Object buffer) {
StringBuffer result = asStringBuffer(buffer);
// allow it to not exist at all, or to be nilled
result.append("(not(");
filter.accept(this, result);
result.append(") or nilled(");
filter.accept(this, result);
result.append("))");
return result;
}
@Override
public Object visit(PropertyIsNil filter, Object extraData) {
return new UnsupportedOperationException("Nil filter not implemented");
}
// TODO Can these geometric filters easily be converted to XPath?
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.BBOX,
* java.lang.Object)
*/
@Override
public Object visit(BBOX arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter BBOX not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Beyond,
* java.lang.Object)
*/
@Override
public Object visit(Beyond arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Beyond not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Contains,
* java.lang.Object)
*/
@Override
public Object visit(Contains arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Contains not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Crosses,
* java.lang.Object)
*/
@Override
public Object visit(Crosses arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Crosses not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Disjoint,
* java.lang.Object)
*/
@Override
public Object visit(Disjoint arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Disjoint not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.DWithin,
* java.lang.Object)
*/
@Override
public Object visit(DWithin arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter DWithin not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Equals,
* java.lang.Object)
*/
@Override
public Object visit(Equals filter, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Equals not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Intersects,
* java.lang.Object)
*/
@Override
public Object visit(Intersects arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Intersects not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Overlaps,
* java.lang.Object)
*/
@Override
public Object visit(Overlaps arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Overlaps not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Touches,
* java.lang.Object)
*/
@Override
public Object visit(Touches arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Touches not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.spatial.Within,
* java.lang.Object)
*/
@Override
public Object visit(Within arg0, Object buffer) {
throw new UnsupportedOperationException("Geometric filter Within not implemented");
}
// TODO Can these time filters easily be converted to XPath?
// FilterToCQL does not support them.
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.After,
* java.lang.Object)
*/
@Override
public Object visit(After arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter After not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.AnyInteracts,
* java.lang.Object)
*/
@Override
public Object visit(AnyInteracts arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter AnyInteracts not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.Before,
* java.lang.Object)
*/
@Override
public Object visit(Before arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter Before not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.Begins,
* java.lang.Object)
*/
@Override
public Object visit(Begins arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter Begins not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.BegunBy,
* java.lang.Object)
*/
@Override
public Object visit(BegunBy arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter BegunBy not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.During,
* java.lang.Object)
*/
@Override
public Object visit(During arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter During not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.EndedBy,
* java.lang.Object)
*/
@Override
public Object visit(EndedBy arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter EndedBy not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.Ends,
* java.lang.Object)
*/
@Override
public Object visit(Ends arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter Ends not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.Meets,
* java.lang.Object)
*/
@Override
public Object visit(Meets arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter Meets not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.MetBy,
* java.lang.Object)
*/
@Override
public Object visit(MetBy arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter MetBy not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.OverlappedBy,
* java.lang.Object)
*/
@Override
public Object visit(OverlappedBy arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter OverlappedBy not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.TContains,
* java.lang.Object)
*/
@Override
public Object visit(TContains arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter TContains not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.TEquals,
* java.lang.Object)
*/
@Override
public Object visit(TEquals arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter TEquals not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visit(org.opengis.filter.temporal.TOverlaps,
* java.lang.Object)
*/
@Override
public Object visit(TOverlaps arg0, Object buffer) {
throw new UnsupportedOperationException("Temporal filter TOverlaps not implemented");
}
/**
* @see org.opengis.filter.FilterVisitor#visitNullFilter(java.lang.Object)
*/
@Override
public Object visitNullFilter(Object buffer) {
throw new UnsupportedOperationException("nullFilter not implemented");
// TODO What is this about? FilterToCQL says
// "Cannot encode null as a Filter"
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.NilExpression,
* java.lang.Object)
*/
@Override
public Object visit(NilExpression arg0, Object buffer) {
throw new UnsupportedOperationException("nil not implemented");
// TODO How to represent nil?
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.Function,
* java.lang.Object)
*/
@Override
public Object visit(Function arg0, Object buffer) {
log.error("Function filter present. Name: " + arg0.getName());
throw new UnsupportedOperationException("Function filter not implemented");
// TODO How to export functions? Is there a list of available functions?
// If so, create a mapping from them to XPath!?
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.Literal,
* java.lang.Object)
*/
@Override
public Object visit(Literal expression, Object buffer) {
StringBuffer result = asStringBuffer(buffer);
Object literal = expression.getValue();
// if (literal instanceof Geometry) {
// Geometry geometry = (Geometry) literal;
// WKTWriter writer = new WKTWriter();
// String wkt = writer.write( geometry );
// output.append( wkt );
// }
// else if( literal instanceof Number ){
// // don't convert to string
// output.append( literal );
// }
// else if (literal instanceof Date ) {
// return date( (Date) literal, output );
// }
// else {
// TODO Needs suitable escaping!
// a split+concat at ' and at the end replacing " with " ?
if (literal instanceof Number)
result.append(literal);
else
result.append('\'').append(literal).append('\'');
return result;
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.Add,
* java.lang.Object)
*/
@Override
public Object visit(Add expression, Object buffer) {
StringBuffer output = asStringBuffer(buffer);
expression.getExpression1().accept(this, output);
output.append(" + ");
expression.getExpression2().accept(this, output);
return output;
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.Divide,
* java.lang.Object)
*/
@Override
public Object visit(Divide expression, Object buffer) {
StringBuffer output = asStringBuffer(buffer);
expression.getExpression1().accept(this, output);
output.append(" div ");
expression.getExpression2().accept(this, output);
return output;
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.Multiply,
* java.lang.Object)
*/
@Override
public Object visit(Multiply expression, Object buffer) {
StringBuffer output = asStringBuffer(buffer);
expression.getExpression1().accept(this, output);
output.append(" * ");
expression.getExpression2().accept(this, output);
return output;
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.Subtract,
* java.lang.Object)
*/
@Override
public Object visit(Subtract expression, Object buffer) {
StringBuffer output = asStringBuffer(buffer);
expression.getExpression1().accept(this, output);
output.append(" - ");
expression.getExpression2().accept(this, output);
return output;
}
/**
* @see org.opengis.filter.expression.ExpressionVisitor#visit(org.opengis.filter.expression.PropertyName,
* java.lang.Object)
*/
@Override
public Object visit(PropertyName expression, Object buffer) {
StringBuffer result = asStringBuffer(buffer);
TypeDefinition rootType = typeDef;
String path = expression.getPropertyName();
boolean propertyModeValue = true;
if (propertyFilter) {
/*
* If we are processing property filters, expressions either start
* with value or test.
*/
// value directly referenced
if ("value".equals(path)) {
// means we reference the current context
result.append('.'); // XXX is this correct?
return result;
}
else if (path.startsWith("parent/")) {
rootType = parentType;
propertyModeValue = false;
// remove prefix from path
path = path.substring(7);
}
else if (path.startsWith("value/")) {
// remove prefix from path
path = path.substring(6);
}
}
List<List<QName>> paths = PropertyResolver.getQueryPaths(rootType, DataSet.SOURCE, path);
// TODO what about no / multiple paths
List<QName> qnames = paths.get(0);
if (propertyFilter && !propertyModeValue) {
// parent mode, we have to prepend the reference to the parent
result.append("../");
}
Definition<?> parent = rootType;
boolean first = true;
for (int i = 0; i < qnames.size(); i++) {
// get the element qualified name
QName segment = qnames.get(i);
// get the associated definition
ChildDefinition<?> def = DefinitionUtil.getChild(parent, segment);
if (def.asProperty() != null) {
// groups are ignored
if (first) {
first = false;
}
else {
result.append('/');
}
if (def.asProperty().getConstraint(XmlAttributeFlag.class).isEnabled()) {
// attributes need to be marked w/ @
result.append('@');
}
// add the qualified name
result.append(qNameToXPathSegment(segment));
}
parent = def;
}
return result;
}
/**
* Process the possibly user supplied buffer parameter into a StringBuffer.
*
* @param buffer the current buffer argument
* @return a StringBuffer for appending the result
*/
protected StringBuffer asStringBuffer(Object buffer) {
if (buffer instanceof StringBuffer)
return (StringBuffer) buffer;
else
return new StringBuffer();
}
/**
* Returns the given QName in a format for XPath.
*
* @param segment the QName to format
* @return the XPath segment
*/
protected String qNameToXPathSegment(QName segment) {
// XXX What if the namespace isn't specified in the map?
// Check for NULL_NS.
if (segment.getNamespaceURI().isEmpty())
return segment.getLocalPart();
else
return namespaceContext.getPrefix(segment.getNamespaceURI()) + ':'
+ segment.getLocalPart();
}
/**
* Returns true, if the given char is a char which may get escaped in XPath
* regular expressions.
*
* @param c the char to test
* @return true, is the given char may be escaped in XPath
*/
private boolean isXPathRegexEscapeChar(char c) {
return (c == '\\') || (c == '|') || (c == '.') || (c == '?') || (c == '*') || (c == '+')
|| (c == '(') || (c == ')') || (c == '{') || (c == '}') || (c == '$') || (c == '-')
|| (c == '[') || (c == ']') || (c == '^');
}
/**
* Escapes special XPath regex chars.
*
* @param s the String to be escaped
* @return the escaped string
*/
private String escapeXPathRegexString(String s) {
StringBuffer result = new StringBuffer(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (isXPathRegexEscapeChar(c))
result.append('\\' + c);
else
result.append(c);
}
return result.toString();
}
}