/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2015, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.data.complex.filter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.geotools.data.complex.AbstractMappingFeatureIterator;
import org.geotools.data.complex.ComplexFeatureConstants;
import org.geotools.data.complex.config.NonFeatureTypeProxy;
import org.geotools.data.complex.config.Types;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.AppSchemaAttributeBuilder;
import org.geotools.feature.AttributeImpl;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.ComplexAttributeImpl;
import org.geotools.feature.GeometryAttributeImpl;
import org.geotools.feature.ValidatingFeatureFactoryImpl;
import org.geotools.feature.type.AttributeDescriptorImpl;
import org.geotools.feature.type.ComplexFeatureTypeFactoryImpl;
import org.geotools.feature.type.GeometryTypeImpl;
import org.geotools.feature.type.UniqueNameFeatureTypeFactoryImpl;
import org.geotools.gml3.GML;
import org.geotools.xs.XSSchema;
import org.opengis.feature.Attribute;
import org.opengis.feature.ComplexAttribute;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureFactory;
import org.opengis.feature.Property;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.ComplexType;
import org.opengis.feature.type.FeatureTypeFactory;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.feature.type.PropertyType;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.PropertyName;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.xml.sax.Attributes;
import com.vividsolutions.jts.geom.Geometry;
/**
* Utility class to evaluate XPath expressions against an Attribute instance, which may be any
* Attribute, whether it is simple, complex, a feature, etc.
* <p>
* At the difference of the Filter subsystem, which works against Attribute contents (for example to
* evaluate a comparison filter), the XPath subsystem, for which this class is the single entry
* point, works against Attribute instances. That is, the result of an XPath expression, if a single
* value, is an Attribtue, not the attribute content, or a List of Attributes, for instance.
* </p>
*
* @author Gabriel Roldan (Axios Engineering)
* @author Rini Angreani (CSIRO Earth Science and Resource Engineering)
* @version $Id$
*
*
*
* @source $URL$
* http://svn.osgeo.org/geotools/trunk/modules/unsupported/app-schema/app-schema/src/main
* /java/org/geotools/data/complex/filter/XPath.java $
* @since 2.4
*/
public class XPath extends XPathUtil {
private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(XPath.class
.getPackage().getName());
private FilterFactory FF;
private FeatureFactory featureFactory;
private CoordinateReferenceSystem crs;
/**
* Used to create specific attribute descriptors for
* {@link #set(Attribute, String, Object, String, AttributeType)} when the actual attribute
* instance is of a derived type of the corresponding one declared in the feature type.
*/
private FeatureTypeFactory descriptorFactory;
public XPath() {
this.FF = CommonFactoryFinder.getFilterFactory(null);
this.featureFactory = new ValidatingFeatureFactoryImpl();
this.descriptorFactory = new UniqueNameFeatureTypeFactoryImpl();
}
public XPath(FilterFactory ff, FeatureFactory featureFactory) {
setFilterFactory(ff);
setFeatureFactory(featureFactory);
// this.descriptorFactory = new TypeFactoryImpl();
}
public void setFilterFactory(FilterFactory ff) {
this.FF = ff;
}
public void setCRS(CoordinateReferenceSystem crs) {
this.crs = crs;
}
public void setFeatureFactory(FeatureFactory featureFactory) {
this.featureFactory = featureFactory;
}
/**
* Sets the value of the attribute of <code>att</code> addressed by <code>xpath</code> and of
* type <code>targetNodeType</code> to be <code>value</code> with id <code>id</code>.
*
* @param att
* the root attribute for which to set the child attribute value
* @param xpath
* the xpath expression that addresses the <code>att</code> child whose value is to
* be set
* @param value
* the value of the attribute addressed by <code>xpath</code>
* @param id
* the identifier of the attribute addressed by <code>xpath</code>, might be
* <code>null</code>
* @param targetNodeType
* the expected type of the attribute addressed by <code>xpath</code>, or
* <code>null</code> if unknown
* @param isXlinkRef
* true if the attribute would only contain xlink:href client property
* @return
*/
public Attribute set(final Attribute att, final StepList xpath, Object value, String id,
AttributeType targetNodeType, boolean isXlinkRef, Expression sourceExpression) {
return set(att, xpath, value, id, targetNodeType, isXlinkRef, null, sourceExpression);
}
public Attribute set(final Attribute att, final StepList xpath, Object value, String id,
AttributeType targetNodeType, boolean isXlinkRef, AttributeDescriptor targetDescriptor,
Expression sourceExpression) {
if (XPath.LOGGER.isLoggable(Level.CONFIG)) {
XPath.LOGGER.entering("XPath", "set", new Object[] { att, xpath, value, id,
targetNodeType });
}
final StepList steps = new StepList(xpath);
Attribute parent = att;
Name rootName = null;
AttributeDescriptor parentDescriptor = parent.getDescriptor();
if (parentDescriptor != null) {
rootName = parentDescriptor.getName();
Step rootStep = (Step) steps.get(0);
QName stepName = rootStep.getName();
if (Types.equals(rootName, stepName)) {
// first step is the self reference to att, so skip it
if (steps.size() > 1) {
steps.remove(0);
} else {
// except when the xpath is the root itself
// where it is done for feature chaining for simple content
if (Types.isSimpleContentType(parent.getType())
|| Types.canHaveTextContent(parent.getType())) {
return setSimpleContentValue(parent, value);
} else if (Types.isGeometryType(parent.getType())) {
ComplexFeatureTypeFactoryImpl typeFactory = new ComplexFeatureTypeFactoryImpl();
GeometryType geomType;
if (parent.getType() instanceof GeometryType) {
geomType = (GeometryType) parent.getType();
} else {
geomType = (GeometryType) ((NonFeatureTypeProxy) parent.getType())
.getSubject();
}
GeometryDescriptor geomDescriptor = typeFactory.createGeometryDescriptor(
geomType, rootName, parentDescriptor.getMinOccurs(),
parentDescriptor.getMaxOccurs(), parentDescriptor.isNillable(),
parentDescriptor.getDefaultValue());
GeometryAttributeImpl geom = new GeometryAttributeImpl(value,
geomDescriptor, null);
ArrayList<Property> geomAtts = new ArrayList<Property>();
geomAtts.add(geom);
parent.setValue(geomAtts);
return geom;
}
}
}
}
Iterator stepsIterator = steps.iterator();
for (; stepsIterator.hasNext();) {
final XPath.Step currStep = (Step) stepsIterator.next();
AttributeDescriptor currStepDescriptor = null;
final boolean isLastStep = !stepsIterator.hasNext();
final QName stepName = currStep.getName();
final Name attributeName = Types.toName(stepName);
final AttributeType _parentType = parent.getType();
if (_parentType.getName().equals(XSSchema.ANYTYPE_TYPE.getName()) && targetDescriptor != null) {
// this needs to be passed on if casting anyType to something else, since it won't
// exist in the schema
currStepDescriptor = targetDescriptor;
} else {
ComplexType parentType = (ComplexType) _parentType;
if (!isLastStep || targetNodeType == null) {
if (null == attributeName.getNamespaceURI()) {
currStepDescriptor = (AttributeDescriptor) Types.findDescriptor(parentType, attributeName.getLocalPart());
} else {
currStepDescriptor = (AttributeDescriptor) Types.findDescriptor(parentType, attributeName);
}
if (currStepDescriptor == null) {
// need to take the non easy way, may be the instance has a
// value for this step with a different name, of a derived
// type of the one declared in the parent type
String prefixedStepName = currStep.toString();
PropertyName name = FF.property(prefixedStepName);
Attribute child = (Attribute) name.evaluate(parent);
if (child != null) {
currStepDescriptor = child.getDescriptor();
}
}
} else {
AttributeDescriptor actualDescriptor;
if (null == attributeName.getNamespaceURI()) {
actualDescriptor = (AttributeDescriptor) Types.findDescriptor(parentType, attributeName.getLocalPart());
} else {
actualDescriptor = (AttributeDescriptor) Types.findDescriptor(parentType, attributeName);
}
if (actualDescriptor != null) {
int minOccurs = actualDescriptor.getMinOccurs();
int maxOccurs = actualDescriptor.getMaxOccurs();
boolean nillable = actualDescriptor.isNillable();
if (actualDescriptor instanceof GeometryDescriptor) {
// important to maintain CRS information encoding
if (Geometry.class.isAssignableFrom(targetNodeType.getBinding())) {
if (!(targetNodeType instanceof GeometryType)) {
targetNodeType = new GeometryTypeImpl(targetNodeType.getName(),
targetNodeType.getBinding(), crs != null ? crs
: ((GeometryDescriptor) actualDescriptor)
.getCoordinateReferenceSystem(),
targetNodeType.isIdentified(), targetNodeType
.isAbstract(),
targetNodeType.getRestrictions(), targetNodeType
.getSuper(), targetNodeType.getDescription());
}
currStepDescriptor = descriptorFactory.createGeometryDescriptor(
(GeometryType) targetNodeType, attributeName, minOccurs,
maxOccurs, nillable, null);
} else {
throw new IllegalArgumentException("Can't set targetNodeType: "
+ targetNodeType.toString() + " for attribute mapping: "
+ attributeName + " as it is not a Geometry type!");
}
} else {
currStepDescriptor = descriptorFactory.createAttributeDescriptor(
targetNodeType, attributeName, minOccurs, maxOccurs, nillable,
null);
}
}
}
if (currStepDescriptor == null) {
StringBuffer parentAtts = new StringBuffer();
Collection properties = parentType.getDescriptors();
for (Iterator it = properties.iterator(); it.hasNext();) {
PropertyDescriptor desc = (PropertyDescriptor) it.next();
Name name = desc.getName();
parentAtts.append(name.getNamespaceURI());
parentAtts.append("#");
parentAtts.append(name.getLocalPart());
if (it.hasNext()) {
parentAtts.append(", ");
}
}
throw new IllegalArgumentException(currStep
+ " is not a valid location path for type " + parentType.getName()
+ ". " + currStep + " ns: " + currStep.getName().getNamespaceURI()
+ ", " + parentType.getName().getLocalPart() + " properties: "
+ parentAtts);
}
}
if (isLastStep) {
// reached the leaf
if (currStepDescriptor == null) {
throw new IllegalArgumentException(currStep
+ " is not a valid location path for type " + _parentType.getName());
}
return setLeafAttribute(currStepDescriptor, currStep, id, value, parent,
targetNodeType, isXlinkRef);
} else {
// parent = appendComplexProperty(parent, currStep,
// currStepDescriptor);
int index = currStep.isIndexed() ? currStep.getIndex() : -1;
parent = setValue(currStepDescriptor, null, new ArrayList<Property>(), index,
parent, null, isXlinkRef);
}
}
throw new IllegalStateException();
}
/**
* Set a simple content value for an attribute.
*
* @param attribute
* Attribute of simple content type.
* @param value
* Value for the simple content.
* @return The attribute with simple content type.
*/
private Attribute setSimpleContentValue(Attribute attribute, Object value) {
Property simpleContent = null;
if (attribute instanceof ComplexAttribute) {
simpleContent = ((ComplexAttribute)attribute).getProperty(ComplexFeatureConstants.SIMPLE_CONTENT);
}
if (simpleContent == null) {
Collection<Property> contents = new ArrayList<Property>();
simpleContent = buildSimpleContent(attribute.getType(), value);
contents.add(simpleContent);
ArrayList<Attribute> nestedAttContents = new ArrayList<Attribute>();
Attribute nestedAtt = new ComplexAttributeImpl(contents, attribute.getDescriptor(),
attribute.getIdentifier());
nestedAttContents.add(nestedAtt);
attribute.setValue(nestedAttContents);
return nestedAtt;
} else {
PropertyType simpleContentType = getSimpleContentType((AttributeType) simpleContent.getType());
Object convertedValue = FF.literal(value).evaluate(value,
simpleContentType.getBinding());
simpleContent.setValue(convertedValue);
return attribute;
}
}
private Attribute setLeafAttribute(AttributeDescriptor currStepDescriptor,
Step currStep, String id, Object value, Attribute parent,
AttributeType targetNodeType, boolean isXlinkRef) {
int index = currStep.isIndexed() ? currStep.getIndex() : -1;
Attribute attribute = setValue(currStepDescriptor, id, value, index, parent,
targetNodeType, isXlinkRef);
return attribute;
}
@SuppressWarnings("unchecked")
private Attribute setValue(final AttributeDescriptor descriptor, final String id,
final Object value, final int index, final Attribute parent,
final AttributeType targetNodeType, boolean isXlinkRef) {
Object convertedValue = null;
Map <Object, Object> simpleContentProperties = null;
if (isFeatureChainedSimpleContent(descriptor, value)) {
List<Property> nestedPropList = getSimpleContentList(value);
if (!nestedPropList.isEmpty()) {
Property nestedProp = nestedPropList.iterator().next();
if (Types.isGeometryType(descriptor.getType())
|| nestedProp.getName().equals(descriptor.getName())) {
convertedValue = nestedProp.getValue();
} else {
convertedValue = nestedPropList;
}
simpleContentProperties = nestedProp.getUserData();
}
} else {
// adapt value to context
convertedValue = convertValue(descriptor, value);
}
Attribute leafAttribute = null;
final Name attributeName = descriptor.getName();
if (!isXlinkRef) {
// skip this process if the attribute would only contain xlink:ref
// that is chained, because it won't contain any values, and we
// want to create a new empty leaf attribute
if (parent instanceof ComplexAttribute) {
Object currStepValue = ((ComplexAttribute) parent).getProperties(attributeName);
if (currStepValue instanceof Collection) {
List<Attribute> values = new ArrayList((Collection) currStepValue);
if (!values.isEmpty()) {
if (isEmpty(convertedValue)) {
// when attribute is empty, it is probably just a parent of a leaf
// attribute
// it could already exist from another attribute mapping for a different
// leaf
// e.g. 2 different attribute mappings:
// sa:relatedObservation/om:Observation/om:parameter[2]/swe:Time/swe:uom
// sa:relatedObservation/om:Observation/om:parameter[2]/swe:Time/swe:value
// and this could be processing om:parameter[2] the second time for
// swe:value
// so we need to find it if it already exists
if (index > -1) {
// get the attribute of specified index
int valueIndex = 1;
for (Attribute stepValue : values) {
Object mappedIndex = stepValue.getUserData().get(
ComplexFeatureConstants.MAPPED_ATTRIBUTE_INDEX);
if (mappedIndex == null) {
mappedIndex = valueIndex;
}
if (index == Integer.parseInt(String.valueOf(mappedIndex))) {
leafAttribute = stepValue;
}
valueIndex++;
}
} else {
// get the last existing node
leafAttribute = values.get(values.size() - 1);
}
} else {
for (Attribute stepValue : values) {
// eliminate duplicates in case the values come from denormalized
// view..
boolean sameIndex = true;
if (index > -1) {
if (stepValue.getUserData().containsKey(
ComplexFeatureConstants.MAPPED_ATTRIBUTE_INDEX)) {
sameIndex = (index == Integer.parseInt(
String.valueOf(stepValue.getUserData().get(
ComplexFeatureConstants.MAPPED_ATTRIBUTE_INDEX))));
}
}
if (sameIndex && stepValue.getValue().equals(convertedValue)) {
leafAttribute = stepValue;
}
}
}
}
} else if (currStepValue instanceof Attribute) {
leafAttribute = (Attribute) currStepValue;
} else if (currStepValue != null) {
throw new IllegalStateException("Unknown addressed object. Xpath:"
+ attributeName + ", addressed: " + currStepValue.getClass().getName()
+ " [" + currStepValue.toString() + "]");
}
}
}
// Build a new leaf if either:
// (1) have no leaf (leafAttribute == null), or
// (2) maxOccurs is greater than one and existing leaf already has xlink:href, in which
// case, as insufficient information at this point to evaluate xlink:href expressions and
// remove duplicates, assume building multivalued xlink:href ClientProperty.
if (leafAttribute == null || (descriptor.getMaxOccurs() > 1
&& leafAttribute.getUserData().containsKey(Attributes.class)
&& ((Map<Object, Object>) leafAttribute.getUserData().get(Attributes.class))
.containsKey(AbstractMappingFeatureIterator.XLINK_HREF_NAME))) {
AppSchemaAttributeBuilder builder = new AppSchemaAttributeBuilder(featureFactory);
if (crs != null) {
builder.setCRS(crs);
}
builder.setDescriptor(parent.getDescriptor());
// check for mapped type override
builder.setType(parent.getType());
if (targetNodeType != null) {
if (parent.getType().getName().equals(XSSchema.ANYTYPE_TYPE.getName())) {
// special handling for casting any type since there's no attributes in its
// schema
leafAttribute = builder.addAnyTypeValue(convertedValue, targetNodeType,
descriptor, id);
} else {
leafAttribute = builder.add(id, convertedValue, attributeName, targetNodeType);
}
} else if (descriptor.getType().getName().equals(XSSchema.ANYTYPE_TYPE.getName())
&& (value == null || (value instanceof Collection && ((Collection) value)
.isEmpty()))) {
// casting anyType as a complex attribute so we can set xlink:href
leafAttribute = builder.addComplexAnyTypeAttribute(convertedValue, descriptor, id);
} else {
leafAttribute = builder.add(id, convertedValue, attributeName);
}
if (index > -1) {
// set attribute index if specified so it can be retrieved later for grouping
leafAttribute.getUserData().put(ComplexFeatureConstants.MAPPED_ATTRIBUTE_INDEX,
index);
}
List newValue = new ArrayList();
newValue.addAll((Collection) parent.getValue());
newValue.add(leafAttribute);
parent.setValue(newValue);
}
if (!isEmpty(convertedValue)) {
leafAttribute.setValue(convertedValue);
}
if (simpleContentProperties != null) {
mergeClientProperties(leafAttribute, simpleContentProperties);
}
return leafAttribute;
}
/**
* Extract the simple content attribute from a list of features.
* This is used when feature chaining is used for simple contents, such
* as gml:name.. therefore the iterator would create a list of features containing the
* simple content attributes.
* @param value List of features
* @return The attribute with simple content
*/
@SuppressWarnings("unchecked")
private List<Property> getSimpleContentList(Object value) {
if (value == null || !(value instanceof Collection)) {
return null;
}
Collection list = (Collection) value;
if (list.size() != 1) {
// there should only 1 feature in a list even if it's multi-valued
// since each value should be wrapped in its own parent node
// eg. the format is
// gsml:specification[1]/gsml:CompositionPart/...
// gsml:specification[2]/gsml:CompositionPart/...
throw new IllegalArgumentException("Expecting only 1 feature in the list!");
}
Object f = list.iterator().next();
if (!(f instanceof Feature)) {
throw new IllegalArgumentException("Expecting a feature!");
}
Feature feature = (Feature) f;
ArrayList<Property> properties = new ArrayList<Property>();
for (Property prop : feature.getProperties()) {
if (!ComplexFeatureConstants.FEATURE_CHAINING_LINK_NAME.equals(prop.getName())) {
properties.add(prop);
}
}
return properties;
}
/**
* Merge client properties from an attribute with a given map.
*
* @param leafAttribute
* The attribute which will have the client properties
* @param simpleContentProperties
* Map of new client properties
*/
@SuppressWarnings("unchecked")
private void mergeClientProperties(Attribute leafAttribute,
Map<Object, Object> simpleContentProperties) {
Map<Object, Object> origData = leafAttribute.getUserData();
for (Object key : simpleContentProperties.keySet()) {
if (key.equals(Attributes.class)) {
// client properties
Map inputMap = (Map) simpleContentProperties.get(key);
if (origData.containsKey(Attributes.class)) {
// check each entry, and copy if it doesn't exist
Map existingMap = (Map) origData.get(key);
for (Object mapKey : inputMap.keySet()) {
if (!existingMap.containsKey(mapKey)) {
existingMap.put(mapKey, inputMap.get(mapKey));
}
}
} else {
// copy the whole thing
origData.put(Attributes.class, inputMap);
}
} else {
if (!origData.containsKey(key)) {
origData.put(key, simpleContentProperties.get(key));
}
}
}
}
/**
* Determine whether or not the value is a feature with target descriptor that is of the given
* attribute descriptor. If it is, then it is a feature chained feature with only simple
* content.
*
* @param descriptor
* The attribute descriptor
* @param value
* value to check
* @return true if the value is an arraylist containing a feature with the descriptor.
*/
@SuppressWarnings("unchecked")
private boolean isFeatureChainedSimpleContent(AttributeDescriptor descriptor, Object value) {
boolean isFeatureChainedSimpleContent = false;
if (value != null) {
if (value instanceof Collection) {
Collection list = (Collection) value;
if (!list.isEmpty()) {
Object f = list.iterator().next();
if (f instanceof Feature) {
Name featureName = ((Feature) f).getDescriptor().getName();
if (((Feature) f).getProperty(featureName) != null) {
isFeatureChainedSimpleContent = true;
}
}
}
}
}
return isFeatureChainedSimpleContent;
}
private boolean isEmpty(Object convertedValue) {
if (convertedValue == null) {
return true;
} else if (convertedValue instanceof Collection && ((Collection) convertedValue).isEmpty()) {
return true;
} else {
return false;
}
}
/**
* Return value converted into a type suitable for this descriptor.
*
* @param descriptor
* @param value
* @return
*/
@SuppressWarnings("serial")
private Object convertValue(final AttributeDescriptor descriptor, final Object value) {
final AttributeType type = descriptor.getType();
Class<?> binding = type.getBinding();
if (type instanceof ComplexType && binding == Collection.class) {
if (!(value instanceof Collection)) {
boolean isSimpleContent = Types.isSimpleContentType(type);
boolean canHaveTextContent = Types.canHaveTextContent(type);
if (isSimpleContent || canHaveTextContent) {
ArrayList<Property> list = new ArrayList<Property>();
if (value == null && !descriptor.isNillable()) {
return list;
}
if (isSimpleContent) {
list.add(buildSimpleContent(type, value));
} else if (canHaveTextContent) {
list.add(buildTextContent(type, value));
}
return list;
}
} else {
// no conversion required
return value;
}
}
if (binding == String.class && value instanceof Collection) {
// if it's a single value in a collection, strip the square brackets
String collectionString = value.toString();
return collectionString.substring(1, collectionString.length() - 1);
}
return FF.literal(value).evaluate(value, binding);
}
/**
* Get base (non-collection) type of simple content.
*
* @param type
* @return
*/
static AttributeType getSimpleContentType(AttributeType type) {
Class<?> binding = type.getBinding();
if (binding == Collection.class) {
return getSimpleContentType(type.getSuper());
} else {
return type;
}
}
/**
* Create a fake property for simple content of a complex type.
*
* @param type
* @param value
* @return
*/
Attribute buildSimpleContent(AttributeType type, Object value) {
AttributeType simpleContentType = getSimpleContentType(type);
return buildSimpleContentInternal(simpleContentType, value);
}
/**
* Create a fake property to store arbitrary text in a complex type.
*
* <p>
* Passed in value is converted to a string and then stored in the special <code>simpleContent</code> attribute.
* </p>
*
* @param type
* @param value
* @return
*/
Attribute buildTextContent(AttributeType type, Object value) {
AttributeTypeBuilder atb = new AttributeTypeBuilder();
atb.setName(ComplexFeatureConstants.SIMPLE_CONTENT.getLocalPart());
atb.setBinding(String.class);
AttributeType textContentType = atb.buildType();
return buildSimpleContentInternal(textContentType, value);
}
private Attribute buildSimpleContentInternal(AttributeType simpleContentType, Object value) {
Object convertedValue = FF.literal(value).evaluate(value, simpleContentType.getBinding());
AttributeDescriptor descriptor = new AttributeDescriptorImpl(simpleContentType,
ComplexFeatureConstants.SIMPLE_CONTENT, 1, 1, true, (Object) null);
return new AttributeImpl(convertedValue, descriptor, null);
}
public boolean isComplexType(final StepList attrXPath, final AttributeDescriptor featureType) {
PropertyName attExp = FF.property(attrXPath.toString());
Object type = attExp.evaluate(featureType);
if (type == null) {
type = attExp.evaluate(featureType);
throw new IllegalArgumentException("path not found: " + attrXPath);
}
AttributeDescriptor node = (AttributeDescriptor) type;
return node.getType() instanceof ComplexType;
}
/**
* @return true if this step represents an id attribute
*/
public static boolean isId(Step step) {
return step.isXmlAttribute() && step.getName().equals(GML.id);
}
}