/*
* Copyright 2017 TNG Technology Consulting GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tngtech.archunit.core.domain;
import java.lang.annotation.Annotation;
import java.util.Map;
import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.properties.HasType;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
/**
* Represents an imported annotation on an annotated object like a class or a method. To be
* independent of the classpath, all properties of this annotation are just stored as a simple
* key value pairs. I.e. if you consider
* <pre><code>
* {@literal @}MyAnnotation(name = "some name", anAttribute = 7)
* class MyClass {}
* </code></pre>
* this annotation will be imported storing the association
* <pre><code>
* name --> "some name"
* anAttribute --> 7
* </code></pre>
* Properties will be made available via {@link #get(String)}, e.g.
* <pre><code>
* myAnnotation.get("anAttribute")
* </code></pre>
* will return the value 7. Since this behavior is inconvenient (loss of type safety),
* there is another approach to retrieve these values, if the annotation can
* be resolved on the classpath. It's then possible to access a simple proxy
* <pre><code>
* MyAnnotation moreConvenient = myAnnotation.as(MyAnnotation.class);
* moreConvenient.anAttribute(); // --> returns 7
* </code></pre>
* ----------<br>
* NOTE<br>
* ----------<br>
* ArchUnit holds the annotation in a classpath independent representation, i.e. some types will
* be mapped, when the access is proxied. Consider
* <pre><code>
* {@literal @}SomeAnnotation(type = String.class)
* class MyClass {}
* </code></pre>
* Accesses to 'type' will be different for the proxied version:
* <pre><code>
* someAnnotation.get("type"); // --> returns JavaClass{String}
* someAnnotation.as(SomeAnnotation.class).type(); // --> returns String.class
* </code></pre>
*/
public final class JavaAnnotation implements HasType {
private final JavaClass type;
private final Map<String, Object> values;
JavaAnnotation(JavaAnnotationBuilder builder) {
this.type = checkNotNull(builder.getType());
this.values = checkNotNull(builder.getValues());
}
@Override
public JavaClass getType() {
return type;
}
/**
* Returns the value of the property with the given name, i.e. the result of the method with the property name
* of the represented {@link java.lang.annotation.Annotation}. E.g. for
* <pre><code> {@literal @}SomeAnnotation(value = "someString", types = {SomeType.class, AnotherType.class})
* class SomeAnnotatedClass {}
* </code></pre>
* the results will be
* <pre><code> someAnnotation.get("value") --> "someString"
* someAnnotation.get("types") --> [JavaClass{SomeType}, JavaClass{AnotherType}]
* </code></pre>
*
* @param property The name of the annotation property, i.e. the declared method name
* @return the value of the given property, where the result type is more precisely
* <ul>
* <li>Class<?> --> TypeDetails{clazz}</li>
* <li>Class<?>[] --> [TypeDetails{clazz},...]</li>
* <li>Enum --> JavaEnumConstant</li>
* <li>Enum[] --> [JavaEnumConstant,...]</li>
* <li>anyOtherType --> anyOtherType</li>
* </ul>
*/
@PublicAPI(usage = ACCESS)
public Optional<Object> get(String property) {
return Optional.fromNullable(values.get(property));
}
/**
* @return a map containing all [property --> value], where each value correlates to {@link #get(String property)}
*/
@PublicAPI(usage = ACCESS)
public Map<String, Object> getProperties() {
return values;
}
@PublicAPI(usage = ACCESS)
public <A extends Annotation> A as(Class<A> annotationType) {
return AnnotationProxy.of(annotationType, this);
}
}