| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.internal.jaxb.referencing; |
| |
| import java.util.Map; |
| import java.util.List; |
| import java.util.LinkedHashMap; |
| import javax.xml.bind.annotation.XmlElement; |
| import org.opengis.parameter.ParameterDescriptor; |
| import org.opengis.parameter.ParameterDescriptorGroup; |
| import org.opengis.parameter.GeneralParameterDescriptor; |
| import org.apache.sis.internal.jaxb.gco.PropertyType; |
| import org.apache.sis.internal.referencing.Resources; |
| import org.apache.sis.parameter.DefaultParameterDescriptorGroup; |
| import org.apache.sis.util.CorruptedObjectException; |
| import org.apache.sis.util.collection.Containers; |
| |
| |
| /** |
| * JAXB adapter mapping implementing class to the GeoAPI interface. See |
| * package documentation for more information about JAXB and interface. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 0.6 |
| * @since 0.6 |
| * @module |
| */ |
| public final class CC_OperationParameterGroup extends PropertyType<CC_OperationParameterGroup,ParameterDescriptorGroup> { |
| /** |
| * Empty constructor for JAXB only. |
| */ |
| public CC_OperationParameterGroup() { |
| } |
| |
| /** |
| * Returns the GeoAPI interface which is bound by this adapter. |
| * This method is indirectly invoked by the private constructor |
| * below, so it shall not depend on the state of this object. |
| * |
| * @return {@code ParameterDescriptorGroup.class} |
| */ |
| @Override |
| protected Class<ParameterDescriptorGroup> getBoundType() { |
| return ParameterDescriptorGroup.class; |
| } |
| |
| /** |
| * Constructor for the {@link #wrap} method only. |
| */ |
| private CC_OperationParameterGroup(final ParameterDescriptorGroup parameter) { |
| super(parameter); |
| } |
| |
| /** |
| * Invoked by {@link PropertyType} at marshalling time for wrapping the given value |
| * in a {@code <gml:OperationParameterGroup>} XML element. |
| * |
| * @param parameter the element to marshal. |
| * @return a {@code PropertyType} wrapping the given the element. |
| */ |
| @Override |
| protected CC_OperationParameterGroup wrap(final ParameterDescriptorGroup parameter) { |
| return new CC_OperationParameterGroup(parameter); |
| } |
| |
| /** |
| * Invoked by JAXB at marshalling time for getting the actual element to write |
| * inside the {@code <gml:OperationParameter>} XML element. |
| * This is the value or a copy of the value given in argument to the {@code wrap} method. |
| * |
| * @return the element to be marshalled. |
| */ |
| @XmlElement(name = "OperationParameterGroup") |
| public DefaultParameterDescriptorGroup getElement() { |
| return DefaultParameterDescriptorGroup.castOrCopy(metadata); |
| } |
| |
| /** |
| * Invoked by JAXB at unmarshalling time for storing the result temporarily. |
| * |
| * @param parameter the unmarshalled element. |
| */ |
| public void setElement(final DefaultParameterDescriptorGroup parameter) { |
| metadata = parameter; |
| } |
| |
| /** |
| * 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 explicitly 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 of 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 explicitly 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 of 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()]); |
| } |
| |
| /** |
| * Throws an exception for mismatched descriptor if a condition is false. |
| * This is used for verifying that a descriptors has the expected properties. |
| */ |
| private static void verifyEquivalence(final String name, final boolean condition) { |
| if (!condition) { |
| throw new IllegalArgumentException(Resources.format(Resources.Keys.MismatchedParameterDescriptor_1, name)); |
| } |
| } |
| } |