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

br.com.objectos.code.AnnotationValueWrapper Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
/*
 * Copyright 2014 Objectos, Fábrica de Software LTDA.
 *
 * 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 br.com.objectos.code;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleAnnotationValueVisitor6;
import javax.lang.model.util.SimpleTypeVisitor6;

import br.com.objectos.core.util.ImmutableSet;

/**
 * @author [email protected] (Marcio Endo)
 */
public class AnnotationValueWrapper {

  private static final Set ERROR_STRING_SET = ImmutableSet.of("", "");

  private final ProcessingEnvironmentWrapper processingEnv;
  private final ExecutableElement element;
  private final AnnotationValue value;

  private AnnotationValueWrapper(ProcessingEnvironmentWrapper processingEnv,
                                 ExecutableElement element,
                                 AnnotationValue value) {
    this.processingEnv = processingEnv;
    this.element = element;
    this.value = value;
  }

  @SuppressWarnings("unchecked")
  public static Stream wrapAll(
      ProcessingEnvironmentWrapper processingEnv, AnnotationMirror annotation) {

    Map valueMap;
    valueMap = processingEnv.getElementValuesWithDefaults(annotation);

    Set set = valueMap.entrySet();
    Set> entrySet;
    entrySet = (Set>) set;

    return entrySet.stream()
        .map(input -> {
          ExecutableElement element = input.getKey();
          AnnotationValue value = input.getValue();
          return wrapperOf(processingEnv, element, value);
        });
  }

  public static AnnotationValueWrapper wrapperOf(
      ProcessingEnvironmentWrapper processingEnv, ExecutableElement element, AnnotationValue value) {
    return new AnnotationValueWrapper(processingEnv, element, value);
  }

  AnnotationValueInfo toAnnotationValueInfo() {
    String name = element.getSimpleName().toString();
    return value.accept(new Visitor(name), name);
  }

  private class Visitor
      extends SimpleAnnotationValueVisitor6
      implements Function {

    String name;
    AnnotationValueKind kind;

    public Visitor(String name) {
      this.name = name;
    }

    @Override
    public AnnotationValueInfo apply(AnnotationValue input) {
      return input.accept(this, name);
    }

    @Override
    public AnnotationValueInfo visitAnnotation(AnnotationMirror type, String p) {
      kind = AnnotationValueKind.ANNOTATION;
      AnnotationMirrorWrapper wrapper = AnnotationMirrorWrapper.wrapperOf(processingEnv, type);
      AnnotationInfo value = wrapper.toAnnotationInfo();
      return AptAnnotationValueInfo.of(p, kind, value);
    }

    @Override
    public AnnotationValueInfo visitArray(List list, String p) {
      List valueInfoList = list.stream()
          .map(this)
          .map(AnnotationValueInfo::value)
          .collect(Collectors.toList());

      if (kind == null) {
        TypeMirror returnType = element.getReturnType();
        kind = returnType.accept(new ArrayVisitor(), AnnotationValueKind.ARRAY_TYPE);
      }

      return AptAnnotationValueInfo.of(p, kind.toArray(), valueInfoList);
    }

    @Override
    public AnnotationValueInfo visitBoolean(boolean b, String p) {
      kind = AnnotationValueKind.PRIMITIVE_BOOLEAN;
      return AptAnnotationValueInfo.of(p, kind, b);
    }

    @Override
    public AnnotationValueInfo visitChar(char c, String p) {
      kind = AnnotationValueKind.PRIMITIVE_CHAR;
      return AptAnnotationValueInfo.of(p, kind, c);
    }

    @Override
    public AnnotationValueInfo visitDouble(double d, String p) {
      kind = AnnotationValueKind.PRIMITIVE_DOUBLE;
      return AptAnnotationValueInfo.of(p, kind, d);
    }

    @Override
    public AnnotationValueInfo visitEnumConstant(VariableElement element, String p) {
      EnumConstantInfo value = EnumConstantInfoVariableElement.wrap(processingEnv, element);
      kind = AnnotationValueKind.ENUM;
      return AptAnnotationValueInfo.of(p, kind, value);
    }

    @Override
    public AnnotationValueInfo visitFloat(float f, String p) {
      kind = AnnotationValueKind.PRIMITIVE_FLOAT;
      return AptAnnotationValueInfo.of(p, kind, f);
    }

    @Override
    public AnnotationValueInfo visitLong(long i, String p) {
      kind = AnnotationValueKind.PRIMITIVE_LONG;
      return AptAnnotationValueInfo.of(p, kind, i);
    }

    @Override
    public AnnotationValueInfo visitInt(int i, String p) {
      kind = AnnotationValueKind.PRIMITIVE_INT;
      return AptAnnotationValueInfo.of(p, kind, i);
    }

    @Override
    public AnnotationValueInfo visitString(String s, String p) {
      // https://github.com/square/dagger/blob/master/compiler/src/main/java/dagger/internal/codegen/Util.java
      if (ERROR_STRING_SET.contains(s)) {
        throw new CodeGenerationIncompleteException();
      }

      kind = AnnotationValueKind.STRING;
      return AptAnnotationValueInfo.of(p, kind, s);
    }

    @Override
    public AnnotationValueInfo visitType(TypeMirror t, String p) {
      kind = AnnotationValueKind.TYPE;
      SimpleTypeInfo typeInfo = SimpleTypeInfoTypeMirror.wrap(processingEnv, t);
      return AptAnnotationValueInfo.of(p, kind, typeInfo);
    }

    @Override
    protected AnnotationValueInfo defaultAction(Object o, String p) {
      kind = AnnotationValueKind.UNKNOWN;
      return AptAnnotationValueInfo.of(p, kind, o);
    }

  }

  private class ArrayVisitor extends SimpleTypeVisitor6 {

    @Override
    public AnnotationValueKind visitArray(ArrayType t, AnnotationValueKind p) {
      TypeMirror componentType = t.getComponentType();
      return componentType.accept(this, p);
    }

    @Override
    public AnnotationValueKind visitDeclared(DeclaredType t, AnnotationValueKind p) {
      Element element = t.asElement();
      ElementKind kind = element.getKind();
      switch (kind) {
      case ANNOTATION_TYPE:
        return AnnotationValueKind.ANNOTATION;

      case CLASS:
      case INTERFACE:
        String qname = processingEnv.getQualifiedName(t);
        return qname.equals("java.lang.String")
            ? AnnotationValueKind.STRING
            : AnnotationValueKind.TYPE;

      case ENUM:
      case ENUM_CONSTANT:
        return AnnotationValueKind.ENUM;

      default:
        return p;

      }
    }

    @Override
    public AnnotationValueKind visitPrimitive(PrimitiveType t, AnnotationValueKind p) {
      return p;
    }

    @Override
    protected AnnotationValueKind defaultAction(TypeMirror e, AnnotationValueKind p) {
      return p;
    }

  }

}