use of org.apache.sis.feature.builder.FeatureTypeBuilder in project sis by apache.
the class ConcatenatedFeatureSetTest method testCommonSuperType.
/**
* Tests the concatenation of two feature sets having different feature types.
*
* @throws DataStoreException if an error occurred while concatenating the feature sets.
*/
@Test
@DependsOnMethod("testSameType")
public void testCommonSuperType() throws DataStoreException {
/*
* First, we prepare two types sharing a common ancestor. We'll create two types using same properties,
* so we can ensure that all data is exposed upon traversal, not only data defined in the super type.
*/
final FeatureTypeBuilder builder = new FeatureTypeBuilder();
builder.addAttribute(Integer.class).setName("value");
builder.setName("parent");
final DefaultFeatureType superType = builder.build();
builder.clear();
builder.setSuperTypes(superType);
builder.addAttribute(String.class).setName("label");
final DefaultFeatureType t1 = builder.setName("t1").build();
final DefaultFeatureType t2 = builder.setName("t2").build();
// Populate a feature set for first type.
final AbstractFeature t1f1 = t1.newInstance();
t1f1.setPropertyValue("value", 2);
t1f1.setPropertyValue("label", "first-first");
final AbstractFeature t1f2 = t1.newInstance();
t1f2.setPropertyValue("value", 3);
t1f2.setPropertyValue("label", "first-second");
final FeatureSet t1fs = new MemoryFeatureSet(null, t1, Arrays.asList(t1f1, t1f2));
// Populate a feature set for second type.
final AbstractFeature t2f1 = t2.newInstance();
t2f1.setPropertyValue("value", 3);
t2f1.setPropertyValue("label", "second-first");
final AbstractFeature t2f2 = t2.newInstance();
t2f2.setPropertyValue("value", 4);
t2f2.setPropertyValue("label", "second-second");
final FeatureSet t2fs = new MemoryFeatureSet(null, t2, Arrays.asList(t2f1, t2f2));
/*
* First, we'll test that total sum of value property is coherent with initialized features.
* After that, we will ensure that we can get back the right labels for each subtype.
*/
final FeatureSet set = ConcatenatedFeatureSet.create(t1fs, t2fs);
final int sum = set.features(true).mapToInt(f -> (int) f.getPropertyValue("value")).sum();
assertEquals("Sum of feature `value` property", 12, sum);
final Object[] t1labels = set.features(false).filter(f -> t1.equals(f.getType())).map(f -> f.getPropertyValue("label")).toArray();
assertArrayEquals("First type labels", new String[] { "first-first", "first-second" }, t1labels);
final Object[] t2labels = set.features(false).filter(f -> t2.equals(f.getType())).map(f -> f.getPropertyValue("label")).toArray();
assertArrayEquals("First type labels", new String[] { "second-first", "second-second" }, t2labels);
}
use of org.apache.sis.feature.builder.FeatureTypeBuilder in project sis by apache.
the class ConcatenatedFeatureSetTest method noCommonType.
/**
* Tests the concatenation of two feature sets having no common parent.
* Creation of {@link ConcatenatedFeatureSet} is expected to fail.
*/
@Test
@DependsOnMethod("testCommonSuperType")
public void noCommonType() {
final FeatureTypeBuilder builder = new FeatureTypeBuilder();
builder.setName("super");
final DefaultFeatureType mockSuperType = builder.build();
final DefaultFeatureType firstType = builder.setSuperTypes(mockSuperType).setName("first").build();
final DefaultFeatureType secondType = builder.clear().setName("second").build();
final FeatureSet fs1 = new MemoryFeatureSet(null, firstType, Collections.emptyList());
final FeatureSet fs2 = new MemoryFeatureSet(null, secondType, Collections.emptyList());
try {
FeatureSet concatenation = ConcatenatedFeatureSet.create(fs1, fs2);
fail("Concatenation succeeded despite the lack of common type. Result is:\n" + concatenation);
} catch (DataStoreContentException e) {
// Expected behavior.
} catch (DataStoreException e) {
fail("Concatenation failed with an error reserved for other kind of error.");
}
}
use of org.apache.sis.feature.builder.FeatureTypeBuilder in project sis by apache.
the class FeatureQuery method expectedType.
/**
* Returns the type of values evaluated by this query when executed on features of the given type.
* If some expressions have no name, default names are computed as below:
*
* <ul>
* <li>If the expression is an instance of {@link ValueReference}, the name of the
* property referenced by the {@linkplain ValueReference#getXPath() x-path}.</li>
* <li>Otherwise the localized string "Unnamed #1" with increasing numbers.</li>
* </ul>
*
* @param valueType the type of features to be evaluated by the expressions in this query.
* @return type resulting from expressions evaluation (never null).
* @throws IllegalArgumentException if this method can operate only on some feature types
* and the given type is not one of them.
* @throws IllegalArgumentException if this method can not determine the result type of an expression
* in this query. It may be because that expression is backed by an unsupported implementation.
*/
final DefaultFeatureType expectedType(final DefaultFeatureType valueType) {
if (projection == null) {
// All columns included: result is of the same type.
return valueType;
}
// Sequential number for unnamed expressions.
int unnamedNumber = 0;
// Names already used, for avoiding collisions.
Set<String> names = null;
final FeatureTypeBuilder ftb = new FeatureTypeBuilder().setName(valueType.getName());
for (int column = 0; column < projection.length; column++) {
/*
* For each property, get the expected type (mandatory) and its name (optional).
* A default name will be computed if no alias were explicitly given by user.
*/
GenericName name = projection[column].alias;
final Expression<?, ?> expression = projection[column].expression;
final FeatureExpression<?, ?> fex = FeatureExpression.castOrCopy(expression);
final PropertyTypeBuilder resultType;
if (fex == null || (resultType = fex.expectedType(valueType, ftb)) == null) {
throw new IllegalArgumentException(Resources.format(Resources.Keys.InvalidExpression_2, expression.getFunctionName().toInternationalString(), column));
}
if (name == null) {
/*
* Build a list of aliases declared by the user, for making sure that we do not collide with them.
* No check for `GenericName` collision here because it was already verified by `setProjection(…)`.
* We may have collision of their `String` representations however, which is okay.
*/
if (names == null) {
names = new HashSet<>(Containers.hashMapCapacity(projection.length));
for (final NamedExpression p : projection) {
if (p.alias != null) {
names.add(p.alias.toString());
}
}
}
/*
* If the expression is a `ValueReference`, the `PropertyType` instance can be taken directly
* from the source feature (the Apache SIS implementation does just that). If the name is set,
* then we assume that it is correct. Otherwise we take the tip of the XPath.
*/
CharSequence text = null;
if (expression instanceof ValueReference<?, ?>) {
final GenericName current = resultType.getName();
if (current != null && names.add(current.toString())) {
continue;
}
String xpath = ((ValueReference<?, ?>) expression).getXPath().trim();
// Works also if '/' is not found.
xpath = xpath.substring(xpath.lastIndexOf('/') + 1);
if (!(xpath.isEmpty() || names.contains(xpath))) {
text = xpath;
}
}
/*
* If we still have no name at this point, create a name like "Unnamed #1".
* Note that despite the use of `Vocabulary` resources, the name will be unlocalized
* (for easier programmatic use) because `GenericName` implementation is designed for
* providing localized names only if explicitly requested.
*/
if (text == null)
do {
text = Vocabulary.formatInternational(Vocabulary.Keys.Unnamed_1, ++unnamedNumber);
} while (!names.add(text.toString()));
name = Names.createLocalName(null, null, text);
}
resultType.setName(name);
}
return ftb.build();
}
Aggregations