From: <svn...@os...> - 2010-03-10 08:52:15
|
Author: ang05a Date: 2010-03-10 03:51:59 -0500 (Wed, 10 Mar 2010) New Revision: 34998 Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/DataAccessMappingFeatureIterator.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/MappingFeatureSource.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/NestedAttributeMapping.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/config/AppSchemaDataAccessConfigurator.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/filter/XPath.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/AttributeBuilder.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/Types.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/filter/NestedAttributeExpression.java branches/2.6.x/modules/unsupported/app-schema/app-schema/src/test/resources/test-data/AppSchemaDataAccess.xsd Log: GEOT-2674: Support polymorphism in app-schema Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/DataAccessMappingFeatureIterator.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/DataAccessMappingFeatureIterator.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/DataAccessMappingFeatureIterator.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -29,6 +29,7 @@ import javax.xml.namespace.QName; +import org.geotools.data.DataAccess; import org.geotools.data.DataSourceException; import org.geotools.data.DefaultQuery; import org.geotools.data.FeatureSource; @@ -299,15 +300,27 @@ * * @return Feature. Target feature sets with simple attributes */ - protected void setAttributeValue(Feature target, final ComplexAttribute source, + protected void setAttributeValue(Attribute target, final Feature source, final AttributeMapping attMapping) throws IOException { final Expression sourceExpression = attMapping.getSourceExpression(); final AttributeType targetNodeType = attMapping.getTargetNodeInstance(); final StepList xpath = attMapping.getTargetXPath(); Map<Name, Expression> clientPropsMappings = attMapping.getClientProperties(); + boolean isNestedFeature = attMapping.isNestedAttribute(); + String id = null; + if (Expression.NIL != attMapping.getIdentifierExpression()) { + id = extractIdForAttribute(attMapping.getIdentifierExpression(), source); + } - boolean isNestedFeature = attMapping.isNestedAttribute(); + if (attMapping.isNestedAttribute()) { + NestedAttributeMapping nestedMapping = ((NestedAttributeMapping) attMapping); + if (nestedMapping.isPolymorphic()) { + setPolymorphicValues(target, id, nestedMapping, source, xpath, clientPropsMappings); + return; + } + } + // otherwise, leave the value as null Object value = getValues(attMapping.isMultiValued(), sourceExpression, source); boolean isHRefLink = isByReference(clientPropsMappings, isNestedFeature); if (isNestedFeature) { @@ -331,10 +344,10 @@ // eg. gsml:GeologicUnit/gsml:occurence/gsml:MappedFeature // and gsml:MappedFeature/gsml:specification/gsml:GeologicUnit nestedFeatures.addAll(((NestedAttributeMapping) attMapping) - .getInputFeatures(val, reprojection)); + .getInputFeatures(val, reprojection, source)); } else { nestedFeatures.addAll(((NestedAttributeMapping) attMapping).getFeatures( - val, reprojection)); + val, reprojection, source)); } } value = nestedFeatures; @@ -343,9 +356,9 @@ // feature type also have a reference back to this type // eg. gsml:GeologicUnit/gsml:occurence/gsml:MappedFeature // and gsml:MappedFeature/gsml:specification/gsml:GeologicUnit - value = ((NestedAttributeMapping) attMapping).getInputFeatures(value, reprojection); + value = ((NestedAttributeMapping) attMapping).getInputFeatures(value, reprojection, source); } else { - value = ((NestedAttributeMapping) attMapping).getFeatures(value, reprojection); + value = ((NestedAttributeMapping) attMapping).getFeatures(value, reprojection, source); } if (isHRefLink) { // only need to set the href link value, not the nested feature properties @@ -353,10 +366,6 @@ return; } } - String id = null; - if (Expression.NIL != attMapping.getIdentifierExpression()) { - id = extractIdForAttribute(attMapping.getIdentifierExpression(), source); - } if (isNestedFeature) { assert (value instanceof Collection); } @@ -411,12 +420,63 @@ } /** + * Special handling for polymorphic mapping. Works out the polymorphic type name by evaluating + * the function on the feature, then set the relevant sub-type values. + * + * @param target + * The target feature to be encoded + * @param id + * The target feature id + * @param nestedMapping + * The mapping that is polymorphic + * @param source + * The source simple feature + * @param xpath + * The xpath of polymorphic type + * @param clientPropsMappings + * Client properties + * @throws IOException + */ + private void setPolymorphicValues(Attribute target, String id, + NestedAttributeMapping nestedMapping, Feature source, StepList xpath, + Map<Name, Expression> clientPropsMappings) throws IOException { + Name polymorphicTypeName = nestedMapping.getNestedFeatureType(source); + if (polymorphicTypeName != null) { + // process sub-type mapping + DataAccess<FeatureType, Feature> da = DataAccessRegistry + .getDataAccess(polymorphicTypeName); + if (da instanceof AppSchemaDataAccess) { + // why wouldn't it be? check just to be safe + List<AttributeMapping> polymorphicMappings = ((AppSchemaDataAccess) da).getMapping( + polymorphicTypeName).getAttributeMappings(); + StepList prefixedXpath = xpath.clone(); + prefixedXpath.add(new Step(new QName(polymorphicTypeName.getNamespaceURI(), + polymorphicTypeName.getLocalPart(), this.namespaces + .getPrefix(polymorphicTypeName.getNamespaceURI())), 1)); + AttributeDescriptor attDescriptor = ((MappingFeatureSource) da + .getFeatureSource(polymorphicTypeName)).getTargetFeature(); + Attribute instance = xpathAttributeBuilder.set(target, prefixedXpath, null, id, + da.getSchema(polymorphicTypeName), false, attDescriptor); + setClientProperties(instance, source, clientPropsMappings); + for (AttributeMapping mapping : polymorphicMappings) { + if (isTopLevelmapping(polymorphicTypeName, mapping.getTargetXPath())) { + // ignore the top level mapping for the Feature itself + // as it was already set + continue; + } + setAttributeValue(instance, source, mapping); + } + } + }// the value could be configured as null, it's a form of polymorphism.. do nothing + } + + /** * Set xlink:href client property for multi-valued chained features. This has to be specially * handled because we don't want to encode the nested features attributes, since it's already an * xLink. Also we need to eliminate duplicates. * * @param target - * The target feature + * The target attribute * @param clientPropsMappings * Client properties mappings * @param value @@ -426,7 +486,7 @@ * @param targetNodeType * Target node type */ - protected void setXlinkReference(Feature target, Map<Name, Expression> clientPropsMappings, + protected void setXlinkReference(Attribute target, Map<Name, Expression> clientPropsMappings, Object value, StepList xpath, AttributeType targetNodeType) { // Make sure the same value isn't already set // in case it comes from a denormalized view for many-to-many relationship. @@ -583,15 +643,10 @@ Feature target = (Feature) builder.build(id); for (AttributeMapping attMapping : mappings) { - StepList targetXpathProperty = attMapping.getTargetXPath(); - if (targetXpathProperty.size() == 1) { - Step rootStep = (Step) targetXpathProperty.get(0); - QName stepName = rootStep.getName(); - if (Types.equals(targetNodeName, stepName)) { - // ignore the top level mapping for the Feature itself - // as it was already set - continue; - } + if (isTopLevelmapping(targetNodeName, attMapping.getTargetXPath())) { + // ignore the top level mapping for the Feature itself + // as it was already set + continue; } // extract the values from multiple source features of the same id // and set them to one built feature @@ -609,6 +664,17 @@ return target; } + private boolean isTopLevelmapping(Name targetNodeName, StepList targetXPath) { + if (targetXPath.size() == 1) { + Step rootStep = targetXPath.get(0); + QName stepName = rootStep.getName(); + if (Types.equals(targetNodeName, stepName)) { + return true; + } + } + return false; + } + protected Feature populateFeatureData(String id) throws IOException { throw new UnsupportedOperationException("populateFeatureData should not be called!"); } @@ -640,7 +706,7 @@ * The xPath matching the attribute * @return The first matching attribute */ - private Property getProperty(ComplexAttribute root, StepList xpath) { + private Property getProperty(Attribute root, StepList xpath) { Property property = root; final StepList steps = new StepList(xpath); Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/MappingFeatureSource.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/MappingFeatureSource.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/MappingFeatureSource.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -35,6 +35,7 @@ import org.geotools.feature.FeatureIterator; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.feature.Feature; +import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; @@ -136,6 +137,10 @@ public FeatureType getSchema() { return (FeatureType) mapping.getTargetFeature().getType(); } + + public AttributeDescriptor getTargetFeature() { + return mapping.getTargetFeature(); + } protected FeatureTypeMapping getMapping() { return mapping; Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/NestedAttributeMapping.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/NestedAttributeMapping.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/NestedAttributeMapping.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.Map; @@ -27,6 +28,8 @@ import org.geotools.data.FeatureSource; import org.geotools.data.complex.filter.XPath.StepList; import org.geotools.feature.FeatureCollection; +import org.geotools.feature.Types; +import org.geotools.filter.AttributeExpressionImpl; import org.geotools.filter.FilterFactoryImplNamespaceAware; import org.geotools.filter.NestedAttributeExpression; import org.opengis.feature.Feature; @@ -47,8 +50,10 @@ * features are also stored for caching if a filter involving these nested features is run. * * @author Rini Angreani, Curtin University of Technology - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/unsupported/app-schema/app-schema/src/main + * /java/org/geotools/data/complex/NestedAttributeMapping.java $ */ public class NestedAttributeMapping extends AttributeMapping { /** @@ -64,7 +69,7 @@ /** * Name of the nested features element */ - private final Name nestedFeatureType; + private final Expression nestedFeatureType; /** * Target xpath that links to nested features @@ -81,6 +86,8 @@ */ private FilterFactory filterFac; + private NamespaceSupport namespaces; + /** * Sole constructor * @@ -100,12 +107,13 @@ */ public NestedAttributeMapping(Expression idExpression, Expression parentExpression, StepList targetXPath, boolean isMultiValued, Map<Name, Expression> clientProperties, - Name sourceElement, StepList sourcePath, NamespaceSupport namespaces) + Expression sourceElement, StepList sourcePath, NamespaceSupport namespaces) throws IOException { super(idExpression, parentExpression, targetXPath, null, isMultiValued, clientProperties); this.nestedTargetXPath = sourcePath; this.nestedFeatureType = sourceElement; this.filterFac = new FilterFactoryImplNamespaceAware(namespaces); + this.namespaces = namespaces; } @Override @@ -127,13 +135,24 @@ * @throws IOException */ public Collection<Feature> getInputFeatures(Object foreignKeyValue, - CoordinateReferenceSystem reprojection) throws IOException { + CoordinateReferenceSystem reprojection, Feature feature) throws IOException { + if (isPolymorphic()) { + // if linkField is null, this method shouldn't be called because the mapping + // should use the same table, and handles it differently + throw new UnsupportedOperationException( + "Link field is missing from feature chaining mapping!"); + } ArrayList<Feature> matchingFeatures = new ArrayList<Feature>(); - if (source == null) { + if (source == null || !(nestedFeatureType instanceof AttributeExpressionImpl)) { // We can't initiate this in the constructor because the feature type mapping // might not be built yet. + Name featureTypeName = getNestedFeatureType(feature); + if (featureTypeName == null) { + // this could be legitimate, for some null values polymorphism use case + return Collections.EMPTY_LIST; + } FeatureTypeMapping featureTypeMapping = AppSchemaDataAccessRegistry - .getMapping(nestedFeatureType); + .getMapping(featureTypeName); assert featureTypeMapping != null; source = featureTypeMapping.getSource(); @@ -157,10 +176,10 @@ Iterator<Feature> it = fCollection.iterator(); while (it.hasNext()) { - Feature feature = it.next(); - Object value = this.nestedSourceExpression.evaluate(feature); + Feature f = it.next(); + Object value = this.nestedSourceExpression.evaluate(f); if (value != null && value.equals(foreignKeyValue)) { - matchingFeatures.add(feature); + matchingFeatures.add(f); } } fCollection.close(it); @@ -178,10 +197,22 @@ * @throws IOException */ public Collection<Feature> getFeatures(Object foreignKeyValue, - CoordinateReferenceSystem reprojection) throws IOException { - if (mappingSource == null) { + CoordinateReferenceSystem reprojection, Feature feature) throws IOException { + if (isPolymorphic()) { + // if linkField is null, this method shouldn't be called because the mapping + // should use the same table, and handles it differently + throw new UnsupportedOperationException( + "Link field is missing from feature chaining mapping!"); + } + if (mappingSource == null || !(nestedFeatureType instanceof AttributeExpressionImpl)) { + // initiate if null, or evaluate a new one if the targetElement is a function + // which value depends on the feature + Name featureTypeName = getNestedFeatureType(feature); + if (featureTypeName == null) { + return Collections.EMPTY_LIST; + } // this cannot be set in the constructor since it might not exist yet - mappingSource = DataAccessRegistry.getFeatureSource(nestedFeatureType); + mappingSource = DataAccessRegistry.getFeatureSource(featureTypeName); } assert mappingSource != null; Filter filter; @@ -217,7 +248,26 @@ /** * @return the nested feature type name */ - public Name getNestedFeatureType() { - return this.nestedFeatureType; + public Name getNestedFeatureType(Feature feature) { + Object fTypeValue; + if (nestedFeatureType instanceof AttributeExpressionImpl) { + fTypeValue = ((AttributeExpressionImpl) nestedFeatureType).getPropertyName(); + } else { + fTypeValue = nestedFeatureType.evaluate(feature); + } + if (fTypeValue == null) { + // this could be legitimate, i.e. in polymorphism + // to evaluate a function with a certain column value + // if null, don't encode this element + return null; + } + return Types.degloseName(fTypeValue.toString(), namespaces); } + + public boolean isPolymorphic() { + // if the linkField is null, we're meant to work out the nestedFeatureType from + // the linkElement, which should contain a function. So the value could vary + // feature per feature. + return this.nestedTargetXPath == null; + } } Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/config/AppSchemaDataAccessConfigurator.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/config/AppSchemaDataAccessConfigurator.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/config/AppSchemaDataAccessConfigurator.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -225,7 +225,7 @@ private AttributeDescriptor getTargetDescriptor(TypeMapping dto, GeometryType geomType) throws IOException { String prefixedTargetName = dto.getTargetElementName(); - Name targetNodeName = degloseName(prefixedTargetName); + Name targetNodeName = Types.degloseName(prefixedTargetName, namespaces); AttributeDescriptor targetDescriptor = typeRegistry.getDescriptor(targetNodeName, geomType, dto.getAttributeMappings()); @@ -299,7 +299,7 @@ final Map clientProperties = getClientProperties(attDto, itemXpath); if (expectedInstanceTypeName != null) { - Name expectedNodeTypeName = degloseTypeName(expectedInstanceTypeName); + Name expectedNodeTypeName = Types.degloseName(expectedInstanceTypeName, namespaces); expectedInstanceOf = typeRegistry .getAttributeType(expectedNodeTypeName, null, null); if (expectedInstanceOf == null) { @@ -319,15 +319,19 @@ attDto.getParentLabel(), attDto.getTargetQueryString(), attDto.getInstancePath()); } else if (sourceElement != null) { - // nested complex attributes + // nested complex attributes, this could be a function expression for polymorphic types + Expression elementExpr = parseOgcCqlExpression(sourceElement); String sourceField = attDto.getLinkField(); - assert sourceField != null; // source field must be specified - - final StepList sourceFieldSteps = XPath.steps(root, sourceField, namespaces); + StepList sourceFieldSteps = null; + if (sourceField != null) { + // it could be null for polymorphism mapping, + // i.e. when the linked element maps to the same table as the container mapping + sourceFieldSteps = XPath.steps(root, sourceField, namespaces); + } // a nested feature attMapping = new NestedAttributeMapping(idExpression, sourceExpression, targetXPathSteps, isMultiValued, clientProperties, - degloseTypeName(sourceElement), sourceFieldSteps, namespaces); + elementExpr, sourceFieldSteps, namespaces); } else { attMapping = new AttributeMapping(idExpression, sourceExpression, targetXPathSteps, expectedInstanceOf, isMultiValued, clientProperties); @@ -396,7 +400,7 @@ for (Iterator it = dto.getClientProperties().entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); String name = (String) entry.getKey(); - Name qName = degloseName(name); + Name qName = Types.degloseName(name, namespaces); String cqlExpression = (String) entry.getValue(); final Expression expression; @@ -427,7 +431,7 @@ AppSchemaDataAccessConfigurator.LOGGER.fine("asking datastore " + sourceDataStore + " for source type " + typeName); - Name name = degloseName(typeName); + Name name = Types.degloseName(typeName, namespaces); FeatureSource fSource = (FeatureSource) sourceDataStore.getFeatureSource(name); if (inputDataAccessIds.contains(dsId)) { @@ -646,59 +650,4 @@ } return resolvedParams; } - - /** - * Takes a prefixed attribute name and returns an {@link Name} by looking which namespace - * belongs the prefix to in {@link AppSchemaDataAccessDTO#getNamespaces()}. - * - * @param prefixedName - * @return - * @throws IllegalArgumentException - * if <code>prefixedName</code> has no prefix. - */ - private Name degloseTypeName(String prefixedName) throws IllegalArgumentException { - Name name = null; - - if (prefixedName == null) { - return null; - } - - int prefixIdx = prefixedName.indexOf(':'); - if (prefixIdx == -1) { - return Types.typeName(prefixedName); - // throw new IllegalArgumentException(prefixedName + " is not - // prefixed"); - } - - String nsPrefix = prefixedName.substring(0, prefixIdx); - String localName = prefixedName.substring(prefixIdx + 1); - String nsUri = namespaces.getURI(nsPrefix); - - name = Types.typeName(nsUri, localName); - - return name; - } - - private Name degloseName(String prefixedName) throws IllegalArgumentException { - Name name = null; - - if (prefixedName == null) { - return null; - } - - int prefixIdx = prefixedName.indexOf(':'); - if (prefixIdx == -1) { - return Types.typeName(prefixedName); - // throw new IllegalArgumentException(prefixedName + " is not - // prefixed"); - } - - String nsPrefix = prefixedName.substring(0, prefixIdx); - String localName = prefixedName.substring(prefixIdx + 1); - String nsUri = namespaces.getURI(nsPrefix); - - name = Types.typeName(nsUri, localName); - - return name; - } } Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/filter/XPath.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/filter/XPath.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/data/complex/filter/XPath.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -526,6 +526,11 @@ */ public Attribute set(final Attribute att, final StepList xpath, Object value, String id, AttributeType targetNodeType, boolean isXlinkRef) { + return set(att, xpath, value, id, targetNodeType, isXlinkRef, null); + } + + public Attribute set(final Attribute att, final StepList xpath, Object value, String id, + AttributeType targetNodeType, boolean isXlinkRef, AttributeDescriptor targetDescriptor) { if (XPath.LOGGER.isLoggable(Level.CONFIG)) { XPath.LOGGER.entering("XPath", "set", new Object[] { att, xpath, value, id, targetNodeType }); @@ -538,7 +543,7 @@ // xpath); // } - ComplexAttribute parent = (ComplexAttribute) att; + Attribute parent = att; Name rootName = null; if (parent.getDescriptor() != null) { rootName = parent.getDescriptor().getName(); @@ -557,78 +562,85 @@ for (; stepsIterator.hasNext();) { final XPath.Step currStep = (Step) stepsIterator.next(); - final ComplexType parentType = (ComplexType) parent.getType(); + AttributeDescriptor currStepDescriptor = null; + final boolean isLastStep = !stepsIterator.hasNext(); final QName stepName = currStep.getName(); final Name attributeName = Types.toName(stepName); - final boolean isLastStep = !stepsIterator.hasNext(); + final AttributeType _parentType = parent.getType(); + if (_parentType.equals(XSSchema.ANYTYPE_TYPE) && 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; - AttributeDescriptor currStepDescriptor = null; + if (!isLastStep || targetNodeType == null) { + if (null == attributeName.getNamespaceURI()) { + currStepDescriptor = (AttributeDescriptor) Types.descriptor(parentType, + attributeName.getLocalPart()); + } else { + currStepDescriptor = (AttributeDescriptor) Types.descriptor(parentType, + attributeName); + } - if (!isLastStep || targetNodeType == null) { - if (null == attributeName.getNamespaceURI()) { - currStepDescriptor = (AttributeDescriptor) Types.descriptor(parentType, - attributeName.getLocalPart()); + 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 { - currStepDescriptor = (AttributeDescriptor) Types.descriptor(parentType, - attributeName); - } + AttributeDescriptor actualDescriptor; + if (null == attributeName.getNamespaceURI()) { + actualDescriptor = (AttributeDescriptor) Types.descriptor(parentType, + attributeName.getLocalPart(), targetNodeType); + } else { + actualDescriptor = (AttributeDescriptor) Types.descriptor(parentType, + attributeName, targetNodeType); + } - 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(); + if (actualDescriptor != null) { + int minOccurs = actualDescriptor.getMinOccurs(); + int maxOccurs = actualDescriptor.getMaxOccurs(); + boolean nillable = actualDescriptor.isNillable(); + currStepDescriptor = descriptorFactory + .createAttributeDescriptor(targetNodeType, attributeName, + minOccurs, maxOccurs, nillable, null); } } - } else { - AttributeDescriptor actualDescriptor; - if (null == attributeName.getNamespaceURI()) { - actualDescriptor = (AttributeDescriptor) Types.descriptor(parentType, - attributeName.getLocalPart(), targetNodeType); - } else { - actualDescriptor = (AttributeDescriptor) Types.descriptor(parentType, - attributeName, targetNodeType); - } - if (actualDescriptor != null) { - int minOccurs = actualDescriptor.getMinOccurs(); - int maxOccurs = actualDescriptor.getMaxOccurs(); - boolean nillable = actualDescriptor.isNillable(); - 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(", "); + 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); } - 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()); + + " is not a valid location path for type " + _parentType.getName()); } int index = currStep.getIndex(); Attribute attribute = setValue(currStepDescriptor, id, value, index, parent, @@ -638,49 +650,50 @@ // parent = appendComplexProperty(parent, currStep, // currStepDescriptor); int index = currStep.getIndex(); - Attribute _parent = setValue(currStepDescriptor, null, null, index, parent, null, - isXlinkRef); - parent = (ComplexAttribute) _parent; + parent = setValue(currStepDescriptor, null, new ArrayList<Property>(), index, + parent, null, isXlinkRef); } } throw new IllegalStateException(); } private Attribute setValue(final AttributeDescriptor descriptor, final String id, - final Object value, final int index, final ComplexAttribute parent, + final Object value, final int index, final Attribute parent, final AttributeType targetNodeType, boolean isXlinkRef) { // adapt value to context Object convertedValue = convertValue(descriptor, value); Attribute leafAttribute = null; final Name attributeName = descriptor.getName(); - Object currStepValue = parent.getProperties(attributeName); - 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 (currStepValue instanceof Collection) { - List<Attribute> values = new ArrayList((Collection) currStepValue); - if (convertedValue == null && values.size() >= index) { - leafAttribute = (Attribute) values.get(index - 1); - } - for (Attribute stepValue : values) { - // eliminate duplicates in case the values come from denormalized view.. - if (stepValue.getValue().equals(convertedValue)) { - return stepValue; + if (parent instanceof ComplexAttribute) { + Object currStepValue = ((ComplexAttribute) parent).getProperties(attributeName); + 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 (currStepValue instanceof Collection) { + List<Attribute> values = new ArrayList((Collection) currStepValue); + if (isEmpty(convertedValue) && values.size() >= index) { + leafAttribute = (Attribute) values.get(index - 1); } + for (Attribute stepValue : values) { + // eliminate duplicates in case the values come from denormalized view.. + if (stepValue.getValue().equals(convertedValue)) { + return 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() + "]"); } - } 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() + "]"); } } if (leafAttribute == null) { AttributeBuilder builder = new AttributeBuilder(featureFactory); builder.setDescriptor(parent.getDescriptor()); - //check for mapped type override + // check for mapped type override builder.setType(parent.getType()); if (parent instanceof ComplexAttribute) { @@ -688,10 +701,11 @@ Collection properties = (Collection) complex.getValue(); for (Iterator itr = properties.iterator(); itr.hasNext();) { Property property = (Property) itr.next(); - if (property instanceof Attribute && ! property.getName().getLocalPart().equals("simpleContent")) { + if (property instanceof Attribute + && !property.getName().getLocalPart().equals("simpleContent")) { Attribute att = (Attribute) property; - builder.add(att.getIdentifier() == null ? null : att.getIdentifier().toString(), att - .getValue(), att.getName()); + builder.add(att.getIdentifier() == null ? null : att.getIdentifier() + .toString(), att.getValue(), att.getName()); } else if (property instanceof Association) { Association assoc = (Association) property; builder.associate(assoc.getValue(), assoc.getName()); @@ -700,7 +714,14 @@ } if (targetNodeType != null) { - leafAttribute = builder.add(id, convertedValue, attributeName, targetNodeType); + if (parent.getType().equals(XSSchema.ANYTYPE_TYPE)) { + // 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 { leafAttribute = builder.add(id, convertedValue, attributeName); } @@ -710,12 +731,23 @@ parent.setValue(newValue); } - if (convertedValue != null) { + if (!isEmpty(convertedValue)) { leafAttribute.setValue(convertedValue); } return leafAttribute; } + 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. * @@ -732,7 +764,7 @@ ArrayList<Property> list = new ArrayList<Property>(); list.add(buildSimpleContent(type, value)); return list; - } + } } if (binding == String.class && value instanceof Collection) { // if it's a single value in a collection, strip the square brackets Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/AttributeBuilder.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/AttributeBuilder.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/AttributeBuilder.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -23,6 +23,7 @@ import java.util.List; import java.util.logging.Logger; +import org.geotools.data.complex.config.NonFeatureTypeProxy; import org.geotools.feature.type.AttributeDescriptorImpl; import org.opengis.feature.Association; import org.opengis.feature.Attribute; @@ -370,7 +371,16 @@ descriptor = new AttributeDescriptorImpl(type, name, minOccurs, maxOccurs, nillable, defaultValue); } - Attribute attribute = create(value, null, descriptor, id); + Attribute attribute; + if (descriptor != null && descriptor.getType() instanceof NonFeatureTypeProxy) { + // we don't want a feature. NonFeatureTypeProxy is used to make non feature types + // a fake feature type, so it can be created as top level feature in app-schema + // mapping file. When created inside other features, it should be encoded as + // complex features though. + attribute = createComplexAttribute(value, null, descriptor, id); + } else { + attribute = create(value, null, descriptor, id); + } properties().add(attribute); return attribute; } @@ -555,9 +565,7 @@ descriptor, id) : attributeFactory.createFeature((Collection) value, (FeatureType) type, id); } else if (type instanceof ComplexType) { - return descriptor != null ? attributeFactory.createComplexAttribute((Collection) value, - descriptor, id) : attributeFactory.createComplexAttribute((Collection) value, - (ComplexType) type, id); + return createComplexAttribute((Collection) value, (ComplexType) type, descriptor, id); } else if (type instanceof GeometryType) { return attributeFactory.createGeometryAttribute(value, (GeometryDescriptor) descriptor, id, getCRS()); @@ -567,6 +575,22 @@ } /** + * Create complex attribute + * + * @param value + * @param type + * @param descriptor + * @param id + * @return + */ + public ComplexAttribute createComplexAttribute(Object value, ComplexType type, + AttributeDescriptor descriptor, String id) { + return descriptor != null ? attributeFactory.createComplexAttribute((Collection) value, + descriptor, id) : attributeFactory.createComplexAttribute((Collection) value, type, + id); + } + + /** * Builds the attribute. * <p> * The class of the attribute built is determined from its type set with @@ -619,4 +643,24 @@ properties().clear(); return built; } + + /** + * Special case for any type. Skip validating existence in the schema, since anyType legally can + * be casted into anything. + * + * @param value + * the value to be set + * @param type + * the type of the value + * @param descriptor + * the attribute descriptor of anyType type + * @param id + * @return + */ + public Attribute addAnyTypeValue(Object value, AttributeType type, + AttributeDescriptor descriptor, String id) { + Attribute attribute = create(value, type, descriptor, id); + properties().add(attribute); + return attribute; + } } Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/Types.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/Types.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/feature/Types.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -29,6 +29,7 @@ import javax.xml.namespace.QName; import org.opengis.feature.IllegalAttributeException; +import org.geotools.data.complex.config.AppSchemaDataAccessDTO; import org.geotools.feature.type.AttributeTypeImpl; import org.opengis.feature.Attribute; import org.opengis.feature.ComplexAttribute; @@ -40,6 +41,7 @@ import org.opengis.feature.type.PropertyType; import org.opengis.filter.Filter; import org.opengis.filter.identity.Identifier; +import org.xml.sax.helpers.NamespaceSupport; /** * This is a set of utility methods used when <b>implementing</b> types. @@ -751,7 +753,43 @@ return name.getNamespaceURI().equals(qName.getNamespaceURI()) && name.getLocalPart().equals(qName.getLocalPart()); } + + + /** + * Takes a prefixed attribute name and returns an {@link Name} by looking which namespace + * belongs the prefix to in {@link AppSchemaDataAccessDTO#getNamespaces()}. + * + * @param prefixedName + * , namespaces + * @return + * @throws IllegalArgumentException + * if <code>prefixedName</code> has no prefix. + */ + public static Name degloseName(String prefixedName, NamespaceSupport namespaces) + throws IllegalArgumentException { + Name name = null; + + if (prefixedName == null) { + return null; + } + + int prefixIdx = prefixedName.indexOf(':'); + if (prefixIdx == -1) { + return Types.typeName(prefixedName); + // throw new IllegalArgumentException(prefixedName + " is not + // prefixed"); + } + + String nsPrefix = prefixedName.substring(0, prefixIdx); + String localName = prefixedName.substring(prefixIdx + 1); + String nsUri = namespaces.getURI(nsPrefix); + + name = Types.typeName(nsUri, localName); + + return name; + } + // /** Wander up getSuper gathering all memberTypes */ // public static Set/*<FeatureType>*/ memberTypes( // FeatureCollectionType collectionType) { Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/filter/NestedAttributeExpression.java =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/filter/NestedAttributeExpression.java 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/main/java/org/geotools/filter/NestedAttributeExpression.java 2010-03-10 08:51:59 UTC (rev 34998) @@ -243,7 +243,7 @@ FeatureTypeMapping fMapping = null; try { ArrayList<Feature> featureList = getFeatures((NestedAttributeMapping) mapping, - attributeValues); + attributeValues, root); // get the next type if there is any stepIndex++; @@ -296,19 +296,22 @@ * @throws IOException */ private ArrayList<Feature> getFeatures(NestedAttributeMapping mapping, - Collection<Object> foreignKeys) throws IOException { + Collection<Object> foreignKeys, Feature feature) throws IOException { ArrayList<Feature> featureList = new ArrayList<Feature>(); - boolean hasSimpleFeatures = AppSchemaDataAccessRegistry.hasName(mapping - .getNestedFeatureType()); + Name fTypeName = mapping.getNestedFeatureType(feature); + if (fTypeName == null) { + return featureList; + } + boolean hasSimpleFeatures = AppSchemaDataAccessRegistry.hasName(fTypeName); if (hasSimpleFeatures) { for (Object val : foreignKeys) { - featureList.addAll(mapping.getInputFeatures(val, crs)); + featureList.addAll(mapping.getInputFeatures(val, crs, feature)); } } else { for (Object val : foreignKeys) { - featureList.addAll(mapping.getFeatures(val, crs)); + featureList.addAll(mapping.getFeatures(val, crs, feature)); } } return featureList; Modified: branches/2.6.x/modules/unsupported/app-schema/app-schema/src/test/resources/test-data/AppSchemaDataAccess.xsd =================================================================== --- branches/2.6.x/modules/unsupported/app-schema/app-schema/src/test/resources/test-data/AppSchemaDataAccess.xsd 2010-03-10 06:45:33 UTC (rev 34997) +++ branches/2.6.x/modules/unsupported/app-schema/app-schema/src/test/resources/test-data/AppSchemaDataAccess.xsd 2010-03-10 08:51:59 UTC (rev 34998) @@ -336,7 +336,7 @@ </documentation> </annotation> <sequence> - <choice> + <choice minOccurs="0"> <element name="OCQL" type="string" /> <element name="Expression"> <complexType> |