AptAssignabilityUtils.java

  1. /*
  2.  * Copyright © 2017, Salesforce.com, Inc
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions are met:
  7.  *     * Redistributions of source code must retain the above copyright
  8.  *       notice, this list of conditions and the following disclaimer.
  9.  *     * Redistributions in binary form must reproduce the above copyright
  10.  *       notice, this list of conditions and the following disclaimer in the
  11.  *       documentation and/or other materials provided with the distribution.
  12.  *     * Neither the name of the <organization> nor the
  13.  *       names of its contributors may be used to endorse or promote products
  14.  *       derived from this software without specific prior written permission.
  15.  *
  16.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19.  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  20.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26.  */
  27. package com.salesforce.apt.graph.types.impl;

  28. import javax.lang.model.element.ExecutableElement;
  29. import javax.lang.model.element.TypeElement;
  30. import javax.lang.model.type.TypeMirror;
  31. import javax.lang.model.util.Elements;
  32. import javax.lang.model.util.Types;

  33. import com.salesforce.apt.graph.model.InstanceModel;
  34. import com.salesforce.apt.graph.naming.NamingTools;
  35. import com.salesforce.apt.graph.types.AssignabilityUtils;

  36. /**
  37.  * Compares types stored in the DefinitionGraph with the best available utils, Types and Elements in the case of apt.
  38.  */
  39. public class AptAssignabilityUtils implements AssignabilityUtils {

  40.   private Types typeUtils;
  41.   private Elements elementUtils;
  42.  
  43.   public AptAssignabilityUtils(Types types, Elements elements) {
  44.     typeUtils = types;
  45.     elementUtils = elements;
  46.   }
  47.  
  48.   /**
  49.    * Given two instances models, the subject and target, it is determined
  50.    * if the subject can be assigned to the dependency on the subject, from the
  51.    * target.
  52.    *
  53.    * @param subject instance to be injected in to the target
  54.    * @param target instance to have the subject injected in to it.
  55.    * @return whether the target can be safely injected in to the correct parameter of the target.
  56.    */
  57.   public boolean isAssignableFrom(InstanceModel subject, InstanceModel target) {
  58.     ExecutableElement factoryOrConstructor = lookUpElement(subject);
  59.     TypeMirror subjectElementType = null;
  60.     if ("<init>".equals(factoryOrConstructor.getSimpleName().toString())) {
  61.       subjectElementType = factoryOrConstructor.getEnclosingElement().asType();
  62.     } else {
  63.       subjectElementType = factoryOrConstructor.getReturnType();
  64.     }
  65.     int count = 0;
  66.     while (!target.getDependencies().get(count).getIdentity().equals(subject.getIdentity())) {
  67.       count ++;
  68.     }
  69.     TypeMirror targetElementType = lookUpElement(target).getParameters().get(count).asType();
  70.     return typeUtils.isAssignable(subjectElementType, targetElementType);
  71.   }
  72.  
  73.   /**
  74.    * Find's the executable element that will have the necessary type
  75.    * information extracted from it.
  76.    *
  77.    * @param target the instance model that is our target
  78.    * @return the element in question
  79.    */
  80.   public ExecutableElement lookUpElement(final InstanceModel target) {
  81.     //Prior I had used the cached instances of the source element, since idea reuses compiler instances,
  82.     //and hence AptProcessor instances that proves dangerous as the Type references for the same class/type
  83.     //are not portable across different compilation rounds.
  84.     //
  85.     // Leaving this as a warning to my future self.
  86.     //
  87.     //if (target.getSourceElement().isPresent()) {
  88.     //  return (ExecutableElement) target.getSourceElement().get();
  89.     //} else {
  90.     TypeElement type = elementUtils.getTypeElement(target.getOwningDefinition().replace('$', '.'));
  91.     final NamingTools names = new NamingTools();
  92.     return type.getEnclosedElements().stream().filter(e ->
  93.         names.elementToName(e).equals(target.getElementLocation()) && ExecutableElement.class.isAssignableFrom(e.getClass()))
  94.           .findFirst()
  95.           .map(e -> (ExecutableElement) e)
  96.           .get();
  97.   }  
  98.  
  99.   /* If the above doesn't work, this handles all but ?, &, | in types.
  100.    *
  101.   public boolean isAssignableFrom(String subject, String target) {
  102.     ParseType subjectType = ParseType.parse(subject);
  103.     ParseType targetType = ParseType.parse(target);
  104.     return typeUtils.isAssignable(from(subjectType),
  105.         from(targetType));
  106.   }
  107.  
  108.   public TypeMirror from(ParseType parsed) {
  109.     return typeUtils.getDeclaredType(elementUtils.getTypeElement(parsed.getType()),
  110.         parsed.getParameters().stream().map(p -> from(p)).toArray(i -> new TypeMirror[i]));
  111.   }
  112.   */
  113. }