use of org.apache.sis.internal.metadata.Dependencies in project sis by apache.
the class PropertyConsistencyCheck method testDependenciesAnnotation.
/**
* Verifies the {@link Dependencies} annotations. This method verifies that the annotation is applied on
* deprecated getter methods, that the referenced properties exist and the getter methods of referenced
* properties are not deprecated.
*
* @throws NoSuchMethodException if {@link PropertyAccessor} references a non-existent method (would be a bug).
*
* @since 0.8
*/
@Test
public void testDependenciesAnnotation() throws NoSuchMethodException {
for (final Class<?> type : types) {
final Class<?> impl = standard.getImplementation(type);
if (impl != null) {
Map<String, String> names = null;
for (final Method method : impl.getDeclaredMethods()) {
final Dependencies dep = method.getAnnotation(Dependencies.class);
if (dep != null) {
final String name = method.getName();
if (names == null) {
names = standard.asNameMap(type, KeyNamePolicy.JAVABEANS_PROPERTY, KeyNamePolicy.METHOD_NAME);
}
/*
* Currently, @Dependencies is applied only on deprecated getter methods.
* However this policy may change in future Apache SIS versions.
*/
assertTrue(name, name.startsWith("get"));
assertTrue(name, method.isAnnotationPresent(Deprecated.class));
/*
* All dependencies shall be non-deprecated methods. Combined with above
* restriction about @Dependencies applied only on deprected methods, this
* ensure that there is no cycle.
*/
for (final String ref : dep.value()) {
// Verify that the dependency is a property name.
assertEquals(name, names.get(ref), ref);
// Verify that the referenced method is non-deprecated.
assertFalse(name, impl.getMethod(names.get(ref)).isAnnotationPresent(Deprecated.class));
}
}
}
}
}
}
use of org.apache.sis.internal.metadata.Dependencies in project sis by apache.
the class DefaultMetadata method getDataSetUri.
/**
* Provides the URI of the dataset to which the metadata applies.
*
* @return Uniform Resource Identifier of the dataset, or {@code null}.
*
* @deprecated As of ISO 19115:2014, replaced by {@link #getIdentificationInfo()} followed by
* {@link DefaultDataIdentification#getCitation()} followed by {@link DefaultCitation#getOnlineResources()}.
*/
@Override
@Deprecated
@Dependencies("getIdentificationInfo")
@XmlElement(name = "dataSetURI", namespace = LegacyNamespaces.GMD)
public String getDataSetUri() {
String linkage = null;
final Collection<Identification> info;
if (FilterByVersion.LEGACY_METADATA.accept() && (info = getIdentificationInfo()) != null) {
for (final Identification identification : info) {
final Citation citation = identification.getCitation();
if (citation instanceof DefaultCitation) {
final Collection<? extends OnlineResource> onlineResources = ((DefaultCitation) citation).getOnlineResources();
if (onlineResources != null) {
for (final OnlineResource link : onlineResources) {
final URI uri = link.getLinkage();
if (uri != null) {
if (linkage == null) {
linkage = uri.toString();
} else {
LegacyPropertyAdapter.warnIgnoredExtraneous(OnlineResource.class, DefaultMetadata.class, "getDataSetUri");
break;
}
}
}
}
}
}
}
return linkage;
}
use of org.apache.sis.internal.metadata.Dependencies in project sis by apache.
the class DefaultContact method getPhone.
/**
* Returns telephone numbers at which the organization or individual may be contacted.
* This method returns the first telephone number associated to {@code TelephoneType.VOICE}
* or {@code TelephoneType.FACSIMILE FACSIMILE}.
*
* @return telephone numbers at which the organization or individual may be contacted, or {@code null}.
*
* @deprecated As of ISO 19115:2014, replaced by {@link #getPhones()}.
*/
@Override
@Deprecated
@Dependencies("getPhones")
@XmlElement(name = "phone", namespace = LegacyNamespaces.GMD)
public Telephone getPhone() {
Telephone phone = null;
if (FilterByVersion.LEGACY_METADATA.accept()) {
final Collection<Telephone> phones = getPhones();
if (phones != null) {
// May be null on marshalling.
CodeList<?> ignored = null;
for (final Telephone c : phones) {
if (c instanceof DefaultTelephone) {
String name;
final CodeList<?> type = ((DefaultTelephone) c).numberType;
if (type != null && ("VOICE".equals(name = type.name()) || "FACSIMILE".equals(name))) {
if (phone == null) {
phone = c;
}
} else if (ignored == null) {
ignored = type;
}
}
}
if (ignored != null) {
Context.warningOccured(Context.current(), DefaultContact.class, "getPhone", Messages.class, Messages.Keys.IgnoredPropertyAssociatedTo_1, ignored);
}
}
}
return phone;
}
use of org.apache.sis.internal.metadata.Dependencies in project sis by apache.
the class DefaultSource method getSourceExtents.
/**
* Returns the information about the spatial, vertical and temporal extent of the source data.
* This method fetches the values from the {@linkplain #getScope() scope}.
*
* @return information about the extent of the source data.
*
* @deprecated As of ISO 19115:2014, moved to {@link DefaultScope#getExtents()}.
*/
@Override
@Deprecated
@Dependencies("getScope")
@XmlElement(name = "sourceExtent", namespace = LegacyNamespaces.GMD)
public Collection<Extent> getSourceExtents() {
if (FilterByVersion.LEGACY_METADATA.accept()) {
Scope scope = getScope();
if (!(scope instanceof DefaultScope)) {
if (isModifiable()) {
scope = new DefaultScope(scope);
this.scope = scope;
} else {
return Collections.singleton(scope.getExtent());
}
}
}
return null;
}
use of org.apache.sis.internal.metadata.Dependencies in project sis by apache.
the class Dispatcher method fetchValue.
/**
* Gets, computes or read from the database a metadata property value.
* This method returns the first non-null value in the following choices:
*
* <ol>
* <li>If the property value is present in the {@linkplain #cache}, the cached value.</li>
* <li>If the "cache" can compute the value from other property values, the result of that computation.
* This case happen mostly for deprecated properties that are replaced by one or more newer properties.</li>
* <li>The value stored in the database. The database is queried only once for the requested property
* and the result is cached for future reuse.</li>
* </ol>
*
* @param info information related to the <em>interface</em> of the metadata object for which a property
* value is requested. This is used for fetching information from the {@link MetadataStandard}.
* @param method the method to be invoked. The class given by {@link Method#getDeclaringClass()} is usually
* the same than the one given by {@link LookupInfo#getMetadataType()}, but not necessarily.
* The two classes may differ if the method is declared only in the implementation class.
* @return the property value, or {@code null} if none.
* @throws ReflectiveOperationException if an error occurred while querying the {@link #cache}.
* @throws SQLException if an error occurred while querying the database.
* @throws MetadataStoreException if a value was not found or can not be converted to the expected type.
*/
private Object fetchValue(final LookupInfo info, final Method method) throws ReflectiveOperationException, SQLException, MetadataStoreException {
Object value = null;
// Okay even if overflow.
final long nullBit = 1L << info.asIndexMap(source.standard).get(method.getName());
/*
* The NULL_COLLECTION semaphore prevents creation of new empty collections by getter methods
* (a consequence of lazy instantiation). The intent is to avoid creation of unnecessary objects
* for all unused properties. Users should not see behavioral difference.
*/
if ((nullValues & nullBit) == 0) {
final Class<?> type = info.getMetadataType();
final boolean allowNull = Semaphores.queryAndSet(Semaphores.NULL_COLLECTION);
try {
Object cache = this.cache;
if (cache != null) {
synchronized (cache) {
value = method.invoke(cache);
}
}
if (value == null) {
// Precaution in case method.invoke(cache) fetched other metadata.
info.setMetadataType(type);
value = source.readColumn(info, method, this);
if (value != null) {
if (cache == null) {
final Class<?> impl = source.standard.getImplementation(type);
if (impl == null) {
return value;
}
this.cache = cache = impl.newInstance();
/*
* We do not use AtomicReference because it is okay if the cache is instantiated twice.
* It would cause us to query the database twice, but we should get the same information.
*/
}
final Map<String, Object> map = source.standard.asValueMap(cache, type, KeyNamePolicy.METHOD_NAME, ValueExistencePolicy.ALL);
synchronized (cache) {
value = map.putIfAbsent(method.getName(), value);
if (value == null) {
value = method.invoke(cache);
}
}
} else {
/*
* If we found no explicit value for the requested property, maybe it is a deprecated property
* computed from other property values and those other properties have not yet been stored in
* the cache object (because that "cache" is also the object computing deprecated properties).
*/
final Class<?> impl = source.standard.getImplementation(type);
if (impl != null) {
final Dependencies dependencies = impl.getMethod(method.getName()).getAnnotation(Dependencies.class);
if (dependencies != null) {
boolean hasValue = false;
for (final String dep : dependencies.value()) {
info.setMetadataType(type);
hasValue |= (fetchValue(info, impl.getMethod(dep)) != null);
}
if (hasValue) {
// Created by recursive 'invoke(…)' call above.
cache = this.cache;
if (cache != null) {
synchronized (cache) {
// Attempt a new computation.
value = method.invoke(cache);
}
}
}
}
}
}
}
} finally {
if (!allowNull) {
Semaphores.clear(Semaphores.NULL_COLLECTION);
}
}
}
if (value == null) {
nullValues |= nullBit;
}
return value;
}
Aggregations