use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class MovingFeature method storeGeometry.
/**
* Sets the geometry of the given attribute to the values collected by this {@code MovingFeatures}.
* This method sets also the {@code "datetimes"} characteristic.
*
* @param <G> the type of the geometry value.
* @param featureName the name of the feature containing the attribute to update, for logging purpose.
* @param index index of the property for which geometry value is desired.
* @param dimension number of dimensions for all coordinates.
* @param factory the factory to use for creating the geometry object.
* @param dest attribute where to store the geometry value.
* @param warningListener where to report warnings. Implementation should set the source class name,
* source method name and logger name, then forward to a {@code WarningListener}.
*/
public final <G> void storeGeometry(final String featureName, final int index, final int dimension, final Geometries<G> factory, final AbstractAttribute<G> dest, final Consumer<LogRecord> warningListener) {
int n = count[index];
final Vector[] vectors = new Vector[n];
for (Period p = properties[index]; p != null; p = p.previous) {
vectors[--n] = Vector.create(p.value, false);
}
if (n != 0) {
// Should never happen unless this object has been modified concurrently in another thread.
throw new CorruptedObjectException();
}
// Maximal number of warnings, for avoiding to flood the logger.
int warnings = 10;
// Total number of points in all vectors, ignoring null vectors.
int numPts = 0;
// If non-null, shall be non-empty.
Vector previous = null;
for (int i = 0; i < vectors.length; i++) {
Vector v = vectors[i];
int length;
if (v == null || (length = v.size()) == 0) {
continue;
}
if ((length % dimension) != 0) {
if (--warnings >= 0) {
Period p = properties[index];
for (int j = i; --j >= 0; ) {
// This is inefficient but used only in case of warnings.
p = p.previous;
}
warningListener.accept(Resources.forLocale(null).getLogRecord(Level.WARNING, Resources.Keys.UnexpectedNumberOfOrdinates_4, featureName, new Date(p.startTime), dimension, length));
}
continue;
}
/*
* At this point we have a non-empty valid sequence of ordinate values. If the first point of current
* vector is equals to the last point of previous vector, assume that they form a continuous polyline.
*/
if (previous != null) {
if (equals(previous, v, dimension)) {
// Skip the first coordinate.
v = v.subList(dimension, length);
length -= dimension;
if (length == 0) {
vectors[i] = null;
continue;
}
vectors[i] = v;
}
}
numPts += length;
previous = v;
}
/*
* At this point we got the list of all coordinates to join together in a polyline.
* We will create the geometry at the end of this method. Before that, interpolate
* the dates and times.
*/
int i = vectors.length;
numPts /= dimension;
final long[] times = new long[numPts];
for (Period p = properties[index]; p != null; p = p.previous) {
final Vector v = vectors[--i];
if (v != null) {
int c = v.size() / dimension;
if (c == 1) {
times[--numPts] = p.endTime;
} else {
final long startTime = p.startTime;
final double scale = (p.endTime - startTime) / (double) (c - 1);
while (--c >= 0) {
times[--numPts] = startTime + Math.round(scale * c);
}
}
}
}
if (numPts != 0) {
// Should never happen unless this object has been modified concurrently in another thread.
throw new CorruptedObjectException();
}
/*
* Store the geometry and characteristics in the attribute.
*/
dest.setValue(factory.createPolyline(dimension, vectors));
final AbstractAttribute<Instant> c = TIME.newInstance();
c.setValues(new DateList(times));
dest.characteristics().values().add(c);
}
use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class FeatureTypeBuilder method build.
/**
* Builds the feature type from the information and properties specified to this builder.
* One of the {@code setName(…)} methods must have been invoked before this {@code build()} method (mandatory).
* All other methods are optional, but some calls to a {@code add} method are usually needed.
*
* <div class="warning"><b>Warning:</b> In a future SIS version, the return type may be changed to the
* {@code org.opengis.feature.FeatureType} interface. This change is pending GeoAPI revision.</div>
*
* <p>If a feature type has already been built and this builder state has not changed since the
* feature type creation, then the previously created {@code FeatureType} instance is returned.</p>
*
* @return the feature type.
* @throws IllegalStateException if the builder contains inconsistent information.
*
* @see #clear()
*/
@Override
public DefaultFeatureType build() throws IllegalStateException {
if (feature == null) {
/*
* Creates an initial array of property types with up to 3 slots reserved for sis:identifier, sis:geometry
* and sis:envelope operations. At first we presume that there is always an identifier. The identifier slot
* will be removed later if there is none.
*/
// Number of explicitely specified properties.
final int numSpecified = properties.size();
// Number of synthetic properties that may be generated.
int numSynthetic;
int envelopeIndex = -1;
int geometryIndex = -1;
final AbstractIdentifiedType[] identifierTypes;
if (identifierCount == 0) {
numSynthetic = 0;
identifierTypes = null;
} else {
numSynthetic = 1;
identifierTypes = new AbstractIdentifiedType[identifierCount];
}
if (defaultGeometry != null) {
envelopeIndex = numSynthetic++;
if (!AttributeConvention.GEOMETRY_PROPERTY.equals(defaultGeometry.getName())) {
geometryIndex = numSynthetic++;
}
}
final AbstractIdentifiedType[] propertyTypes = new AbstractIdentifiedType[numSynthetic + numSpecified];
int propertyCursor = numSynthetic;
int identifierCursor = 0;
for (int i = 0; i < numSpecified; i++) {
final PropertyTypeBuilder builder = properties.get(i);
final AbstractIdentifiedType instance = builder.build();
propertyTypes[propertyCursor] = instance;
/*
* Collect the attributes to use as identifier components while we loop over all properties.
* A NullPointerException or an ArrayIndexOutOfBoundsException in this block would mean that
* identifierCount field has not been updated correctly by an addRole(AttributeRole) method.
*/
if (builder.isIdentifier()) {
identifierTypes[identifierCursor++] = instance;
}
/*
* If there is a default geometry, add a link named "sis:geometry" to that geometry.
* It may happen that the property created by the user is already named "sis:geometry",
* in which case we will avoid to duplicate the property.
*/
if (builder == defaultGeometry && geometryIndex >= 0) {
if (propertyTypes[geometryIndex] != null) {
/*
* Assuming that there is no bug in our implementation, this error could happen if the user
* has modified this FeatureTypeBuilder in another thread during this build() execution.
*/
throw new CorruptedObjectException();
}
propertyTypes[geometryIndex] = FeatureOperations.link(name(AttributeConvention.GEOMETRY_PROPERTY), instance);
}
propertyCursor++;
}
/*
* Create the "envelope" operation only after we created all other properties.
* Actually it is okay if the 'propertyTypes' array still contains null elements not needed for envelope calculation
* like "sis:identifier", since FeatureOperations.envelope(…) constructor ignores any property which is not for a value.
*/
if (envelopeIndex >= 0)
try {
propertyTypes[envelopeIndex] = FeatureOperations.envelope(name(AttributeConvention.ENVELOPE_PROPERTY), null, propertyTypes);
} catch (FactoryException e) {
throw new IllegalStateException(e);
}
/*
* If a synthetic identifier need to be created, create it now as the first property.
* It may happen that the user provided a single identifier component already named
* "sis:identifier", in which case we avoid to duplicate the property.
*/
if (identifierTypes != null) {
if (identifierCursor != identifierTypes.length) {
/*
* Assuming that there is no bug in our implementation, this error could happen if the user
* has modified this FeatureTypeBuilder in another thread during this build() execution.
*/
throw new CorruptedObjectException();
}
if (AttributeConvention.IDENTIFIER_PROPERTY.equals(identifierTypes[0].getName())) {
if (identifierCursor > 1) {
throw new IllegalStateException(Resources.format(Resources.Keys.PropertyAlreadyExists_2, getDisplayName(), AttributeConvention.IDENTIFIER_PROPERTY));
}
System.arraycopy(propertyTypes, 1, propertyTypes, 0, --propertyCursor);
} else {
propertyTypes[0] = FeatureOperations.compound(name(AttributeConvention.IDENTIFIER_PROPERTY), idDelimiter, idPrefix, idSuffix, identifierTypes);
}
}
feature = new DefaultFeatureType(identification(), isAbstract(), superTypes.toArray(new DefaultFeatureType[superTypes.size()]), ArraysExt.resize(propertyTypes, propertyCursor));
}
return feature;
}
use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class SparseFeature method setPropertyValue.
/**
* Sets the value for the property of the given name.
*
* @param name the attribute name.
* @param value the new value for the given attribute (may be {@code null}).
* @throws ClassCastException if the value is not assignable to the expected value class.
* @throws IllegalArgumentException if the given value can not be assigned for another reason.
*/
@Override
public void setPropertyValue(final String name, final Object value) throws IllegalArgumentException {
ArgumentChecks.ensureNonNull("name", name);
final Integer index = getIndex(name);
if (index < 0) {
setOperationValue(name, value);
return;
}
if (valuesKind == VALUES) {
final Object previous = properties.put(index, value);
/*
* Slight optimization: if we replaced a previous value of the same class, then we can skip the
* checks for name and type validity since those checks have been done previously. But if we add
* a new value or a value of a different type, then we need to check the name and type validity.
*/
if (!canSkipVerification(previous, value)) {
// This initial value will restore the previous value if the check fail.
Object toStore = previous;
try {
toStore = verifyPropertyValue(name, value);
} finally {
if (toStore != value) {
replace(index, value, toStore);
}
}
}
} else if (valuesKind == PROPERTIES) {
setPropertyValue(getPropertyInstance(name), value);
} else {
throw new CorruptedObjectException(getName());
}
}
use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class SparseFeature method getPropertyValue.
/**
* Returns the value for the property of the given name.
*
* @param name the property name.
* @return the value for the given property, or {@code null} if none.
* @throws IllegalArgumentException if the given argument is not an attribute or association name of this feature.
*/
@Override
public Object getPropertyValue(final String name) throws IllegalArgumentException {
ArgumentChecks.ensureNonNull("name", name);
final Integer index = getIndex(name);
if (index < 0) {
return getOperationValue(name);
}
final Object element = properties.get(index);
if (element != null) {
if (valuesKind == VALUES) {
// Most common case.
return element;
} else if (element instanceof AbstractAttribute<?>) {
return getAttributeValue((AbstractAttribute<?>) element);
} else if (element instanceof AbstractAssociation) {
return getAssociationValue((AbstractAssociation) element);
} else if (valuesKind == PROPERTIES) {
throw new IllegalArgumentException(unsupportedPropertyType(((Property) element).getName()));
} else {
throw new CorruptedObjectException(getName());
}
} else if (properties.containsKey(index)) {
// Null has been explicitely set.
return null;
} else {
return getDefaultValue(name);
}
}
use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class CC_OperationParameterGroup method merge.
/**
* Invoked by {@link DefaultParameterDescriptorGroup#setDescriptors(GeneralParameterDescriptor[])}
* for merging into a single set the descriptors which are repeated twice in a GML document.
*
* <p>The {@code descriptors} argument gives the descriptors listed explicitely inside a
* {@code <gml:OperationParameterGroup>} or {@code <gml:OperationMethod>} element. Those
* descriptors are said "incomplete" (from SIS point of view) because they are missing the
* {@link ParameterDescriptor#getValueClass()} property, which does not exist in GML but
* is mandatory for us. However an exception to this "incompleteness" happen when SIS has
* been able to match the {@code <gml:OperationMethod>} parent to one of the pre-defined
* operations in the {@link org.apache.sis.internal.referencing.provider} package.</p>
*
* <p>The {@code fromValues} argument gives the descriptors declared in each {@code <gml:ParameterValue>}
* instances of a {@code <gml:ParameterValueGroup>} or {@code <gml:AbstractSingleOperation>} element.
* Contrarily to the {@code descriptors} argument, the {@code fromValues} instances should have non-null
* {@link ParameterDescriptor#getValueClass()} property inferred by SIS from the parameter value.</p>
*
* <p>So the preferred descriptors from more complete to less complete are:</p>
* <ol>
* <li>{@code descriptors} if and only if they contain pre-defined parameters inferred by SIS from the {@code <gml:OperationMethod>} name.</li>
* <li>{@code fromValues}, which contain the descriptors declared in the {@code <gml:ParameterValue>} instances.</li>
* <li>{@code descriptors}, which contain the descriptor listed in {@code <gml:OperationParameterGroup>} or {@code <gml:OperationMethod>}.</li>
* </ol>
*
* <div class="note"><b>Note:</b>
* this code is defined in this {@code CC_OperationParameterGroup} class instead than in the
* {@link DefaultParameterDescriptorGroup} class in the hope to reduce the amount of code
* processed by the JVM in the common case where JAXB (un)marshalling is not needed.</div>
*
* @param descriptors the descriptors declared in the {@code ParameterDescriptorGroup}.
* @param fromValues the descriptors declared in the {@code ParameterValue} instances.
* They are said "valid" because they contain the mandatory {@code valueClass} property.
* @param replacements an {@code IdentityHashMap} where to store the replacements that the caller needs to
* apply in the {@code GeneralParameterValue} instances.
* @return a sequence containing the merged set of parameter descriptors.
*
* @see <a href="http://issues.apache.org/jira/browse/SIS-290">SIS-290</a>
*/
public static GeneralParameterDescriptor[] merge(final List<GeneralParameterDescriptor> descriptors, final GeneralParameterDescriptor[] fromValues, final Map<GeneralParameterDescriptor, GeneralParameterDescriptor> replacements) {
if (descriptors.isEmpty()) {
return fromValues;
}
final Map<String, GeneralParameterDescriptor> union = new LinkedHashMap<>(Containers.hashMapCapacity(descriptors.size()));
/*
* Collect the descriptors declared explicitely in the ParameterDescriptorGroup. We should never have
* two descriptors of the same name since the DefaultParameterDescriptorGroup constructor checked for
* name ambiguity. If a name collision is nevertheless detected, this would mean that a descriptor's
* name mutated.
*/
for (final GeneralParameterDescriptor p : descriptors) {
final String name = p.getName().getCode();
if (union.put(name, p) != null) {
throw new CorruptedObjectException(name);
}
}
/*
* Verify if any descriptors found in the ParameterValue instances could replace the descriptors in the group.
* We give precedence to the descriptors having a non-null 'valueClass' property, which normally appear in the
* 'fromValues' array.
*/
for (final GeneralParameterDescriptor valueDescriptor : fromValues) {
final String name = valueDescriptor.getName().getCode();
GeneralParameterDescriptor complete = valueDescriptor;
GeneralParameterDescriptor previous = union.put(name, complete);
if (previous != null) {
if (previous instanceof ParameterDescriptor<?>) {
verifyEquivalence(name, complete instanceof ParameterDescriptor<?>);
final Class<?> valueClass = ((ParameterDescriptor<?>) previous).getValueClass();
if (valueClass != null) {
/*
* This may happen if the 'descriptors' argument contain the parameters of a pre-defined
* method from the 'org.apache.sis.internal.referencing.provider' package instead than a
* descriptor from the GML file. In such case, presume that 'previous' is actually more
* complete than 'complete'.
*
* Note that 'r' should never be null unless JAXB unmarshalled the elements in reverse
* order (e.g. <gml:ParameterValue> before <gml:OperationMethod>). Since this behavior
* may depend on JAXB implementation, we are better to check for such case.
*/
final Class<?> r = ((ParameterDescriptor<?>) complete).getValueClass();
if (r != null) {
verifyEquivalence(name, valueClass == r);
}
// Restore the previous value in the map and swap 'previous' with 'replacement'.
previous = union.put(name, complete = previous);
}
} else if (previous instanceof ParameterDescriptorGroup) {
verifyEquivalence(name, complete instanceof ParameterDescriptorGroup);
}
/*
* Verify that the replacement contains at least all the information provided by the previous
* descriptor. The replacement is allowed to contain more information however.
*/
final GeneralParameterDescriptor replacement = CC_GeneralOperationParameter.merge(previous, complete);
if (replacement != valueDescriptor) {
union.put(name, replacement);
if (replacements.put(valueDescriptor, replacement) != null) {
// Should never happen, unless the parameter name changed during execution of this loop.
throw new CorruptedObjectException(name);
}
}
}
}
return union.values().toArray(new GeneralParameterDescriptor[union.size()]);
}
Aggregations