Search in sources :

Example 6 with CorruptedObjectException

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;
}
Also used : CorruptedObjectException(org.apache.sis.util.CorruptedObjectException) Cloner(org.apache.sis.internal.util.Cloner)

Example 7 with CorruptedObjectException

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();
    }
}
Also used : CorruptedObjectException(org.apache.sis.util.CorruptedObjectException) InternationalString(org.opengis.util.InternationalString)

Example 8 with 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);
    }
}
Also used : DefaultParameterDescriptorGroup(org.apache.sis.parameter.DefaultParameterDescriptorGroup) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) CorruptedObjectException(org.apache.sis.util.CorruptedObjectException) ParameterNotFoundException(org.opengis.parameter.ParameterNotFoundException) HashSet(java.util.HashSet)

Example 9 with CorruptedObjectException

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;
}
Also used : MetadataStandard(org.apache.sis.metadata.MetadataStandard) InvalidMetadataException(org.apache.sis.metadata.InvalidMetadataException) AbstractMetadata(org.apache.sis.metadata.AbstractMetadata) ModifiableMetadata(org.apache.sis.metadata.ModifiableMetadata) Iterator(java.util.Iterator) Collection(java.util.Collection) CorruptedObjectException(org.apache.sis.util.CorruptedObjectException) IdentityHashMap(java.util.IdentityHashMap) Map(java.util.Map)

Aggregations

CorruptedObjectException (org.apache.sis.util.CorruptedObjectException)9 DefaultParameterDescriptorGroup (org.apache.sis.parameter.DefaultParameterDescriptorGroup)2 GeneralParameterDescriptor (org.opengis.parameter.GeneralParameterDescriptor)2 Instant (java.time.Instant)1 Collection (java.util.Collection)1 Date (java.util.Date)1 HashSet (java.util.HashSet)1 IdentityHashMap (java.util.IdentityHashMap)1 Iterator (java.util.Iterator)1 LinkedHashMap (java.util.LinkedHashMap)1 Map (java.util.Map)1 AbstractIdentifiedType (org.apache.sis.feature.AbstractIdentifiedType)1 DefaultFeatureType (org.apache.sis.feature.DefaultFeatureType)1 Cloner (org.apache.sis.internal.util.Cloner)1 Vector (org.apache.sis.math.Vector)1 AbstractMetadata (org.apache.sis.metadata.AbstractMetadata)1 InvalidMetadataException (org.apache.sis.metadata.InvalidMetadataException)1 MetadataStandard (org.apache.sis.metadata.MetadataStandard)1 ModifiableMetadata (org.apache.sis.metadata.ModifiableMetadata)1 ParameterDescriptor (org.opengis.parameter.ParameterDescriptor)1