public void skipNestedMapping(AttributeMapping attMapping, List<Feature> sources) throws IOException { if (attMapping instanceof JoiningNestedAttributeMapping) { for (Feature source : sources) { Object value = getValues(attMapping.isMultiValued(), attMapping.getSourceExpression(), source); if (value instanceof Collection) { for (Object val : (Collection) value) { ((JoiningNestedAttributeMapping) attMapping).skip(this, val, getIdValues(source)); } } else { ((JoiningNestedAttributeMapping) attMapping).skip(this, value, getIdValues(source)); } } } }
protected Feature computeNext() throws IOException { String id = getNextFeatureId(); List<Feature> sources = getSources(id); final Name targetNodeName = targetFeature.getName(); AttributeBuilder builder = new AttributeBuilder(attf); builder.setDescriptor(targetFeature); Feature target = (Feature) builder.build(id); for (AttributeMapping attMapping : selectedMapping) { try { if (skipTopElement(targetNodeName, attMapping, targetFeature.getType())) { // ignore the top level mapping for the Feature itself // as it was already set continue; } if (attMapping.isList()) { Attribute instance = setAttributeValue( target, null, sources.get(0), attMapping, null, null, selectedProperties.get(attMapping)); if (sources.size() > 1 && instance != null) { List<Object> values = new ArrayList<Object>(); Expression sourceExpr = attMapping.getSourceExpression(); for (Feature source : sources) { values.add(getValue(sourceExpr, source)); } String valueString = StringUtils.join(values.iterator(), " "); StepList fullPath = attMapping.getTargetXPath(); StepList leafPath = fullPath.subList(fullPath.size() - 1, fullPath.size()); if (instance instanceof ComplexAttributeImpl) { // xpath builder will work out the leaf attribute to set values on xpathAttributeBuilder.set( instance, leafPath, valueString, null, null, false, sourceExpr); } else { // simple attributes instance.setValue(valueString); } } } else if (attMapping.isMultiValued()) { // extract the values from multiple source features of the same id // and set them to one built feature for (Feature source : sources) { setAttributeValue( target, null, source, attMapping, null, null, selectedProperties.get(attMapping)); } } else { String indexString = attMapping.getSourceIndex(); // if not specified, get the first row by default int index = 0; if (indexString != null) { if (ComplexFeatureConstants.LAST_INDEX.equals(indexString)) { index = sources.size() - 1; } else { index = Integer.parseInt(indexString); } } setAttributeValue( target, null, sources.get(index), attMapping, null, null, selectedProperties.get(attMapping)); // When a feature is not multi-valued but still has multiple rows with the same ID in // a denormalised table, by default app-schema only takes the first row and ignores // the rest (see above). The following line is to make sure that the cursors in the // 'joining nested mappings'skip any extra rows that were linked to those rows that are // being ignored. // Otherwise the cursor will stay there in the wrong spot and none of the following // feature chaining // will work. That can really only occur if the foreign key is not unique for the ID of // the parent // feature (otherwise all of those rows would be already passed when creating the feature // based on // the first row). This never really occurs in practice I have noticed, but it is a // theoretic // possibility, as there is no requirement for the foreign key to be unique per id. skipNestedMapping(attMapping, sources.subList(1, sources.size())); } } catch (Exception e) { throw new RuntimeException( "Error applying mapping with targetAttribute " + attMapping.getTargetXPath(), e); } } cleanEmptyElements(target); return target; }
/** * 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; }