use of org.apache.sis.util.collection.BackingStoreException in project sis by apache.
the class Types method forStandardName.
/**
* Returns the Java type (usually a GeoAPI interface) for the given ISO name, or {@code null} if none.
* The identifier argument shall be the value documented in the {@link UML#identifier()} annotation on
* the Java type.
*
* <div class="note"><b>Examples:</b>
* <ul>
* <li>{@code forStandardName("CI_Citation")} returns <code>{@linkplain org.opengis.metadata.citation.Citation}.class</code></li>
* <li>{@code forStandardName("CS_AxisDirection")} returns <code>{@linkplain org.opengis.referencing.cs.AxisDirection}.class</code></li>
* </ul>
* </div>
*
* Only identifiers for the stable part of GeoAPI or for some Apache SIS classes are recognized.
* This method does not handle the identifiers for interfaces in the {@code geoapi-pending} module.
*
* <div class="note"><b>Future evolution:</b>
* when a new ISO type does not yet have a corresponding GeoAPI interface,
* this method may temporarily return an Apache SIS class instead until a future version can use the interface.
* For example {@code forStandardName("CI_Individual")} returns
* <code>{@linkplain org.apache.sis.metadata.iso.citation.DefaultIndividual}.class</code> in Apache SIS versions
* that depend on GeoAPI 3.0, but the return type may be changed to {@code Individual.class} when Apache SIS will
* be upgraded to GeoAPI 3.1.</div>
*
* @param identifier the ISO {@linkplain UML} identifier, or {@code null}.
* @return the GeoAPI interface, or {@code null} if the given identifier is {@code null} or unknown.
*/
public static synchronized Class<?> forStandardName(final String identifier) {
if (identifier == null) {
return null;
}
if (typeForNames == null) {
final Class<Types> c = Types.class;
final InputStream in = c.getResourceAsStream("class-index.properties");
if (in == null) {
throw new MissingResourceException("class-index.properties", c.getName(), identifier);
}
final Properties props = new Properties();
try {
props.load(in);
in.close();
} catch (IOException | IllegalArgumentException e) {
throw new BackingStoreException(e);
}
typeForNames = new HashMap<>(props);
typeForNames.putIfAbsent("MI_SensorTypeCode", "org.apache.sis.internal.metadata.SensorType");
}
final Object value = typeForNames.get(identifier);
if (value == null || value instanceof Class<?>) {
return (Class<?>) value;
}
final Class<?> type;
try {
type = Class.forName((String) value);
} catch (ClassNotFoundException e) {
throw new TypeNotPresentException((String) value, e);
}
typeForNames.put(identifier, type);
return type;
}
use of org.apache.sis.util.collection.BackingStoreException in project sis by apache.
the class Store method components.
/**
* Returns all resources found in the folder given at construction time.
* Only the resources recognized by a {@link DataStore} will be included.
* This includes sub-folders. Resources are in no particular order.
*/
@Override
@SuppressWarnings("ReturnOfCollectionOrArrayField")
public synchronized Collection<Resource> components() throws DataStoreException {
if (components == null) {
final List<DataStore> resources = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(location, this)) {
for (final Path candidate : stream) {
/*
* The candidate path may be a symbolic link to a file that we have previously read.
* In such case, use the existing data store. A use case is a directory containing
* hundred of GeoTIFF files all accompanied by ".prj" files having identical content.
* (Note: those ".prj" files should be invisible since they should be identified as
* GeoTIFF auxiliary files, but current Store implementation does not know that).
*/
final Path real = candidate.toRealPath();
DataStore next = children.get(real);
if (next instanceof Store) {
// Warn about directories only.
((Store) next).sharedRepository(real);
}
if (next == null) {
/*
* The candidate file has never been read before. Try to read it now.
* If the file format is unknown (UnsupportedStorageException), we will
* check if we can open it as a child folder store before to skip it.
*/
final StorageConnector connector = new StorageConnector(candidate);
connector.setOption(OptionKey.LOCALE, locale);
connector.setOption(OptionKey.TIMEZONE, timezone);
connector.setOption(OptionKey.ENCODING, encoding);
try {
if (componentProvider == null) {
// May throw UnsupportedStorageException.
next = DataStores.open(connector);
} else if (componentProvider.probeContent(connector).isSupported()) {
// Open a file of specified format.
next = componentProvider.open(connector);
} else if (Files.isDirectory(candidate)) {
// Open a sub-directory.
next = new Store(this, connector);
} else {
// Not the format specified at construction time.
connector.closeAllExcept(null);
continue;
}
} catch (UnsupportedStorageException ex) {
if (!Files.isDirectory(candidate)) {
connector.closeAllExcept(null);
listeners.warning(Level.FINE, null, ex);
continue;
}
next = new Store(this, connector);
} catch (DataStoreException ex) {
try {
connector.closeAllExcept(null);
} catch (DataStoreException s) {
ex.addSuppressed(s);
}
throw ex;
}
/*
* At this point we got the data store. It could happen that a store for
* the same file has been added concurrently, so we need to check again.
*/
final DataStore existing = children.putIfAbsent(real, next);
if (existing != null) {
next.close();
next = existing;
if (next instanceof Store) {
// Warn about directories only.
((Store) next).sharedRepository(real);
}
}
}
resources.add(next);
}
} catch (DirectoryIteratorException | UncheckedIOException ex) {
// The cause is an IOException (no other type allowed).
throw new DataStoreException(canNotRead(), ex.getCause());
} catch (IOException ex) {
throw new DataStoreException(canNotRead(), ex);
} catch (BackingStoreException ex) {
throw ex.unwrapOrRethrow(DataStoreException.class);
}
components = UnmodifiableArrayList.wrap(resources.toArray(new Resource[resources.size()]));
}
// Safe because unmodifiable list.
return components;
}
use of org.apache.sis.util.collection.BackingStoreException in project sis by apache.
the class Store method write.
/**
* Replaces the content of this GPX file by the given metadata and features.
*
* @param metadata the metadata to write, or {@code null} if none.
* @param features the features to write, or {@code null} if none.
* @throws ConcurrentReadException if the {@code features} stream was provided by this data store.
* @throws DataStoreException if an error occurred while writing the data.
*/
public synchronized void write(final Metadata metadata, final Stream<? extends AbstractFeature> features) throws DataStoreException {
try {
/*
* If we created a reader for reading metadata, we need to close that reader now otherwise the call
* to 'new Writer(…)' will fail. Note that if that reader was in use by someone else, the 'reader'
* field would be null and the 'new Writer(…)' call should detect that a reader is in use somewhere.
*/
final Reader r = reader;
if (r != null) {
reader = null;
r.close();
}
/*
* Get the writer if no read or other write operation is in progress, then write the data.
*/
try (Writer writer = new Writer(this, org.apache.sis.internal.storage.gpx.Metadata.castOrCopy(metadata, locale))) {
writer.writeStartDocument();
if (features != null) {
features.forEachOrdered(writer);
}
writer.writeEndDocument();
}
} catch (BackingStoreException e) {
final Throwable cause = e.getCause();
if (cause instanceof DataStoreException) {
throw (DataStoreException) cause;
}
throw new DataStoreException(e.getLocalizedMessage(), cause);
} catch (Exception e) {
if (e instanceof UncheckedIOException) {
e = ((UncheckedIOException) e).getCause();
}
throw new DataStoreException(e);
}
}
use of org.apache.sis.util.collection.BackingStoreException in project sis by apache.
the class CoordinateOperationRegistry method search.
/**
* Returns operations for conversions or transformations between two coordinate reference systems.
* This method extracts the authority code from the supplied {@code sourceCRS} and {@code targetCRS},
* and submit them to the {@link #registry}. If no operation is found for those codes, then this method
* returns {@code null}.
*
* @param sourceCRS source coordinate reference system.
* @param targetCRS target coordinate reference system.
* @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}, or {@code null}
* if no such operation is explicitly defined in the underlying database.
* @throws IllegalArgumentException if the coordinate systems are not of the same type or axes do not match.
* @throws IncommensurableException if the units are not compatible or a unit conversion is non-linear.
* @throws FactoryException if an error occurred while creating the operation.
*/
private List<CoordinateOperation> search(final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS) throws IllegalArgumentException, IncommensurableException, FactoryException {
final List<String> sources = findCode(sourceCRS);
if (sources.isEmpty())
return null;
final List<String> targets = findCode(targetCRS);
if (targets.isEmpty())
return null;
final List<CoordinateOperation> operations = new ArrayList<>();
boolean foundDirectOperations = false;
boolean useDeprecatedOperations = false;
for (final String sourceID : sources) {
for (final String targetID : targets) {
if (sourceID.equals(targetID)) {
/*
* Above check is necessary because this method may be invoked in some situations where the code
* are equal while the CRS are not. Such situation should be illegal, but unfortunately it still
* happen because many software products are not compliant with EPSG definition of axis order.
* In such cases we will need to compute a transform from sourceCRS to targetCRS ignoring the
* source and target codes. The CoordinateOperationFinder class can do that, providing that we
* prevent this CoordinateOperationRegistry to (legitimately) claims that the operation from
* sourceCode to targetCode is the identity transform.
*/
return null;
}
/*
* Some pairs of CRS have a lot of coordinate operations backed by datum shift grids.
* We do not want to load all of them until we found the right coordinate operation.
* The non-public Semaphores.METADATA_ONLY mechanism instructs EPSGDataAccess to
* instantiate DeferredCoordinateOperation instead of full coordinate operations.
*/
final boolean mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
try {
Collection<CoordinateOperation> authoritatives;
try {
authoritatives = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
final boolean inverse = Containers.isNullOrEmpty(authoritatives);
if (inverse) {
/*
* No operation from 'source' to 'target' available. But maybe there is an inverse operation.
* This is typically the case when the user wants to convert from a projected to a geographic CRS.
* The EPSG database usually contains transformation paths for geographic to projected CRS only.
*/
if (foundDirectOperations) {
// Ignore inverse operations if we already have direct ones.
continue;
}
authoritatives = registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
if (Containers.isNullOrEmpty(authoritatives)) {
continue;
}
} else if (!foundDirectOperations) {
foundDirectOperations = true;
// Keep only direct operations.
operations.clear();
}
} catch (NoSuchAuthorityCodeException | MissingFactoryResourceException e) {
/*
* sourceCode or targetCode is unknown to the underlying authority factory.
* Ignores the exception and fallback on the generic algorithm provided by
* CoordinateOperationFinder.
*/
log(null, e);
continue;
}
/*
* If we found at least one non-deprecated operation, we will stop the search at
* the first deprecated one (assuming that deprecated operations are sorted last).
* Deprecated operations are kept only if there is no non-deprecated operations.
*/
try {
for (final CoordinateOperation candidate : authoritatives) {
if (candidate != null) {
// Paranoiac check.
if ((candidate instanceof Deprecable) && ((Deprecable) candidate).isDeprecated()) {
if (!useDeprecatedOperations && !operations.isEmpty())
break;
useDeprecatedOperations = true;
} else if (useDeprecatedOperations) {
useDeprecatedOperations = false;
// Replace deprecated operations by non-deprecated ones.
operations.clear();
}
operations.add(candidate);
}
}
} catch (BackingStoreException exception) {
throw exception.unwrapOrRethrow(FactoryException.class);
}
} finally {
if (!mdOnly) {
Semaphores.clear(Semaphores.METADATA_ONLY);
}
}
}
}
/*
* At this point we got the list of coordinate operations. Now, sort them in preference order.
* We will loop over all coordinate operations and select the one having the largest intersection
* with the area of interest. Note that if the user did not specified an area of interest himself,
* then we need to get one from the CRS. This is necessary for preventing the transformation from
* NAD27 to NAD83 in Idaho to select the transform for Alaska (since the later has a larger area).
*/
CoordinateOperationSorter.sort(operations, Extents.getGeographicBoundingBox(areaOfInterest));
final ListIterator<CoordinateOperation> it = operations.listIterator();
while (it.hasNext()) {
/*
* At this point we filtered a CoordinateOperation by looking only at its metadata.
* Code following this point will need the full coordinate operation, including its
* MathTransform. So if we got a deferred operation, we need to resolve it now.
* Conversely, we should not use metadata below this point because the call to
* inverse(CoordinateOperation) is not guaranteed to preserve all metadata.
*/
CoordinateOperation operation = it.next();
try {
if (operation instanceof DeferredCoordinateOperation) {
operation = ((DeferredCoordinateOperation) operation).create();
}
if (operation instanceof SingleOperation && operation.getMathTransform() == null) {
operation = fromDefiningConversion((SingleOperation) operation, foundDirectOperations ? sourceCRS : targetCRS, foundDirectOperations ? targetCRS : sourceCRS);
if (operation == null) {
it.remove();
continue;
}
}
if (!foundDirectOperations) {
operation = inverse(operation);
}
} catch (NoninvertibleTransformException | MissingFactoryResourceException e) {
/*
* If we failed to get the real CoordinateOperation instance, remove it from
* the collection and try again in order to get the next best choices.
*/
log(null, e);
it.remove();
// Try again with the next best case.
continue;
}
/*
* It is possible that the CRS given to this method were not quite right. For example the user
* may have created his CRS from a WKT using a different axis order than the order specified by
* the authority and still (wrongly) call those CRS "EPSG:xxxx". So we check if the source and
* target CRS for the operation we just created are equivalent to the CRS specified by the user.
*
* NOTE: FactoryException may be thrown if we fail to create a transform from the user-provided
* CRS to the authority-provided CRS. That transform should have been only an identity transform,
* or a simple affine transform if the user specified wrong CRS as explained in above paragraph.
* If we fail here, we are likely to fail for all other transforms. So we are better to let the
* FactoryException propagate.
*/
operation = complete(operation, sourceCRS, targetCRS);
if (filter(operation)) {
if (stopAtFirst) {
operations.clear();
operations.add(operation);
break;
}
it.set(operation);
} else {
it.remove();
}
}
return operations;
}
use of org.apache.sis.util.collection.BackingStoreException in project sis by apache.
the class IdentifiedObjectFinder method findSingleton.
/**
* Lookups only one object which is approximatively equal to the specified object.
* This method invokes {@link #find(IdentifiedObject)}, then examine the returned {@code Set} as below:
*
* <ul>
* <li>If the set is empty, then this method returns {@code null}.</li>
* <li>If the set contains exactly one element, then this method returns that element.</li>
* <li>If the set contains more than one element, but only one element has the same axis order
* than {@code object} and all other elements have different axis order,
* then this method returns the single element having the same axis order.</li>
* <li>Otherwise this method considers that there is ambiguity and returns {@code null}.</li>
* </ul>
*
* @param object the object looked up.
* @return the identified object, or {@code null} if none or ambiguous.
* @throws FactoryException if an error occurred while creating an object.
*/
public IdentifiedObject findSingleton(final IdentifiedObject object) throws FactoryException {
/*
* Do not invoke Set.size() because it may be a costly operation if the subclass
* implements a mechanism that create IdentifiedObject instances only on demand.
*/
IdentifiedObject result = null;
boolean sameAxisOrder = false;
boolean ambiguous = false;
try {
for (final IdentifiedObject candidate : find(object)) {
final boolean so = !ignoreAxes || Utilities.deepEquals(candidate, object, COMPARISON_MODE);
if (result != null) {
ambiguous = true;
if (sameAxisOrder && so) {
// Found two matches even when taking in account axis order.
return null;
}
}
result = candidate;
sameAxisOrder = so;
}
} catch (BackingStoreException e) {
throw e.unwrapOrRethrow(FactoryException.class);
}
return (sameAxisOrder || !ambiguous) ? result : null;
}
Aggregations