View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transformer;
8   
9   import org.mule.api.transformer.DataType;
10  import org.mule.api.transformer.DiscoverableTransformer;
11  import org.mule.api.transformer.Transformer;
12  
13  import java.util.List;
14  
15  import org.apache.commons.beanutils.MethodUtils;
16  
17  /**
18   * Given a {@link org.mule.api.transformer.Transformer} instance, an input class and output class
19   * this object will create a weighting for a transformer. This weighthing can be used compare one transformer with
20   * another, which can be useful for choosing a transformer to use given the input class and required output class.
21   */
22  public class TransformerWeighting implements Comparable
23  {
24      private Transformer transformer;
25      private int inputWeighting;
26      private int outputWeighting;
27      private Class inputClass;
28      private Class outputClass;
29  
30      public TransformerWeighting(Class inputClass, Class outputClass, Transformer transformer)
31      {
32          this.inputClass = inputClass;
33          this.outputClass = outputClass;
34          this.transformer = transformer;
35          init();
36      }
37  
38      private void init()
39      {
40          inputWeighting = Integer.MAX_VALUE;
41          List<DataType<?>> sourceTypes = transformer.getSourceDataTypes();
42  
43          for (DataType type : sourceTypes)
44          {
45              int weighting = getWeighting(-1, inputClass, type.getType());
46              if (weighting < inputWeighting && weighting != -1)
47              {
48                  inputWeighting = weighting;
49              }
50          }
51  
52          outputWeighting = getWeighting(-1, outputClass, transformer.getReturnClass());
53  
54          inputWeighting = (inputWeighting == Integer.MAX_VALUE ? -1 : inputWeighting);
55          outputWeighting = (outputWeighting == Integer.MAX_VALUE ? -1 : outputWeighting);
56  
57      }
58  
59      /**
60       * This is a very basic algorithm for creating a match rating for two classes. An offset weighting
61       * can also be passed in. Where w is weighting,
62       * if the classes are not assignable but the src is a primitive type, the w for the corresponding object type will be returned, otherwise return -1
63       * if the classes match exactly and dest is Object then w+3 is returned
64       * if the classes match exactly and dest is not Object then w+1 is returned
65       * if the classes are assignable and there is a direct equality to an interface on the class, w+2  is returned,
66       * If there a super class, that will get matched using the above criteria but using w = w + 1
67       * If there is no match -1 is returned
68       *
69       * @param weighting an offset weighting, by default -1 should be used
70       * @param src the src class being matched
71       * @param dest the destination class to match to
72       * @return a weighting where 0 would be an exact match, -1 would be no match and a positive integer that defines how close the match is
73       */
74      protected int getWeighting(int weighting, Class src, Class dest)
75      {
76          if (!dest.isAssignableFrom(src))
77          {
78              if (src.isPrimitive())
79              {
80                  return getWeighting(weighting, MethodUtils.getPrimitiveWrapper(src), dest);
81              }
82  
83              return -1;
84          }
85  
86          if (dest.equals(src))
87          {
88              if (dest.equals(Object.class))
89              {
90                  return weighting + 3;
91              }
92              else
93              {
94                  return weighting + 1;
95              }
96          }
97  
98          if (dest.isInterface() && src.getInterfaces().length > 0)
99          {
100             for (int i = 0; i < src.getInterfaces().length; i++)
101             {
102                 Class aClass = src.getInterfaces()[i];
103                 if (dest.equals(aClass))
104                 {
105                     return weighting + 2;
106                 }
107             }
108         }
109 
110         if (src.getSuperclass() != null)
111         {
112             return getWeighting(weighting + 1, src.getSuperclass(), dest);
113         }
114 
115         return -1;
116     }
117 
118     public Class getInputClass()
119     {
120         return inputClass;
121     }
122 
123     public int getInputWeighting()
124     {
125         return inputWeighting;
126     }
127 
128     public Class getOutputClass()
129     {
130         return outputClass;
131     }
132 
133     public int getOutputWeighting()
134     {
135         return outputWeighting;
136     }
137 
138     public Transformer getTransformer()
139     {
140         return transformer;
141     }
142 
143     public boolean isExactMatch()
144     {
145         return inputWeighting == 0 && outputWeighting == 0;
146     }
147 
148     public boolean isNotMatch()
149     {
150         return inputWeighting == -1 || outputWeighting == -1;
151     }
152 
153     public int compareTo(Object o)
154     {
155         TransformerWeighting weighting = (TransformerWeighting) o;
156         if (weighting.getInputWeighting() == getInputWeighting() &&
157                 weighting.getOutputWeighting() == getOutputWeighting())
158         {
159             //We only check the weighting if we have an exact match
160             //These transformers should always implement DiscoverableTransformer, but jic we check here
161             if (weighting.getTransformer() instanceof DiscoverableTransformer
162                     && this.getTransformer() instanceof DiscoverableTransformer)
163             {
164                 int x = ((DiscoverableTransformer) weighting.getTransformer()).getPriorityWeighting();
165                 int y = ((DiscoverableTransformer) this.getTransformer()).getPriorityWeighting();
166                 if (x > y)
167                 {
168                     return -1;
169                 }
170                 if (x < y)
171                 {
172                     return 1;
173                 }
174                 return 0;
175             }
176             else
177             {
178                 return 0;
179             }
180         }
181         else
182         {
183             if (isNotMatch())
184             {
185                 return -1;
186             }
187             else if (weighting.isNotMatch() && !isNotMatch())
188             {
189                 return 1;
190             }
191             else if (weighting.isExactMatch() && !isExactMatch())
192             {
193                 return -1;
194             }
195             else if (weighting.getInputWeighting() < getInputWeighting() &&
196                     weighting.getOutputWeighting() < getOutputWeighting())
197             {
198                 return -1;
199             }
200             //If the outputWeighting is closer to 0 its a better match
201             else if (weighting.getInputWeighting() == getInputWeighting() &&
202                     weighting.getOutputWeighting() < getOutputWeighting())
203             {
204                 return -1;
205             }
206 
207             else if (weighting.getInputWeighting() < getInputWeighting() &&
208                     weighting.getOutputWeighting() == getOutputWeighting())
209             {
210                 return -1;
211             }
212             return 1;
213         }
214     }
215 
216     public boolean equals(Object o)
217     {
218         if (this == o)
219         {
220             return true;
221         }
222         if (o == null || getClass() != o.getClass())
223         {
224             return false;
225         }
226 
227         TransformerWeighting that = (TransformerWeighting) o;
228 
229         if (inputClass != null ? !inputClass.equals(that.inputClass) : that.inputClass != null)
230         {
231             return false;
232         }
233         if (outputClass != null ? !outputClass.equals(that.outputClass) : that.outputClass != null)
234         {
235             return false;
236         }
237 
238         return true;
239     }
240 
241     public int hashCode()
242     {
243         int result;
244         result = (transformer != null ? transformer.hashCode() : 0);
245         result = 31 * result + inputWeighting;
246         result = 31 * result + outputWeighting;
247         result = 31 * result + (inputClass != null ? inputClass.hashCode() : 0);
248         result = 31 * result + (outputClass != null ? outputClass.hashCode() : 0);
249         return result;
250     }
251 
252 
253     public String toString()
254     {
255         final StringBuffer sb = new StringBuffer();
256         sb.append("TransformerWeighting");
257         sb.append("{inputClass=").append(inputClass);
258         sb.append(", inputWeighting=").append(inputWeighting);
259         sb.append(", outputClass=").append(outputClass);
260         sb.append(", outputWeighting=").append(outputWeighting);
261         sb.append(", transformer=").append(transformer.getName());
262         sb.append('}');
263         return sb.toString();
264     }
265 }