/**
   * 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());
 }
Exemple #7
0
/**
 * @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>
   *       &lt;strong&gt;wq_ir_results&lt;/strong&gt;			&lt;strong&gt;wq_plus&lt;/strong&gt;
   *        station_no		--&gt;	wq_plus@id
   *        sitename		--&gt;	sitename
   *        anzlic_no		--&gt;	anzlic_no
   *        project_no		--&gt;	project_no
   *        id		--&gt;	measurement/@id
   *        sample_collection_date--&gt; [not used]
   *        determinand_description--&gt;measurement/determinand_description
   *        results_value		--&gt;measurement/result
   *        location		--&gt;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;
  }