use of org.apache.sis.util.collection.BackingStoreException in project sis by apache.
the class IdentifiedObjectSet method get.
/**
* Returns the identified object for the specified value, creating it if needed.
*
* @throws BackingStoreException if the object creation failed.
*
* @see #createObject(String)
*/
final T get(final String code) throws BackingStoreException {
T object;
boolean success;
synchronized (objects) {
object = objects.get(code);
success = (object != null || !objects.containsKey(code));
}
/*
* If we need to create the object, it should be done outside synchronized block.
* There is a risk that the same object is created twice in concurrent threads.
* If this happen, we will discard the duplicated value.
*/
if (!success) {
try {
object = createObject(code);
// Shall be set only after above line succeed.
success = true;
} catch (FactoryException exception) {
if (!isRecoverableFailure(exception)) {
throw new BackingStoreException(exception);
}
final LogRecord record = Messages.getResources(getLocale()).getLogRecord(Level.WARNING, Messages.Keys.CanNotInstantiateForIdentifier_3, type, code, getCause(exception));
record.setLoggerName(Loggers.CRS_FACTORY);
Logging.log(IdentifiedObjectSet.class, "createObject", record);
}
synchronized (objects) {
if (success) {
/*
* The check for 'containsKey' is a paranoiac check in case the element has been removed
* in another thread while we were creating the object. This is likely to be unnecessary
* in the vast majority of cases where the set of codes is never modified after this set
* has been published. However, if someone decided to do such concurrent modifications,
* not checking for concurrent removal could be a subtle and hard-to-find bug, so we are
* better to be safe. Note that if a concurrent removal happened, we still return the non-null
* object but we do not put it in this IdentifiedObjectSet. This behavior is as if this method
* has been invoked before the concurrent removal happened.
*/
if (objects.containsKey(code)) {
// Needed because code may be associated to null value.
final T c = objects.putIfAbsent(code, object);
if (c != null) {
// The object has been created concurrently.
object = c;
}
}
} else if (objects.remove(code, null)) {
// Do not remove if a concurrent thread succeeded.
codes = null;
}
}
}
return object;
}
use of org.apache.sis.util.collection.BackingStoreException in project sis by apache.
the class MultiAuthoritiesFactory method getAuthorityCodes.
/**
* Returns the set of authority codes for objects of the given type.
* This method returns the union of codes returned by all factories specified at construction time.
*
* <p>The {@link Set#contains(Object)} method of the returned set is lenient:
* it accepts various ways to format a code even if the iterator returns only one form.
* For example the {@code contains(Object)} method may return {@code true} for {@code "EPSG:4326"},
* {@code "EPSG::4326"}, {@code "urn:ogc:def:crs:EPSG::4326"}, <i>etc.</i> even if
* the iterator returns only {@code "EPSG:4326"}.</p>
*
* <p><b>Warnings:</b></p>
* <ul>
* <li>Callers should not retain a reference to the returned collection for a long time,
* since it may be backed by database connections (depending on the factory implementations).</li>
* <li>The returned set is not thread-safe. Each thread should ask its own instance and let
* the garbage collector disposes it as soon as the collection is not needed anymore.</li>
* <li>Call to the {@link Set#size()} method on the returned collection should be avoided
* since it may be costly.</li>
* </ul>
*
* @param type the spatial reference objects type.
* @return the set of authority codes for spatial reference objects of the given type.
* @throws FactoryException if access to an underlying factory failed.
*/
@Override
public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException {
return new SetOfUnknownSize<String>() {
/**
* Returns an iterator over all authority codes.
* Codes are fetched on-the-fly.
*/
@Override
public Iterator<String> iterator() {
return new AbstractIterator<String>() {
/**
* An iterator over the factories for which to return codes.
*/
private final Iterator<AuthorityFactory> factories = getAllFactories();
/**
* An iterator over the codes of the current factory.
*/
private Iterator<String> codes = Collections.emptyIterator();
/**
* The prefix to prepend before codes, or {@code null} if none.
*/
private String prefix;
/**
* For filtering duplicated codes when there is many versions of the same authority.
*/
private final Set<String> done = new HashSet<>();
/**
* Tests if there is more codes to return.
*/
@Override
public boolean hasNext() {
while (next == null) {
while (!codes.hasNext()) {
do {
if (!factories.hasNext()) {
return false;
}
final AuthorityFactory factory = factories.next();
codes = getAuthorityCodes(factory).iterator();
prefix = getCodeSpace(factory);
} while (!done.add(prefix));
}
next = codes.next();
}
return true;
}
/**
* Returns the next element, with namespace inserted before the code if needed.
*/
@Override
public String next() {
String code = super.next();
if (prefix != null && code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR) < 0) {
code = prefix + DefaultNameSpace.DEFAULT_SEPARATOR + code;
}
return code;
}
};
}
/**
* The cache of values returned by {@link #getAuthorityCodes(AuthorityFactory)}.
*/
private final Map<AuthorityFactory, Set<String>> cache = new IdentityHashMap<>();
/**
* Returns the authority codes for the given factory.
* This method invokes {@link AuthorityFactory#getAuthorityCodes(Class)}
* only once per factory and caches the returned {@code Set<String>}.
*/
final Set<String> getAuthorityCodes(final AuthorityFactory factory) {
Set<String> codes = cache.get(factory);
if (codes == null) {
try {
codes = factory.getAuthorityCodes(type);
} catch (FactoryException e) {
throw new BackingStoreException(e);
}
if (cache.put(factory, codes) != null) {
throw new ConcurrentModificationException();
}
}
return codes;
}
/**
* The collection size, or a negative value if we have not yet computed the size.
* A negative value different than -1 means that we have not counted all elements,
* but we have determined that the set is not empty.
*/
private int size = -1;
/**
* Returns {@code true} if the {@link #size()} method is cheap.
*/
@Override
protected boolean isSizeKnown() {
return size >= 0;
}
/**
* Returns the number of elements in this set (costly operation).
*/
@Override
public int size() {
if (size < 0) {
int n = 0;
final Set<String> done = new HashSet<>();
for (final Iterator<AuthorityFactory> it = getAllFactories(); it.hasNext(); ) {
final AuthorityFactory factory = it.next();
if (done.add(getCodeSpace(factory))) {
n += getAuthorityCodes(factory).size();
}
}
size = n;
}
return size;
}
/**
* Returns {@code true} if the set does not contain any element.
* This method is much more efficient than testing {@code size() != 0}
* since it will stop iteration as soon as an element is found.
*/
@Override
public boolean isEmpty() {
if (size == -1) {
for (final Iterator<AuthorityFactory> it = getAllFactories(); it.hasNext(); ) {
if (!getAuthorityCodes(it.next()).isEmpty()) {
// Size still unknown, but we know that the set is not empty.
size = -2;
return false;
}
}
size = 0;
}
return size == 0;
}
/**
* The proxy for the {@code GeodeticAuthorityFactory.getAuthorityCodes(type).contains(String)}.
* Used by {@link #contains(Object)} for delegating its work to the most appropriate factory.
*/
private final AuthorityFactoryProxy<Boolean> contains = new AuthorityFactoryProxy<Boolean>(Boolean.class, AuthorityFactoryIdentifier.ANY) {
@Override
Boolean createFromAPI(AuthorityFactory factory, String code) throws FactoryException {
return getAuthorityCodes(factory).contains(code);
}
@Override
AuthorityFactoryProxy<Boolean> specialize(String typeName) {
return this;
}
};
/**
* Returns {@code true} if the factory contains the given code.
*/
@Override
public boolean contains(final Object code) {
if (code instanceof String)
try {
return create(contains, (String) code);
} catch (NoSuchAuthorityCodeException e) {
// Ignore - will return false.
} catch (FactoryException e) {
throw new BackingStoreException(e);
}
return false;
}
/**
* Declared soon as unsupported operation for preventing a call to {@link #size()}.
*/
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
};
}
Aggregations