/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.impl.metamodel;
import juzu.impl.compiler.ElementHandle;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Captures the precise state of an annotation in a serializable object. The content of the map are the value declared
* by the annotation, however the state keeps also track of the default values and those can be queried using the
* {@link #resolve(String)} method.
*
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public class AnnotationState extends HashMap<String, Serializable> {
/** Set indicating which member were really declared in the annotation (i.e the default values). */
private HashMap<String, Serializable> undeclared;
/**
* Returns the annotation member if present, otherwise the default value.
*
* @param key the member key
* @return the serializable value
*/
public Serializable resolve(String key) {
Serializable value = get(key);
if (value == null && undeclared != null) {
value = undeclared.get(key);
}
return value;
}
/**
* Return true if the member is present.
*
* @param key the member key
* @return true if the annotation declared the specified member
*/
public boolean isDeclared(String key) {
return containsKey(key);
}
/**
* Return true if the member is not declared.
*
* @param key the member key
* @return true if the annotation does not declare the specified member
*/
public boolean isUndeclared(String key) {
return undeclared == null || undeclared.containsKey(key);
}
/**
* Get an annotation state from the specified element and annotation type element, or null when
* it cannot be found.
*
* @param element the element
* @param annotationType the annotation type
* @return the annotation state
* @throws NullPointerException
*/
public static AnnotationState get(Element element, TypeMirror annotationType) throws NullPointerException {
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
if (annotation.getAnnotationType().equals(annotationType)) {
return AnnotationState.create(annotation);
}
}
return null;
}
public static AnnotationState create(AnnotationMirror annotation) throws NullPointerException {
if (annotation == null) {
throw new NullPointerException("No null annotation allowed");
}
//
AnnotationState state = new AnnotationState();
//
TypeElement annotationTypeElement = (TypeElement)annotation.getAnnotationType().asElement();
Map<? extends ExecutableElement, ? extends AnnotationValue> values = annotation.getElementValues();
for (Element member : annotationTypeElement.getEnclosedElements()) {
if (member instanceof ExecutableElement) {
ExecutableElement xMember = (ExecutableElement)member;
AnnotationValue value = values.get(xMember);
String key = xMember.getSimpleName().toString();
HashMap<String, Serializable> target;
if (value == null) {
if (state.undeclared == null) {
state.undeclared = new HashMap<String, Serializable>();
}
target = state.undeclared;
value = xMember.getDefaultValue();
} else {
target = state;
}
if (value != null) {
Serializable serialized = unwrap(value, xMember.getReturnType());
target.put(key, serialized);
}
}
}
//
return state;
}
private static Serializable unwrap(Object value, TypeMirror type) {
if (value instanceof AnnotationValue) {
value = ((AnnotationValue)value).getValue();
}
//
if (type instanceof ArrayType) {
TypeMirror componentType = ((ArrayType)type).getComponentType();
if (value instanceof List) {
List<?> array = (List<?>)value;
if (array.size() == 0) {
// Need to force the cast, javadoc says it is serializable
return (Serializable)Collections.<Serializable>emptyList();
} else {
ArrayList<Object> list = new ArrayList<Object>(array.size());
for (Object element : array) {
list.add(unwrap(element, componentType));
}
return list;
}
}
else {
throw new UnsupportedOperationException("Impossible ? " + value + " " + value.getClass().getName());
}
}
else if (value instanceof VariableElement) {
return ((VariableElement)value).getSimpleName().toString();
}
else if (value instanceof DeclaredType) {
return ElementHandle.Type.create((TypeElement)((DeclaredType)value).asElement());
}
else if (value instanceof AnnotationMirror) {
return create((AnnotationMirror)value);
}
else if (value instanceof Serializable) {
return (Serializable)value;
}
else {
throw new UnsupportedOperationException("Need to unwrap not serializable type " + value + " " +
value.getClass().getName());
}
}
}