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

io.sundr.adapter.apt.AnnotationMirrorToAnnotationRef Maven / Gradle / Ivy

/**
 * Copyright 2015 The original authors.
 * 
 * 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 io.sundr.adapter.apt;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeMirror;

import io.sundr.SundrException;
import io.sundr.adapter.apt.visitors.TypeRefTypeVisitor;
import io.sundr.model.AnnotationRef;
import io.sundr.model.AnnotationRefBuilder;
import io.sundr.model.ClassRef;
import io.sundr.model.TypeRef;
import io.sundr.model.utils.Types;

public class AnnotationMirrorToAnnotationRef implements Function {

  private static final String EMPTY_PARENTHESIS = "()";
  private static final String EMPTY = "";
  private static final String ERROR = "";

  private final AptContext context;
  private final Function referenceAdapterFunction;

  public AnnotationMirrorToAnnotationRef(AptContext context, Function referenceAdapterFunction) {
    this.context = context;
    this.referenceAdapterFunction = referenceAdapterFunction;
  }

  @Override
  public AnnotationRef apply(AnnotationMirror item) {
    TypeRef annotationType = item.getAnnotationType().accept(new TypeRefTypeVisitor(context), 0);
    Map parameters = new HashMap();
    if (annotationType instanceof ClassRef) {
      for (Map.Entry entry : item.getElementValues()
          .entrySet()) {
        checkEntry(entry);
        String key = entry.getKey().toString().replace(EMPTY_PARENTHESIS, EMPTY);
        Object value = mapAnnotationValue(entry.getValue().getValue());
        parameters.put(key, value);
      }
      return new AnnotationRefBuilder().withClassRef((ClassRef) annotationType).withParameters(parameters).build();
    }
    throw new IllegalStateException("Annotation type: [" + annotationType + "] is not a class reference.");
  }

  private Object mapAnnotationValue(Object value) {
    if (value instanceof Collection) {
      List list = (List) ((Collection) value).stream().map(this::mapAnnotationValue).collect(Collectors.toList());
      if (list.isEmpty()) {
        return null;
      }
      return toArray(list);
    } else if (value instanceof AnnotationMirror) {
      return apply((AnnotationMirror) value);
    } else if (value instanceof AnnotationValue) {
      return ((AnnotationValue) value).getValue();
    } else if (value instanceof TypeMirror) {
      return referenceAdapterFunction.apply((TypeMirror) value);
    } else {
      return value;
    }
  }

  /**
   * Checks if there is error extracting the specified annotation parameter
   * 
   * @param entry the entry that represents the annotation param.
   */
  private void checkEntry(Map.Entry entry) {
    ExecutableElement key = entry.getKey();
    Object value = entry.getValue().getValue();
    if (ERROR.equals(value)) {
      TypeRef returnType = referenceAdapterFunction.apply(key.getReturnType());
      if (returnType.equals(Types.CLASS)) {
        throw new SundrException("Failed to extract class parameter from annotation. " + Messages.POTENTIAL_UNRESOLVED_SYMBOL);
      }
    }
  }

  /**
   * Convert the specified {@link List} into an array.
   * 
   * @return an {@link Object} instance that holds the array.
   */
  private static Object toArray(List list) {
    if (list == null || list.size() == 0) {
      return null;
    }
    try {
      Class type = toPrimitive(list.get(0).getClass());
      Object result = Array.newInstance(type, list.size());
      for (int i = 0; i < list.size(); i++) {
        Array.set(result, i, list.get(i));
      }
      return result;
    } catch (Exception e) {
      e.printStackTrace();
      throw new SundrException(e);
    }
  }

  /**
   * Converts boxed type into into its primitve equivalent.
   * This method also supports {@link com.sun.tools.javac.code.Symbol$VarSymbol} which are used internally and that makes the
   * method extremely falky.
   * 
   * @return a matching primitve or the input type if no match was found.
   */
  private static Class toPrimitive(Class t) {
    String s = t.toString(); //We need to compare using toString() to cover `Symbol$VarSymbol`.
    if (s.contains(Boolean.class.getName())) {
      return boolean.class;
    }
    if (s.contains(Character.class.getName())) {
      return char.class;
    }
    if (s.contains(Short.class.getName())) {
      return short.class;
    }
    if (s.contains(Integer.class.getName())) {
      return int.class;
    }
    if (s.contains(Long.class.getName())) {
      return long.class;
    }
    if (s.contains(Double.class.getName())) {
      return double.class;
    }
    if (s.contains(Float.class.getName())) {
      return float.class;
    }
    return t;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy