All Downloads are FREE. Search and download functionalities are using the official Maven repository.

juzu.impl.metamodel.AnnotationState Maven / Gradle / Ivy

/*
 * 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 Julien Viet
 */
public class AnnotationState extends HashMap {

  /** Set indicating which member were really declared in the annotation (i.e the default values). */
  private HashMap 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 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 target;
        if (value == null) {
          if (state.undeclared == null) {
            state.undeclared = new HashMap();
          }
          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.emptyList();
        } else {
          ArrayList list = new ArrayList(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());
    }
  }
}