/** * 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 Attribute setPolymorphicValues( Name mappingName, Attribute target, String id, NestedAttributeMapping nestedMapping, Object source, StepList xpath, Map<Name, Expression> clientPropsMappings) throws IOException { // process sub-type mapping DataAccess<FeatureType, Feature> da = DataAccessRegistry.getDataAccess((Name) mappingName); if (da instanceof AppSchemaDataAccess) { // why wouldn't it be? check just to be safe FeatureTypeMapping fTypeMapping = ((AppSchemaDataAccess) da).getMappingByName((Name) mappingName); List<AttributeMapping> polymorphicMappings = fTypeMapping.getAttributeMappings(); AttributeDescriptor attDescriptor = fTypeMapping.getTargetFeature(); AttributeType type = attDescriptor.getType(); Name polymorphicTypeName = attDescriptor.getName(); StepList prefixedXpath = xpath.clone(); prefixedXpath.add( new Step( new QName( polymorphicTypeName.getNamespaceURI(), polymorphicTypeName.getLocalPart(), this.namespaces.getPrefix(polymorphicTypeName.getNamespaceURI())), 1)); if (!fTypeMapping.getFeatureIdExpression().equals(Expression.NIL)) { id = fTypeMapping.getFeatureIdExpression().evaluate(source, String.class); } Attribute instance = xpathAttributeBuilder.set( target, prefixedXpath, null, id, type, false, attDescriptor, null); setClientProperties(instance, source, clientPropsMappings); for (AttributeMapping mapping : polymorphicMappings) { if (skipTopElement(polymorphicTypeName, mapping, type)) { // if the top level mapping for the Feature itself, the attribute instance // has already been created.. just need to set the client properties setClientProperties(instance, source, mapping.getClientProperties()); continue; } setAttributeValue( instance, null, source, mapping, null, null, selectedProperties.get(mapping)); } return instance; } return null; }
/** * Sets the values of grouping attributes. * * @param target * @param source * @param attMapping * @param values * @return Feature. Target feature sets with simple attributes */ protected Attribute setAttributeValue( Attribute target, String id, final Object source, final AttributeMapping attMapping, Object values, StepList inputXpath, List<PropertyName> selectedProperties) throws IOException { final Expression sourceExpression = attMapping.getSourceExpression(); final AttributeType targetNodeType = attMapping.getTargetNodeInstance(); StepList xpath = inputXpath == null ? attMapping.getTargetXPath().clone() : inputXpath; Map<Name, Expression> clientPropsMappings = attMapping.getClientProperties(); boolean isNestedFeature = attMapping.isNestedAttribute(); if (id == null && Expression.NIL != attMapping.getIdentifierExpression()) { id = extractIdForAttribute(attMapping.getIdentifierExpression(), source); } if (attMapping.isNestedAttribute()) { NestedAttributeMapping nestedMapping = ((NestedAttributeMapping) attMapping); Object mappingName = nestedMapping.getNestedFeatureType(source); if (mappingName != null) { if (nestedMapping.isSameSource() && mappingName instanceof Name) { // data type polymorphism mapping return setPolymorphicValues( (Name) mappingName, target, id, nestedMapping, source, xpath, clientPropsMappings); } else if (mappingName instanceof String) { // referential polymorphism mapping return setPolymorphicReference( (String) mappingName, clientPropsMappings, target, xpath, targetNodeType); } } else { // polymorphism could result in null, to skip the attribute return null; } } if (values == null && source != null) { values = getValues(attMapping.isMultiValued(), sourceExpression, source); } boolean isHRefLink = isByReference(clientPropsMappings, isNestedFeature); if (isNestedFeature) { if (values == null) { // polymorphism use case, if the value doesn't match anything, don't encode return null; } // get built feature based on link value if (values instanceof Collection) { ArrayList<Attribute> nestedFeatures = new ArrayList<Attribute>(((Collection) values).size()); for (Object val : (Collection) values) { if (val instanceof Attribute) { val = ((Attribute) val).getValue(); if (val instanceof Collection) { val = ((Collection) val).iterator().next(); } while (val instanceof Attribute) { val = ((Attribute) val).getValue(); } } if (isHRefLink) { // get the input features to avoid infinite loop in case the nested // feature type also have a reference back to this type // eg. gsml:GeologicUnit/gsml:occurence/gsml:MappedFeature // and gsml:MappedFeature/gsml:specification/gsml:GeologicUnit nestedFeatures.addAll( ((NestedAttributeMapping) attMapping) .getInputFeatures( this, val, getIdValues(source), source, reprojection, selectedProperties, includeMandatory)); } else { nestedFeatures.addAll( ((NestedAttributeMapping) attMapping) .getFeatures( this, val, getIdValues(source), reprojection, source, selectedProperties, includeMandatory)); } } values = nestedFeatures; } else if (isHRefLink) { // get the input features to avoid infinite loop in case the nested // feature type also have a reference back to this type // eg. gsml:GeologicUnit/gsml:occurence/gsml:MappedFeature // and gsml:MappedFeature/gsml:specification/gsml:GeologicUnit values = ((NestedAttributeMapping) attMapping) .getInputFeatures( this, values, getIdValues(source), source, reprojection, selectedProperties, includeMandatory); } else { values = ((NestedAttributeMapping) attMapping) .getFeatures( this, values, getIdValues(source), reprojection, source, selectedProperties, includeMandatory); } if (isHRefLink) { // only need to set the href link value, not the nested feature properties setXlinkReference(target, clientPropsMappings, values, xpath, targetNodeType); return null; } } Attribute instance = null; if (values instanceof Collection) { // nested feature type could have multiple instances as the whole purpose // of feature chaining is to cater for multi-valued properties for (Object singleVal : (Collection) values) { ArrayList valueList = new ArrayList(); // copy client properties from input features if they're complex features // wrapped in app-schema data access if (singleVal instanceof Attribute) { // copy client properties from input features if they're complex features // wrapped in app-schema data access Map<Name, Expression> valueProperties = getClientProperties((Attribute) singleVal); if (!valueProperties.isEmpty()) { clientPropsMappings.putAll(valueProperties); } } if (!isNestedFeature) { if (singleVal instanceof Attribute) { singleVal = ((Attribute) singleVal).getValue(); } if (singleVal instanceof Collection) { valueList.addAll((Collection) singleVal); } else { valueList.add(singleVal); } } else { valueList.add(singleVal); } instance = xpathAttributeBuilder.set( target, xpath, valueList, id, targetNodeType, false, sourceExpression); setClientProperties(instance, source, clientPropsMappings); } } else { if (values instanceof Attribute) { // copy client properties from input features if they're complex features // wrapped in app-schema data access Map<Name, Expression> newClientProps = getClientProperties((Attribute) values); if (!newClientProps.isEmpty()) { newClientProps.putAll(clientPropsMappings); clientPropsMappings = newClientProps; } values = ((Attribute) values).getValue(); } instance = xpathAttributeBuilder.set( target, xpath, values, id, targetNodeType, false, sourceExpression); setClientProperties(instance, source, clientPropsMappings); } if (instance != null && attMapping.encodeIfEmpty()) { instance.getDescriptor().getUserData().put("encodeIfEmpty", attMapping.encodeIfEmpty()); } return instance; }