use of org.apache.sis.parameter.DefaultParameterDescriptorGroup 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()]);
}
use of org.apache.sis.parameter.DefaultParameterDescriptorGroup in project sis by apache.
the class OperationMethodSetTest method createMethod.
/**
* Creates a new two-dimensional operation method for an operation of the given name.
*
* @param type the value to be returned by {@link DefaultOperationMethod#getOperationType()}.
* @param method the operation name (example: "Mercator (variant A)").
* @return the operation method.
*/
@SuppressWarnings("serial")
private static DefaultOperationMethod createMethod(final Class<? extends Projection> type, final String method) {
Map<String, ?> properties = Collections.singletonMap(DefaultOperationMethod.NAME_KEY, method);
final ParameterDescriptorGroup parameters = new DefaultParameterDescriptorGroup(properties, 1, 1);
/*
* Recycle the ParameterDescriptorGroup name for DefaultOperationMethod.
* This save us one object creation, and is often the same name anyway.
*/
properties = Collections.singletonMap(DefaultOperationMethod.NAME_KEY, parameters.getName());
return new DefaultOperationMethod(properties, 2, 2, parameters) {
@Override
public Class<? extends Projection> getOperationType() {
return type;
}
};
}
use of org.apache.sis.parameter.DefaultParameterDescriptorGroup 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.parameter.DefaultParameterDescriptorGroup in project sis by apache.
the class EPSGDataAccess method createOperationMethod.
/**
* Creates description of the algorithm and parameters used to perform a coordinate operation.
* An {@code OperationMethod} is a kind of metadata: it does not perform any coordinate operation
* (e.g. map projection) by itself, but tells us what is needed in order to perform such operation.
*
* <div class="note"><b>Example:</b>
* some EPSG codes for operation methods are:
*
* <table class="sis" summary="EPSG codes examples">
* <tr><th>Code</th> <th>Description</th></tr>
* <tr><td>9804</td> <td>Mercator (variant A)</td></tr>
* <tr><td>9802</td> <td>Lambert Conic Conformal (2SP)</td></tr>
* <tr><td>9810</td> <td>Polar Stereographic (variant A)</td></tr>
* <tr><td>9624</td> <td>Affine parametric transformation</td></tr>
* </table></div>
*
* @param code value allocated by EPSG.
* @return the operation method for the given code.
* @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
* @throws FactoryException if the object creation failed for some other reason.
*/
@Override
public synchronized OperationMethod createOperationMethod(final String code) throws NoSuchAuthorityCodeException, FactoryException {
ArgumentChecks.ensureNonNull("code", code);
OperationMethod returnValue = null;
try (ResultSet result = executeQuery("Coordinate_Operation Method", "COORD_OP_METHOD_CODE", "COORD_OP_METHOD_NAME", "SELECT COORD_OP_METHOD_CODE," + " COORD_OP_METHOD_NAME," + " REMARKS," + " DEPRECATED" + " FROM [Coordinate_Operation Method]" + " WHERE COORD_OP_METHOD_CODE = ?", code)) {
while (result.next()) {
final Integer epsg = getInteger(code, result, 1);
final String name = getString(code, result, 2);
final String remarks = getOptionalString(result, 3);
final boolean deprecated = getOptionalBoolean(result, 4);
final Integer[] dim = getDimensionsForMethod(epsg);
final ParameterDescriptor<?>[] descriptors = createParameterDescriptors(epsg);
Map<String, Object> properties = createProperties("Coordinate_Operation Method", name, epsg, remarks, deprecated);
// We do not store the formula at this time, because the text is very verbose and rarely used.
final OperationMethod method = new DefaultOperationMethod(properties, dim[0], dim[1], new DefaultParameterDescriptorGroup(properties, 1, 1, descriptors));
returnValue = ensureSingleton(method, returnValue, code);
}
} catch (SQLException exception) {
throw databaseFailure(OperationMethod.class, code, exception);
}
if (returnValue == null) {
throw noSuchAuthorityCode(OperationMethod.class, code);
}
return returnValue;
}
use of org.apache.sis.parameter.DefaultParameterDescriptorGroup in project sis by apache.
the class DefaultOperationMethod method updateDescriptors.
/**
* Invoked by {@link AbstractSingleOperation} for completing the parameter descriptor.
*/
final void updateDescriptors(final GeneralParameterDescriptor[] descriptors) {
final ParameterDescriptorGroup previous = parameters;
parameters = new DefaultParameterDescriptorGroup(IdentifiedObjects.getProperties(previous), previous.getMinimumOccurs(), previous.getMaximumOccurs(), descriptors);
}
Aggregations