/** * Return all matching properties from provided root attribute and xPath. * * @param root The root attribute to start searching from * @param xpath The xPath matching the attribute * @return The matching attributes collection */ private Collection<Property> getProperties(ComplexAttribute root, StepList xpath) { final StepList steps = new StepList(xpath); Iterator<Step> stepsIterator = steps.iterator(); Collection<Property> properties = null; Step step = null; if (stepsIterator.hasNext()) { step = stepsIterator.next(); properties = ((ComplexAttribute) root).getProperties(Types.toTypeName(step.getName())); } while (stepsIterator.hasNext()) { step = stepsIterator.next(); Collection<Property> nestedProperties = new ArrayList<Property>(); for (Property property : properties) { assert property instanceof ComplexAttribute; Collection<Property> tempProperties = ((ComplexAttribute) property).getProperties(Types.toTypeName(step.getName())); if (!tempProperties.isEmpty()) { nestedProperties.addAll(tempProperties); } } properties.clear(); if (nestedProperties.isEmpty()) { return properties; } properties.addAll(nestedProperties); } return properties; }
/** * Implementation for tests of fid -> fid unmapping. * * @param idExpression * @throws Exception */ private void checkUnrollIdExpression(Expression idExpression) throws Exception { AttributeMapping featureMapping = null; Name featurePath = mapping.getTargetFeature().getName(); QName featureName = Types.toQName(featurePath); for (Iterator it = mapping.getAttributeMappings().iterator(); it.hasNext(); ) { AttributeMapping attMapping = (AttributeMapping) it.next(); StepList targetXPath = attMapping.getTargetXPath(); if (targetXPath.size() > 1) { continue; } Step step = (Step) targetXPath.get(0); if (featureName.equals(step.getName())) { featureMapping = attMapping; break; } } featureMapping.setIdentifierExpression(idExpression); this.visitor = new UnmappingFilterVisitor(this.mapping); // retrieve a single sample feature Feature sourceFeature = DataUtilities.first(mapping.getSource().getFeatures()); String fid = sourceFeature.getIdentifier().toString(); Id fidFilter = ff.id(Collections.singleton(ff.featureId(fid))); Filter unrolled = (Filter) fidFilter.accept(visitor, null); assertNotNull(unrolled); assertTrue(unrolled instanceof Id); FeatureCollection<SimpleFeatureType, SimpleFeature> results = mapping.getSource().getFeatures(unrolled); assertEquals(1, getCount(results)); SimpleFeature unmappedFeature = DataUtilities.first(results); assertEquals(fid, unmappedFeature.getID()); }
/** * This is just a compilation of constants used in app-schema module. * * @author Rini Angreani, CSIRO Earth Science and Resource Engineering */ public class ComplexFeatureConstants { /** Static attribute name used to link different feature types. */ public static final Name FEATURE_CHAINING_LINK_NAME = new NameImpl("FEATURE_LINK"); /** * Static attribute descriptor used to link different feature types. This attribute won't appear * in the output document as it doesn't exist in the schema. Specifying the index would allow more * than one instances to be used in one type that can be chained by different parent feature * types. */ public static final PropertyDescriptor FEATURE_CHAINING_LINK = new AttributeDescriptorImpl( XSSchema.STRING_TYPE, FEATURE_CHAINING_LINK_NAME, 0, -1, true, null); /** Name representation of xlink:href */ public static final Name XLINK_HREF_NAME = Types.toTypeName(XLINK.HREF); /** Definition of xs:anyType as a complex type to get their client properties encoded. */ @SuppressWarnings("unchecked") public static final ComplexType ANYTYPE_TYPE = new ComplexTypeImpl( new NameImpl("http://www.w3.org/2001/XMLSchema", "anyType"), null, false, true, Collections.EMPTY_LIST, null, null); /** Hints key for xlink:href used in ToXlinkHrefFunction */ public static final Hints.Key STRING_KEY = new Hints.Key(String.class); }
/** * Find the source expression if the step is a client property. * * @param nextRootStep the step * @param fMapping feature type mapping to get namespaces from * @param mapping attribute mapping * @param targetXPath the full target xpath * @return */ private Expression getClientPropertyExpression(AttributeMapping mapping, Step lastStep) { if (lastStep.isXmlAttribute()) { Map<Name, Expression> clientProperties = mapping.getClientProperties(); QName lastStepQName = lastStep.getName(); Name lastStepName; if (lastStepQName.getPrefix() != null && lastStepQName.getPrefix().length() > 0 && (lastStepQName.getNamespaceURI() == null || lastStepQName.getNamespaceURI().length() == 0)) { String prefix = lastStepQName.getPrefix(); String uri = namespaceSupport.getURI(prefix); lastStepName = Types.typeName(uri, lastStepQName.getLocalPart()); } else { lastStepName = Types.toTypeName(lastStepQName); } if (clientProperties.containsKey(lastStepName)) { // end NC - added return (Expression) clientProperties.get(lastStepName); } } return null; }
/** * Returns first matching attribute from provided root and xPath. * * @param root The root attribute to start searching from * @param xpath The xPath matching the attribute * @return The first matching attribute */ private Property getProperty(Attribute root, StepList xpath) { Property property = root; final StepList steps = new StepList(xpath); Iterator<Step> stepsIterator = steps.iterator(); while (stepsIterator.hasNext()) { assert property instanceof ComplexAttribute; Step step = stepsIterator.next(); property = ((ComplexAttribute) property).getProperty(Types.toTypeName(step.getName())); if (property == null) { return null; } } return property; }
private static boolean isSimpleType(FeatureTypeMapping mapping) { return Types.isSimpleContentType(mapping.getTargetFeature().getType()); }
/** * @author Gabriel Roldan, Axios Engineering * @version $Id$ * @source $URL$ * @since 2.4 */ public class TestData { public static final Name WATERSAMPLE_TYPENAME = Types.typeName("wq_ir_results"); private TestData() {} /** * Complex type: * * <ul> * wq_plus * <li>sitename * <li>anzlic_no * <li>project_no * <li>measurement (0..*) * <ul> * <li>determinand_description * <li>result * </ul> * <li>location * </ul> * * @return */ public static FeatureType createComplexWaterQualityType() { FeatureTypeFactory tfac = new UniqueNameFeatureTypeFactoryImpl(); TypeBuilder builder = new TypeBuilder(tfac); FeatureType wq_plusType; AttributeType detdesc = builder.name("determinand_description").bind(String.class).attribute(); AttributeType result = builder.name("result").bind(Float.class).attribute(); builder.setName("measurement"); builder.addAttribute("determinand_description", detdesc); builder.addAttribute("result", result); ComplexType MEASUREMENT = builder.complex(); /* * <li>sitename <li>anzlic_no <li>project_no <li>location <li>measurement * (0..*) <ul> <li>determinand_description</li> <li>result</li> * </ul> */ AttributeType sitename = builder.name("sitename").bind(String.class).attribute(); AttributeType anzlic_no = builder.name("anzlic_no").bind(String.class).attribute(); AttributeType project_no = builder.name("project_no").bind(String.class).attribute(); AttributeType location = builder.name("location").bind(Point.class).geometry(); builder.setName("wq_plus"); builder.addAttribute("sitename", sitename); builder.addAttribute("anzlic_no", anzlic_no); builder.addAttribute("project_no", project_no); builder.cardinality(0, Integer.MAX_VALUE); builder.addAttribute("measurement", MEASUREMENT); builder.cardinality(1, 1); builder.addAttribute("location", location); wq_plusType = builder.feature(); return wq_plusType; } public static FeatureType createComplexWaterSampleType() { FeatureTypeFactory tfac = new UniqueNameFeatureTypeFactoryImpl(); TypeBuilder builder = new TypeBuilder(tfac); FeatureType sampleType; AttributeType parameter = builder.name("parameter").bind(String.class).attribute(); AttributeType value = builder.name("value").bind(Double.class).attribute(); builder.setName("measurement"); builder.addAttribute("parameter", parameter); builder.addAttribute("value", value); ComplexType MEASUREMENT = builder.complex(); builder.setName("sample"); builder.cardinality(0, Integer.MAX_VALUE); builder.addAttribute("measurement", MEASUREMENT); sampleType = builder.feature(); return sampleType; } /** * * * <pre> * </pre> * * @param targetFeature * @return * @throws Exception */ public static List /* <AttributeMapping> */ createMappingsColumnsAndValues( AttributeDescriptor targetFeature) throws Exception { List mappings = new LinkedList(); AttributeMapping attMapping; Expression source; String target; FilterFactory ff = CommonFactoryFinder.getFilterFactory(null); source = ff.literal("ph"); target = "sample/measurement[1]/parameter"; // empty nssupport as the test properties have no namespace NamespaceSupport namespaces = new NamespaceSupport(); attMapping = new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces)); mappings.add(attMapping); source = ff.property("ph"); target = "sample/measurement[1]/value"; attMapping = new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces)); mappings.add(attMapping); source = ff.literal("temp"); target = "sample/measurement[2]/parameter"; attMapping = new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces)); mappings.add(attMapping); source = ff.property("temp"); target = "sample/measurement[2]/value"; attMapping = new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces)); mappings.add(attMapping); source = ff.literal("turbidity"); target = "sample/measurement[3]/parameter"; attMapping = new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces)); mappings.add(attMapping); source = ff.property("turbidity"); target = "sample/measurement[3]/value"; attMapping = new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces)); mappings.add(attMapping); return mappings; } /** * Flat FeatureType: * * <ul> * wq_ir_results * <li>station_no * <li>sitename * <li>anzlic_no * <li>project_no * <li>id * <li>sample_collection_date * <li>determinand_description * <li>results_value * <li>location * </ul> * * <p>Complex type: * * <ul> * wq_plus * <li>sitename * <li>anzlic_no * <li>project_no * <li>location * <li>measurement (0..*) * <ul> * <li>determinand_description * <li>result * </ul> * </ul> * * <p>Mappings definition: * * <pre> * <strong>wq_ir_results</strong> <strong>wq_plus</strong> * station_no --> wq_plus@id * sitename --> sitename * anzlic_no --> anzlic_no * project_no --> project_no * id --> measurement/@id * sample_collection_date--> [not used] * determinand_description-->measurement/determinand_description * results_value -->measurement/result * location -->location * </pre> * * @param simpleStore * @return * @throws Exception */ public static FeatureTypeMapping createMappingsGroupByStation(MemoryDataStore simpleStore) throws Exception { Name sourceTypeName = WATERSAMPLE_TYPENAME; final SimpleFeatureSource wsSource = simpleStore.getFeatureSource(sourceTypeName); FeatureType targetType = createComplexWaterQualityType(); FeatureTypeFactory tf = new UniqueNameFeatureTypeFactoryImpl(); AttributeDescriptor targetFeature = tf.createAttributeDescriptor( targetType, targetType.getName(), 0, Integer.MAX_VALUE, true, null); List mappings = new LinkedList(); Expression id; Expression source; String target; FilterFactory ff = CommonFactoryFinder.getFilterFactory(null); id = ff.property("station_no"); source = Expression.NIL; target = "wq_plus"; NamespaceSupport namespaces = new NamespaceSupport(); mappings.add(new AttributeMapping(id, source, XPath.steps(targetFeature, target, namespaces))); source = ff.property("sitename"); target = "wq_plus/sitename"; mappings.add( new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces))); source = ff.property("anzlic_no"); target = "wq_plus/anzlic_no"; mappings.add( new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces))); source = ff.property("project_no"); target = "wq_plus/project_no"; mappings.add( new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces))); id = ff.property("id[1]"); source = null; target = "wq_plus/measurement"; mappings.add( new AttributeMapping( id, source, XPath.steps(targetFeature, target, namespaces), null, true, null)); source = ff.property("determinand_description"); target = "wq_plus/measurement/determinand_description"; mappings.add( new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces))); source = ff.property("results_value"); target = "wq_plus/measurement/result"; mappings.add( new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces))); source = ff.property("location"); target = "wq_plus/location"; mappings.add( new AttributeMapping(null, source, XPath.steps(targetFeature, target, namespaces))); return new FeatureTypeMapping(wsSource, targetFeature, mappings, namespaces); } /** * Creates a flat FeatureType <code>wq_ir_results</code> with a structure like the following, from * which a complex one should be constructed grouping by station_no attribute. * * <p>Following this sample schema, a total of 10 unique station_no identifiers will be created, * and for each one, a total of N desagregate rows with the same station_no, where N goes from 1 * to 10. So for the first station_no there will be just one occurrence and the last one will have * 10. * * <p> * * <table> * <tr> * <td> station_no (string) </td> * <td> sitename (string)</td> * <td> anzlic_no (string)</td> * <td> project_no (string)</td> * <td> id (string)</td> * <td> sample_collection_date (string)</td> * <td> determinand_description (string)</td> * <td> results_value (float)</td> * <td> location (Point)</td> * </tr> * <tr> * <td> station_1 </td> * <td> sitename_1 </td> * <td> anzlic_no_1 </td> * <td> project_no_1 </td> * <td> id_1_1 </td> * <td> sample_collection_date_1_1 </td> * <td> determinand_description_1_1 </td> * <td> 1.1 </td> * <td> POINT(1, 1) </td> * </tr> * <tr> * <td> station_2 </td> * <td> sitename_2 </td> * <td> anzlic_no_2 </td> * <td> project_no_2 </td> * <td> id_2_1 </td> * <td> sample_collection_date_2_1 </td> * <td> determinand_description_2_1 </td> * <td> 2.1 </td> * <td> POINT(2, 2) </td> * </tr> * <tr> * <td> station_2 </td> * <td> sitename_2 </td> * <td> anzlic_no_2 </td> * <td> project_no_2 </td> * <td> id_2_2 </td> * <td> sample_collection_date_2_2 </td> * <td> determinand_description_2_2 </td> * <td> 2.2 </td> * <td> POINT(2, 2) </td> * </tr> * <tr> * <td colspan="9">...</td> * </tr> * <tr> * <td> station_10 </td> * <td> sitename_10 </td> * <td> anzlic_no_10 </td> * <td> project_no_10 </td> * <td> id_10_10 </td> * <td> sample_collection_date_10_9 </td> * <td> determinand_description_10_9 </td> * <td> 10.10 </td> * <td> POINT(10, 10) </td> * </tr> * <tr> * <td> station_10 </td> * <td> sitename_10 </td> * <td> anzlic_no_10 </td> * <td> project_no_10 </td> * <td> id_10_10 </td> * <td> sample_collection_date_10_10 </td> * <td> determinand_description_10_10 </td> * <td> 10.10 </td> * <td> POINT(10, 10) </td> * </tr> * </table> * * @return * @throws Exception */ public static MemoryDataStore createDenormalizedWaterQualityResults() throws Exception { MemoryDataStore dataStore = new MemoryDataStore(); SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.setName(TestData.WATERSAMPLE_TYPENAME.getLocalPart()); builder.add("station_no", String.class); builder.add("sitename", String.class); builder.add("anzlic_no", String.class); builder.add("project_no", String.class); builder.add("id", String.class); builder.add("sample_collection_date", String.class); builder.add("determinand_description", String.class); builder.add("results_value", Float.class); builder.add("location", Point.class); SimpleFeatureType type = builder.buildFeatureType(); dataStore.createSchema(type); final int NUM_STATIONS = 10; GeometryFactory gf = new GeometryFactory(); SimpleFeatureBuilder fb = new SimpleFeatureBuilder(type); for (int groupValue = 1; groupValue <= NUM_STATIONS; groupValue++) { for (int measurement = 1; measurement <= groupValue; measurement++) { String fid = type.getName().getLocalPart() + "." + groupValue + "." + measurement; fb.add("station_no." + groupValue); fb.add("sitename" + groupValue); fb.add("anzlic_no" + groupValue); fb.add("project_no" + groupValue); String sufix = "_" + groupValue + "_" + measurement; fb.add("id" + sufix); fb.add("sample_collection_date" + sufix); fb.add("determinand_description" + sufix); fb.add(new Float(groupValue + "." + measurement)); fb.add(gf.createPoint(new Coordinate(groupValue, groupValue))); SimpleFeature f = fb.buildFeature(fid); dataStore.addFeature(f); } } return dataStore; } }
private List<Object> getValues( int startIndex, int endIndex, List<Feature> roots, FeatureTypeMapping fMapping, AttributeMapping prevMapping) { List<Object> values = new ArrayList<Object>(); if (startIndex > fullSteps.size() || endIndex > fullSteps.size()) { return values; } while (startIndex <= endIndex) { List<AttributeMapping> attMappings = new ArrayList<AttributeMapping>(); StepList steps = null; if (isLastStep(endIndex)) { // exhausted all paths return values; } while (attMappings.isEmpty() && endIndex < fullSteps.size()) { endIndex++; steps = fullSteps.subList(startIndex, endIndex); attMappings = fMapping.getAttributeMappingsIgnoreIndex(steps); if (steps.size() == 1) { if (Types.equals(fMapping.getTargetFeature().getName(), steps.get(0).getName()) && !(Types.isSimpleContentType(fMapping.getTargetFeature().getType()))) { // skip element type name, but not when it's a simple content // like gml:name because it wouldn't have the element type name in the xpath startIndex++; endIndex = startIndex; steps = fullSteps.subList(startIndex, endIndex); attMappings = fMapping.getAttributeMappingsIgnoreIndex(steps); continue; } else if (attMappings.isEmpty() && steps.get(0).isId()) { // sometimes there's no explicit attribute mapping for top element name // but id should still resolve to primary key by default // e.g. gsml:GeologicUnit/@gml:id should resolve even though there's no // AttributeMapping for gsml:GeologicUnit setIdValues(null, roots, values); return values; } } } if (attMappings.isEmpty()) { // not found here, but might be found in other nodes if multi-valued // and polymorphic continue; } for (AttributeMapping mapping : attMappings) { if (mapping instanceof NestedAttributeMapping) { if (isClientProperty(endIndex)) { // check for client properties boolean isNestedXlinkHref = isXlinkHref(mapping); boolean valueFound = false; if (!isNestedXlinkHref) { // check if client properties are set in the parent attributeMapping in root mapping // file valueFound = getClientProperties(mapping, values, roots); } if (!valueFound) { // or if they're set in the attributeMapping in feature chained mapping file getNestedClientProperties( (NestedAttributeMapping) mapping, roots, values, isNestedXlinkHref); } } else { boolean isSimpleContent = Types.isSimpleContent(steps, fMapping.getTargetFeature().getType()); // if simple content, then it doesn't need to increment the next starting // index // since there will be no type name in the xpath, e.g. when gml:name is // feature // chained // the path stays as gml:name.. but if it's a complex type with complex // content, // e.g. gsml:specification // the path will be gsml:specification/gsml:GeologicUnit/<some leaf // attribute to // filter by> getNestedValues( (NestedAttributeMapping) mapping, roots, values, isSimpleContent ? startIndex : startIndex + 1); } } else { // normal attribute mapping if (endIndex == fullSteps.size()) { Expression exp = mapping.getSourceExpression(); for (Feature f : roots) { Object value = getValue(exp, f); if (value != null) { values.add(value); } } } else if (isClientProperty(endIndex)) { // peek at the next attribute to check for client properties if (getLastStep().isId()) { setIdValues(mapping, roots, values); } else { getClientProperties(mapping, values, roots); } } else { // increment the xpath List<Object> nestedValues = getValues(startIndex, endIndex, roots, fMapping, mapping); if (nestedValues != null) { values.addAll(nestedValues); } } } } return values; } return values; }