use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class SparseFeature method clone.
/**
* Returns a copy of this feature
* This method clones also all {@linkplain Cloneable cloneable} property instances in this feature,
* but not necessarily property values. Whether the property values are cloned or not (i.e. whether
* the clone operation is <cite>deep</cite> or <cite>shallow</cite>) depends on the behavior or
* property {@code clone()} methods.
*
* @return a clone of this attribute.
* @throws CloneNotSupportedException if this feature can not be cloned, typically because
* {@code clone()} on a property instance failed.
*/
@Override
@SuppressWarnings("unchecked")
public SparseFeature clone() throws CloneNotSupportedException {
final SparseFeature clone = (SparseFeature) super.clone();
clone.properties = (HashMap<Integer, Object>) clone.properties.clone();
switch(clone.valuesKind) {
default:
throw new AssertionError(clone.valuesKind);
case CORRUPTED:
throw new CorruptedObjectException(clone.getName());
// Nothing to do.
case VALUES:
break;
case PROPERTIES:
{
final Cloner cloner = new Cloner();
for (final Map.Entry<Integer, Object> entry : clone.properties.entrySet()) {
final Property property = (Property) entry.getValue();
if (property instanceof Cloneable) {
entry.setValue(cloner.clone(property));
}
}
break;
}
}
return clone;
}
use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class ModifiableLocationType method removeParent.
/**
* Removes the given element from the list of parent.
*
* @param parent the parent to remove.
* @throws IllegalArgumentException if the given parent has not been found.
*/
public void removeParent(final ModifiableLocationType parent) {
ArgumentChecks.ensureNonNull("parent", parent);
final String key = parent.name.toString();
final ModifiableLocationType removed = parents.remove(key);
if (removed == null) {
throw new IllegalArgumentException(Resources.format(Resources.Keys.LocationTypeNotFound_1, key));
}
if (removed.children.remove(name.toString()) != this || removed != parent) {
throw new CorruptedObjectException();
}
}
use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class CC_GeneralOperationParameter method merge.
/**
* Returns a descriptor with the given properties, completed with information not found in GML.
* Those extra information are given by the {@code complete} descriptor.
*
* @param caller the public source class to report if a log message need to be emitted.
* @param properties properties as declared in the GML document, to be used if {@code complete} is incompatible.
* @param merged more complete properties, to be used if {@code complete} is compatible.
* @param minimumOccurs value to assign to {@link DefaultParameterDescriptorGroup#getMinimumOccurs()}.
* @param maximumOccurs value to assign to {@link DefaultParameterDescriptorGroup#getMaximumOccurs()}.
* @param provided parameter descriptors declared in the GML document. This array will be overwritten.
* @param complete more complete parameter descriptors.
* @param canSubstitute {@code true} if this method is allowed to return {@code complete}.
* @return the parameter descriptor group to use (may be the {@code complete} instance).
*
* @see <a href="http://issues.apache.org/jira/browse/SIS-290">SIS-290</a>
*/
static ParameterDescriptorGroup merge(final Class<?> caller, final Map<String, ?> properties, final Map<String, ?> merged, final int minimumOccurs, final int maximumOccurs, final GeneralParameterDescriptor[] provided, final ParameterDescriptorGroup complete, boolean canSubstitute) {
boolean isCompatible = true;
final Set<GeneralParameterDescriptor> included = new HashSet<>(Containers.hashMapCapacity(provided.length));
for (int i = 0; i < provided.length; i++) {
final GeneralParameterDescriptor p = provided[i];
try {
/*
* Replace the descriptors provided in the GML document by descriptors from the 'complete' instance,
* if possible. Keep trace of the complete descriptors that we found in this process.
*/
GeneralParameterDescriptor predefined = complete.descriptor(p.getName().getCode());
if (predefined != null) {
// Safety in case 'complete' is a user's implementation.
canSubstitute &= (provided[i] = merge(p, predefined)) == predefined;
if (!included.add(predefined)) {
// Broken hashCode/equals, or object mutated.
throw new CorruptedObjectException(predefined);
}
continue;
}
} catch (ParameterNotFoundException e) {
/*
* Log at Level.WARNING for the first parameter (canSubstitute == true) and at Level.FINE
* for all other (canSubstitute == false). We do not use CC_GeneralOperationParameter as
* the source class because this is an internal class. We rather use the first public class
* in the caller hierarchy, which is either DefaultParameterValueGroup or DefaultOperationMethod.
*/
Context.warningOccured(Context.current(), caller, (caller == DefaultParameterValueGroup.class) ? "setValues" : "setDescriptors", e, canSubstitute);
}
/*
* If a parameter was not found in the 'complete' descriptor, we will not be able to use that descriptor.
* But we may still be able to use its properties (name, alias, identifier) provided that the parameter
* not found was optional.
*/
isCompatible &= p.getMinimumOccurs() == 0;
canSubstitute = false;
}
if (isCompatible) {
/*
* At this point, we determined that all mandatory parameters in the GML document exist in the 'complete'
* descriptor. However the converse is not necessarily true. Verify that all parameters missing in the GML
* document were optional.
*/
for (final GeneralParameterDescriptor descriptor : complete.descriptors()) {
if (!included.contains(descriptor) && descriptor.getMinimumOccurs() != 0 && !CC_OperationMethod.isImplicitParameter(descriptor)) {
canSubstitute = false;
isCompatible = false;
break;
}
}
}
if (canSubstitute) {
return complete;
} else {
return new DefaultParameterDescriptorGroup(isCompatible ? merged : properties, minimumOccurs, maximumOccurs, provided);
}
}
use of org.apache.sis.util.CorruptedObjectException in project sis by apache.
the class Merger method copy.
/**
* Implementation of {@link #copy(Object, ModifiableMetadata)} method,
* to be invoked recursively for all child properties to merge.
*
* @param dryRun {@code true} for executing the merge operation in "dry run" mode instead than performing the
* actual merge. This mode is used for verifying if there is a merge conflict before to perform
* the actual operation.
* @return {@code true} if the merge operation is valid, or {@code false} if the given arguments are valid
* metadata but the merge operation can nevertheless not be executed because it could cause data lost.
*/
@SuppressWarnings("fallthrough")
private boolean copy(final Object source, final ModifiableMetadata target, final boolean dryRun) {
/*
* Verify if the given source can be merged with the target. If this is not the case, action
* taken will depend on the caller: it may either skips the value or throws an exception.
*/
final MetadataStandard standard = target.getStandard();
if (!standard.getInterface(source.getClass()).isInstance(target)) {
return false;
}
/*
* Only after we verified that the merge operation is theoretically allowed, remember that
* we are going to merge those two metadata and verify that we are not in an infinite loop.
* We will also verify that the target metadata does not contain a source, or vis-versa.
*/
{
// For keeping 'sourceDone' and 'targetDone' more local.
final Boolean sourceDone = done.put(source, Boolean.FALSE);
final Boolean targetDone = done.put(target, Boolean.TRUE);
if (sourceDone != null || targetDone != null) {
if (Boolean.FALSE.equals(sourceDone) && Boolean.TRUE.equals(targetDone)) {
/*
* At least, the 'source' and 'target' status are consistent. Pretend that we have already
* merged those metadata since actually the merge operation is probably underway by the caller.
*/
return true;
} else {
throw new IllegalArgumentException(errors().getString(Errors.Keys.CrossReferencesNotSupported));
}
}
}
/*
* Get views of metadata as maps. Those maps are live: write operations
* on those maps will be reflected on the metadata objects and conversely.
*/
final Map<String, Object> targetMap = target.asMap();
final Map<String, Object> sourceMap;
if (source instanceof AbstractMetadata) {
// Gives to subclasses a chance to override.
sourceMap = ((AbstractMetadata) source).asMap();
} else {
sourceMap = standard.asValueMap(source, null, KeyNamePolicy.JAVABEANS_PROPERTY, ValueExistencePolicy.NON_EMPTY);
}
/*
* Iterate on source values in order to find the objects that need to be copied or merged.
* If the value does not exist in the target map, then it can be copied directly.
*/
boolean success = true;
for (final Map.Entry<String, Object> entry : sourceMap.entrySet()) {
final String propertyName = entry.getKey();
final Object sourceValue = entry.getValue();
final Object targetValue = dryRun ? targetMap.get(propertyName) : targetMap.putIfAbsent(propertyName, sourceValue);
if (targetValue != null) {
if (targetValue instanceof ModifiableMetadata) {
success = copy(sourceValue, (ModifiableMetadata) targetValue, dryRun);
if (!success) {
/*
* This exception may happen if the source is a subclass of the target. This is the converse
* of what we usually have in Java (we can assign a sub-type to a more generic Java variable)
* but happen here because if the source is a sub-type, we may not be able to copy all values
* from the source to the target. We do not use ClassCastException type in the hope to reduce
* confusion.
*/
if (dryRun)
break;
throw new InvalidMetadataException(errors().getString(Errors.Keys.IllegalPropertyValueClass_3, name(target, propertyName), ((ModifiableMetadata) targetValue).getInterface(), Classes.getClass(sourceValue)));
}
} else if (targetValue instanceof Collection<?>) {
/*
* If the merge is executed in dry run, there is no need to verify the collection elements since
* in case of conflict, it is always possible to append the source values as new elements at the
* end of the collection.
*/
if (dryRun)
continue;
/*
* If the target value is a collection, then the source value should be a collection too
* (otherwise the two objects would not be implementing the same standard, in which case
* a ClassCastException is conform to this method contract). The loop tries to merge the
* source elements to target elements that are specialized enough.
*/
final Collection<?> targetList = (Collection<?>) targetValue;
final Collection<?> sourceList = new LinkedList<>((Collection<?>) sourceValue);
for (final Object element : targetList) {
if (element instanceof ModifiableMetadata) {
final Iterator<?> it = sourceList.iterator();
distribute: while (it.hasNext()) {
final Object value = it.next();
switch(resolve(value, (ModifiableMetadata) element)) {
// case SEPARATE: do nothing.
case MERGE:
{
/*
* If enabled, copy(…, true) call verified that the merge can be done, including
* by recursive checks in all children. The intent is to have a "all or nothing"
* behavior, before the copy(…, false) call below starts to modify the values.
*/
if (!copy(value, (ModifiableMetadata) element, false))
break;
// Fall through
}
case IGNORE:
{
it.remove();
// Merge at most one source element to each target element.
break distribute;
}
}
}
}
}
/*
* Add remaining elements one-by-one. In such case, the Apache SIS metadata implementation
* shall add the elements to the collection instead than replacing the whole collection by
* a singleton. As a partial safety check, we verify that the collection instance contains
* all the previous values.
*/
for (final Object element : sourceList) {
final Object old = targetMap.put(propertyName, element);
if (old instanceof Collection<?>) {
final Collection<?> oldList = (Collection<?>) old;
if (oldList.size() <= targetList.size()) {
// Below is a more expansive check if assertions are enabled.
assert targetList.containsAll(oldList) : propertyName;
continue;
}
}
throw new InvalidMetadataException(errors().getString(Errors.Keys.UnsupportedImplementation_1, Classes.getShortClassName(targetList)));
}
} else {
success = targetValue.equals(sourceValue);
if (!success) {
if (dryRun)
break;
merge(target, propertyName, sourceValue, targetValue);
// If no exception has been thrown by 'merged', assume the conflict solved.
success = true;
}
}
}
}
if (dryRun) {
if (!Boolean.FALSE.equals(done.remove(source)) || !Boolean.TRUE.equals(done.remove(target))) {
// Should never happen.
throw new CorruptedObjectException();
}
}
return success;
}
Aggregations