Package net.karneim.pojobuilder.analysis

Source Code of net.karneim.pojobuilder.analysis.DirectivesFactory$HierarchyElement

package net.karneim.pojobuilder.analysis;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import net.karneim.pojobuilder.GeneratePojoBuilder;

public class DirectivesFactory {
  private final Elements elements;
  private final Types types;
  private final JavaModelAnalyzerUtil javaModelAnalyzerUtil;

  public DirectivesFactory(Elements elements, Types types,
      JavaModelAnalyzerUtil javaModelAnalyzerUtil) {
    this.elements = elements;
    this.types = types;
    this.javaModelAnalyzerUtil = javaModelAnalyzerUtil;
  }

  /**
   * Scans the annotation hierarchy of all annotations on the given (annotated) element for the
   * presence of a {@link GeneratePojoBuilder} annotation and returns a {@link Directives} object
   * populated with the aggregation of the specified {@link GeneratePojoBuilder} element values.
   * <p>
   * Please note that element values can be overridden top-down. This means, that element values
   * that appear further up in the source code will be overridden by element values further down.
   *
   * @param annotatedEl the annotated element
   * @param orginatingElements this out parameter will be filled with all elements that have (or
   *        could have) contributed to the {@link Directives} throughout the scan process.
   * @return a {@link Directives} object populated with the effective annotation element values
   */
  public Directives getDirectives(Element annotatedEl, Set<Element> orginatingElements) {
    List<HierarchyElement> hierarchy = findAnnotationHierarchy(annotatedEl);
    List<HierarchyElement> relevantHierarchy = filterByType(hierarchy, GeneratePojoBuilder.class);
    Map<String, Object> valueMap = getValueMap(relevantHierarchy);
    fillOrginatingElements(orginatingElements, hierarchy);
    Directives result = new Directives(valueMap);
    validate(annotatedEl, result);
    return result;
  }

  private List<HierarchyElement> filterByType(List<HierarchyElement> hierarchy,
      Class<GeneratePojoBuilder> class1) {
    TypeMirror generatePojoBuilderAnno =
        elements.getTypeElement(GeneratePojoBuilder.class.getName()).asType();
    List<HierarchyElement> result = new ArrayList<HierarchyElement>();
    for (HierarchyElement el : hierarchy) {
      if (types.isSameType(el.annotation.getAnnotationType(), generatePojoBuilderAnno)) {
        result.add(el);
      }
    }
    return result;
  }

  private void fillOrginatingElements(Set<Element> result, List<HierarchyElement> hierarchy) {
    for (HierarchyElement hierarchyElement : hierarchy) {
      result.add(javaModelAnalyzerUtil.getCompilationUnit(hierarchyElement.annotatedEl));
    }
  }

  private void validate(Element annotatedEl, Directives directives) {
    if (directives.isGenerateBuilderProperties()
        && Void.class.getName().equals(directives.getBuilderInterfaceName())) {
      String message =
          String
              .format("Value for \"withBuilderInterface\" not specified. When setting \"withBuilderProperties\" to true, you must also specifiy a valid builder interface.");
      throw new InvalidElementException(message, annotatedEl);
    }
  }

  private static class HierarchyElement {
    Element annotatedEl;
    AnnotationMirror annotation;

    public HierarchyElement(Element annotatedEl, AnnotationMirror annotation) {
      super();
      this.annotatedEl = annotatedEl;
      this.annotation = annotation;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((annotatedEl == null) ? 0 : annotatedEl.hashCode());
      result = prime * result + ((annotation == null) ? 0 : annotation.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      HierarchyElement other = (HierarchyElement) obj;
      if (annotatedEl == null) {
        if (other.annotatedEl != null)
          return false;
      } else if (!annotatedEl.equals(other.annotatedEl))
        return false;
      if (annotation == null) {
        if (other.annotation != null)
          return false;
      } else if (!annotation.equals(other.annotation))
        return false;
      return true;
    }

    @Override
    public String toString() {
      return "HierarchyElement [annotatedEl=" + annotatedEl + ", annotation=" + annotation + "]";
    }
  }

  private List<HierarchyElement> findAnnotationHierarchy(Element annotatedEl) {
    Set<Element> visitedElements = new HashSet<Element>();
    List<HierarchyElement> result = new LinkedList<HierarchyElement>();
    findAnnotationHierarchy(annotatedEl, result, visitedElements);
    return result;
  }

  private void findAnnotationHierarchy(Element annotatedEl, List<HierarchyElement> result,
      Set<Element> visitedElements) {
    if (!visitedElements.add(annotatedEl)) {
      return;
    }
    List<? extends AnnotationMirror> annos = annotatedEl.getAnnotationMirrors();
    for (AnnotationMirror anno : annos) {
      Element el = anno.getAnnotationType().asElement();
      findAnnotationHierarchy(el, result, visitedElements);
      HierarchyElement hElem = new HierarchyElement(annotatedEl, anno);
      result.add(hElem);
    }
  }

  private boolean isGeneratePojoBuilderAnnotation(AnnotationMirror anno) {
    TypeElement generatePojoBuilderTypeEl =
        elements.getTypeElement(GeneratePojoBuilder.class.getName());
    return types.isSameType(anno.getAnnotationType(), generatePojoBuilderTypeEl.asType());
  }

  private Map<String, Object> getValueMap(List<HierarchyElement> hierarchy) {
    Map<String, Object> result = null;
    for (HierarchyElement el : hierarchy) {
      if (isGeneratePojoBuilderAnnotation(el.annotation)) {
        if (result == null) {
          result = getValueMap(elements.getElementValuesWithDefaults(el.annotation));
        } else {
          result.putAll(getValueMap(el.annotation.getElementValues()));
        }
      }
    }
    return result;
  }

  private Map<String, Object> getValueMap(
      Map<? extends ExecutableElement, ? extends AnnotationValue> values) {
    Map<String, Object> result = new HashMap<String, Object>();
    for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : values
        .entrySet()) {
      String name = entry.getKey().getSimpleName().toString();
      Object value = entry.getValue().getValue();
      if (value instanceof TypeMirror) {
        TypeMirror tm = (TypeMirror) value;
        if (tm.getKind() == TypeKind.DECLARED) {
          DeclaredType declType = (DeclaredType) tm;
          TypeElement elem = (TypeElement) declType.asElement();
          result.put(name, elem.getQualifiedName().toString());
        } else {
          // TODO could this really happen?
          result.put(name, String.valueOf(value));
        }
      } else {
        result.put(name, value);
      }
    }
    return result;
  }

}
TOP

Related Classes of net.karneim.pojobuilder.analysis.DirectivesFactory$HierarchyElement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.