use of org.apache.sis.internal.util.DefinitionURI in project sis by apache.
the class MultiAuthoritiesFactory method create.
/**
* Creates an object from a code using the given proxy.
*
* @param <T> the type of the object to be returned.
* @param proxy the proxy to use for creating the object.
* @param code the code of the object to create.
* @return the object from one of the authority factory specified at construction time.
* @throws FactoryException if an error occurred while creating the object.
*/
private <T> T create(AuthorityFactoryProxy<? extends T> proxy, String code) throws FactoryException {
ArgumentChecks.ensureNonNull("code", code);
final String authority, version;
final String[] parameters;
final DefinitionURI uri = DefinitionURI.parse(code);
if (uri != null) {
Class<? extends T> type = proxy.type;
proxy = proxy.specialize(uri.type);
/*
* If the URN or URL contains combined references for compound coordinate reference systems,
* create the components. First we verify that all component references have been parsed
* before to start creating any object.
*/
if (uri.code == null) {
final DefinitionURI[] components = uri.components;
if (components != null) {
for (int i = 0; i < components.length; i++) {
if (components[i] == null) {
throw new NoSuchAuthorityCodeException(Resources.format(Resources.Keys.CanNotParseCombinedReference_2, i + 1, uri.isHTTP ? 1 : 0), uri.authority, null, uri.toString());
}
}
// Use the more specific type declared in the URN.
if (proxy != null)
type = proxy.type;
return combine(type, components, uri.isHTTP);
}
}
/*
* At this point we determined that the URN or URL references a single instance (not combined references).
* Example: "urn:ogc:def:crs:EPSG:9.1:4326". Verifies that the object type is recognized and that a code
* is present. The remainder steps are the same as if the user gave a simple code (e.g. "EPSG:4326").
*/
if (uri.authority == null) {
// We want this check before the 'code' value is modified below.
throw new NoSuchAuthorityCodeException(Resources.format(Resources.Keys.MissingAuthority_1, code), null, uri.code, code);
}
authority = uri.authority;
version = uri.version;
code = uri.code;
parameters = uri.parameters;
if (code == null || proxy == null) {
final String s = uri.toString();
final String message;
if (code == null) {
message = Errors.format(Errors.Keys.MissingComponentInElement_2, s, "code");
} else {
message = Resources.format(Resources.Keys.CanNotCreateObjectAsInstanceOf_2, type, DefinitionURI.PREFIX + DefinitionURI.SEPARATOR + uri.type);
}
throw new NoSuchAuthorityCodeException(message, authority, code, s);
}
} else {
/*
* Separate the authority from the rest of the code. The authority is mandatory; if missing,
* an exception will be thrown. Note that the CharSequences.skipLeading/TrailingWhitespaces(…)
* methods are robust to negative index, so the code will work even if code.indexOf(…) returned -1.
*/
int afterAuthority = code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR);
int end = CharSequences.skipTrailingWhitespaces(code, 0, afterAuthority);
int start = CharSequences.skipLeadingWhitespaces(code, 0, end);
if (start >= end) {
throw new NoSuchAuthorityCodeException(Resources.format(Resources.Keys.MissingAuthority_1, code), null, code);
}
authority = code.substring(start, end);
/*
* Separate the version from the rest of the code. The version is optional. The code may have no room
* for version (e.g. "EPSG:4326"), or specify an empty version (e.g. "EPSG::4326"). If the version is
* equals to an empty string or to the "0" string, it will be considered as no version. Usage of 0 as
* a pseudo-version is a practice commonly found in other software products.
*/
int afterVersion = code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR, ++afterAuthority);
start = CharSequences.skipLeadingWhitespaces(code, afterAuthority, afterVersion);
end = CharSequences.skipTrailingWhitespaces(code, start, afterVersion);
version = (start < end && !code.regionMatches(start, DefinitionURI.NO_VERSION, 0, DefinitionURI.NO_VERSION.length())) ? code.substring(start, end) : null;
if (version != null && !Character.isUnicodeIdentifierPart(version.codePointAt(0))) {
throw new NoSuchAuthorityCodeException(Errors.format(Errors.Keys.InvalidVersionIdentifier_1, version), authority, code);
}
/*
* Separate the code from the authority and the version.
*/
code = CharSequences.trimWhitespaces(code, Math.max(afterAuthority, afterVersion + 1), code.length()).toString();
parameters = null;
}
/*
* At this point we have the code without the authority and version parts.
* Push back the authority part if the factory may need it. For now we do that only if the code has
* parameters, since interpretation of the unit parameter in "AUTO(2):42001,unit,longitude,latitude"
* depends on whether the authority is "AUTO" or "AUTO2". This works for now, but we may need a more
* rigorous approach in a future SIS version.
*/
if (parameters != null || code.indexOf(CommonAuthorityFactory.SEPARATOR) >= 0) {
final StringBuilder buffer = new StringBuilder(authority.length() + code.length() + 1).append(authority).append(DefaultNameSpace.DEFAULT_SEPARATOR).append(code);
if (parameters != null) {
for (final String p : parameters) {
buffer.append(CommonAuthorityFactory.SEPARATOR).append(p);
}
}
code = buffer.toString();
}
return proxy.createFromAPI(getAuthorityFactory(AuthorityFactoryIdentifier.create(proxy.factoryType, authority, version)), code);
}
use of org.apache.sis.internal.util.DefinitionURI in project sis by apache.
the class MultiAuthoritiesFactory method combine.
/**
* Invoked when a {@code createFoo(…)} method is given a combined URI.
* A combined URI is a URN or URL referencing other components. For example if the given URI
* is {@code "urn:ogc:def:crs, crs:EPSG::27700, crs:EPSG::5701"}, then the components are:
* <ol>
* <li>{@code "urn:ogc:def:crs:EPSG:9.1:27700"}</li>
* <li>{@code "urn:ogc:def:crs:EPSG:9.1:5701"}</li>
* </ol>
*
* We do not require the components to be instance of CRS, since the "Definition identifier URNs in
* OGC namespace" best practice paper allows other kinds of combination (e.g. of coordinate operations).
*
* @param <T> compile-time value of {@code type} argument.
* @param type type of object to create.
* @param references parsed URI of the components.
* @param isHTTP whether the user URI is an URL (i.e. {@code "http://something"}) instead than a URN.
* @return the combined object.
* @throws FactoryException if an error occurred while creating the combined object.
*/
private <T> T combine(final Class<T> type, final DefinitionURI[] references, final boolean isHTTP) throws FactoryException {
/*
* Identify the type requested by the user and create all components with the assumption that they will
* be of that type. This is the most common case. If during iteration we find an object of another kind,
* then the array type will be downgraded to IdentifiedObject[]. The 'componentType' variable will keep
* its non-null value only if the array stay of the expected sub-type.
*/
final byte requestedType;
IdentifiedObject[] components;
Class<? extends IdentifiedObject> componentType;
if (CoordinateReferenceSystem.class.isAssignableFrom(type)) {
requestedType = AuthorityFactoryIdentifier.CRS;
componentType = CoordinateReferenceSystem.class;
// Intentional covariance.
components = new CoordinateReferenceSystem[references.length];
} else if (CoordinateOperation.class.isAssignableFrom(type)) {
requestedType = AuthorityFactoryIdentifier.OPERATION;
componentType = CoordinateOperation.class;
// Intentional covariance.
components = new CoordinateOperation[references.length];
} else {
throw new FactoryException(Resources.format(Resources.Keys.CanNotCombineUriAsType_1, type));
}
// Note: "compound-crs" ⟶ "crs".
final String expected = NameMeaning.toObjectType(componentType);
for (int i = 0; i < references.length; i++) {
final DefinitionURI ref = references[i];
final IdentifiedObject component = createObject(ref.toString());
if (componentType != null && (!componentType.isInstance(component) || !expected.equalsIgnoreCase(ref.type))) {
componentType = null;
components = Arrays.copyOf(components, components.length, IdentifiedObject[].class);
}
components[i] = component;
}
/*
* At this point we have successfully created all components. The way to interpret those components
* depends mostly on the type of object requested by the user. For a given requested type, different
* rules apply depending on the type of components. Those rules are described in OGC 07-092r1 (2007):
* "Definition identifier URNs in OGC namespace".
*/
IdentifiedObject combined = null;
switch(requestedType) {
case AuthorityFactoryIdentifier.OPERATION:
{
if (componentType != null) {
/*
* URN combined references for concatenated operations. We build an operation name from
* the operation identifiers (rather than CRS identifiers) because this is what the user
* gave to us, and because source/target CRS are not guaranteed to be defined. We do not
* yet support swapping roles of source and target CRS if an implied-reverse coordinate
* operation is included.
*/
final CoordinateOperation[] ops = (CoordinateOperation[]) components;
String name = IdentifiedObjects.getIdentifierOrName(ops[0]) + " ⟶ " + IdentifiedObjects.getIdentifierOrName(ops[ops.length - 1]);
combined = DefaultFactories.forBuildin(CoordinateOperationFactory.class).createConcatenatedOperation(Collections.singletonMap(CoordinateOperation.NAME_KEY, name), ops);
}
break;
}
case AuthorityFactoryIdentifier.CRS:
{
if (componentType != null) {
/*
* URN combined references for compound coordinate reference systems.
* The URNs of the individual well-known CRSs are listed in the same order in which the
* individual coordinate tuples are combined to form the CompoundCRS coordinate tuple.
*/
combined = CRS.compound((CoordinateReferenceSystem[]) components);
} else if (!isHTTP) {
final CoordinateSystem cs = remove(references, components, CoordinateSystem.class);
if (cs != null) {
final Datum datum = remove(references, components, Datum.class);
if (datum != null) {
/*
* URN combined references for datum and coordinate system. In this case, the URN shall
* concatenate the URNs of one well-known datum and one well-known coordinate system.
*/
if (ArraysExt.allEquals(references, null)) {
combined = combine((GeodeticDatum) datum, cs);
}
} else {
/*
* URN combined references for projected or derived CRSs. In this case, the URN shall
* concatenate the URNs of the one well-known CRS, one well-known Conversion, and one
* well-known CartesianCS. Similar action can be taken for derived CRS.
*/
CoordinateReferenceSystem baseCRS = remove(references, components, CoordinateReferenceSystem.class);
CoordinateOperation op = remove(references, components, CoordinateOperation.class);
if (ArraysExt.allEquals(references, null) && op instanceof Conversion) {
combined = combine(baseCRS, (Conversion) op, cs);
}
}
}
}
break;
}
}
/*
* At this point the combined object has been created if we know how to create it.
* Maybe the result matches the definition of an existing object in the database,
* in which case we will use the existing definition for better metadata.
*/
if (combined == null) {
throw new FactoryException(Resources.format(Resources.Keys.UnexpectedComponentInURI));
}
final IdentifiedObject existing = newIdentifiedObjectFinder().findSingleton(combined);
return type.cast(existing != null ? existing : combined);
}
use of org.apache.sis.internal.util.DefinitionURI in project sis by apache.
the class Code method getIdentifier.
/**
* Returns the identifier for this value. This method is the converse of the constructor.
* If the {@link #codeSpace} contains a semicolon, then the part after the last semicolon
* will be taken as the authority version number. This is for consistency with what the
* constructor does.
*
* @return the identifier, or {@code null} if none.
*/
public ReferenceIdentifier getIdentifier() {
String c = code;
if (c == null) {
return null;
}
Citation authority = null;
String version = null, cs = codeSpace;
final DefinitionURI parsed = DefinitionURI.parse(c);
if (parsed != null && parsed.code != null) {
/*
* Case where the URN has been successfully parsed. The OGC's URN contains an "authority" component,
* which we take as the Identifier.codeSpace value (not Identifier.authority despite what the names
* would suggest).
*
* The GML document may also provide a 'codeSpace' attribute separated from the URN, which we take
* as the authority. This is the opposite of what the names would suggest, but we can not map the
* 'codeSpace' attribute to Identifier.codeSpace because the 'codeSpace' attribute value found in
* practice is often "IOGP" while the 'Identifier.description' example provided in ISO 19115-1 for
* an EPSG code has the "EPSG" codespace. Example:
*
* - XML: <gml:identifier codeSpace="IOGP">urn:ogc:def:crs:EPSG::4326</gml:identifier>
* - ISO: For "EPSG:4326", Identifier.codeSpace = "EPSG" and Identifier.code = "4326".
*
* Apache SIS attempts to organize this apparent contradiction by considering IOGP as the codespace of
* the EPSG codespace, but this interpretation is not likely to be widely used by libraries other than
* SIS. For now, a special handling is hard-coded below: if codeSpace = "IOGP" and authority = "EPSG",
* then we take the authority as the Citations.EPSG constant, which has a "IOGP:EPSG" identifier.
*
* A symmetrical special handling for EPSG is done in the 'forIdentifiedObject(…)' method of this class.
*/
if (org.apache.sis.internal.util.Citations.isEPSG(cs, parsed.authority)) {
authority = Citations.EPSG;
} else {
// May be null.
authority = Citations.fromName(cs);
}
cs = parsed.authority;
version = parsed.version;
c = parsed.code;
} else if (cs != null) {
/*
* Case where the URN can not be parsed but a 'codeSpace' attribute exists. We take this 'codeSpace'
* as both the code space and the authority. As a special case, if there is a semi-colon, we take all
* text after that semi-color as the version number.
*/
final int s = cs.lastIndexOf(DefinitionURI.SEPARATOR);
if (s >= 0) {
version = cs.substring(s + 1);
cs = cs.substring(0, s);
}
authority = Citations.fromName(cs);
}
return new NamedIdentifier(authority, cs, c, version, null);
}
Aggregations