1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.signature.cts;
18 
19 import java.lang.reflect.Modifier;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.List;
23 
24 /**
25  * Represents class descriptions loaded from a jdiff xml file.  Used
26  * for CTS SignatureTests.
27  */
28 public class JDiffClassDescription {
29 
30     public enum JDiffType {
31         INTERFACE, CLASS
32     }
33 
34     private final String mPackageName;
35     private final String mShortClassName;
36 
37     /**
38      * Package name + short class name
39      */
40     private final String mAbsoluteClassName;
41 
42     private int mModifier;
43     private boolean mIsPrevious;
44 
45     private String mExtendedClass;
46     private final List<String> implInterfaces = new ArrayList<>();
47     private final List<JDiffField> jDiffFields = new ArrayList<>();
48     private final List<JDiffMethod> jDiffMethods = new ArrayList<>();
49     private final List<JDiffConstructor> jDiffConstructors = new ArrayList<>();
50 
51     private JDiffType mClassType;
52 
53     /**
54      * Creates a new JDiffClassDescription.
55      *
56      * @param pkg the java package this class will end up in.
57      * @param className the name of the class.
58      */
JDiffClassDescription(String pkg, String className)59     public JDiffClassDescription(String pkg, String className) {
60         mPackageName = pkg;
61         mShortClassName = className;
62         mAbsoluteClassName = mPackageName + "." + mShortClassName;
63         mIsPrevious = false;
64     }
65 
setPreviousApiFlag(boolean isPreviousApi)66     public JDiffClassDescription setPreviousApiFlag(boolean isPreviousApi) {
67         mIsPrevious = isPreviousApi;
68         return this;
69     }
70 
isPreviousApi()71     boolean isPreviousApi() {
72         return mIsPrevious;
73     }
74 
getPackageName()75     String getPackageName() {
76         return mPackageName;
77     }
78 
getShortClassName()79     public String getShortClassName() {
80         return mShortClassName;
81     }
82 
getModifier()83     int getModifier() {
84         return mModifier;
85     }
86 
getExtendedClass()87     String getExtendedClass() {
88         return mExtendedClass;
89     }
90 
getImplInterfaces()91     List<String> getImplInterfaces() {
92         return implInterfaces;
93     }
94 
getFields()95     List<JDiffField> getFields() {
96         return jDiffFields;
97     }
98 
getMethods()99     List<JDiffMethod> getMethods() {
100         return jDiffMethods;
101     }
102 
getConstructors()103     List<JDiffConstructor> getConstructors() {
104         return jDiffConstructors;
105     }
106 
getClassType()107     JDiffType getClassType() {
108         return mClassType;
109     }
110 
111     /**
112      * adds implemented interface name.
113      *
114      * @param iname name of interface
115      */
addImplInterface(String iname)116     public void addImplInterface(String iname) {
117         implInterfaces.add(iname);
118     }
119 
120     /**
121      * Adds a field.
122      *
123      * @param field the field to be added.
124      */
addField(JDiffField field)125     public void addField(JDiffField field) {
126         jDiffFields.add(field);
127     }
128 
129     /**
130      * Adds a method.
131      *
132      * @param method the method to be added.
133      */
addMethod(JDiffMethod method)134     public void addMethod(JDiffMethod method) {
135         jDiffMethods.add(method);
136     }
137 
138     /**
139      * Adds a constructor.
140      *
141      * @param tc the constructor to be added.
142      */
addConstructor(JDiffConstructor tc)143     public void addConstructor(JDiffConstructor tc) {
144         jDiffConstructors.add(tc);
145     }
146 
convertModifiersToAccessLevel(int modifiers)147     private static String convertModifiersToAccessLevel(int modifiers) {
148         if ((modifiers & Modifier.PUBLIC) != 0) {
149             return "public";
150         } else if ((modifiers & Modifier.PRIVATE) != 0) {
151             return "private";
152         } else if ((modifiers & Modifier.PROTECTED) != 0) {
153             return "protected";
154         } else {
155             // package protected
156             return "";
157         }
158     }
159 
convertModifersToModifierString(int modifiers)160     private static String convertModifersToModifierString(int modifiers) {
161         StringBuilder sb = new StringBuilder();
162         String separator = "";
163 
164         // order taken from Java Language Spec, sections 8.1.1, 8.3.1, and 8.4.3
165         if ((modifiers & Modifier.ABSTRACT) != 0) {
166             sb.append(separator).append("abstract");
167             separator = " ";
168         }
169         if ((modifiers & Modifier.STATIC) != 0) {
170             sb.append(separator).append("static");
171             separator = " ";
172         }
173         if ((modifiers & Modifier.FINAL) != 0) {
174             sb.append(separator).append("final");
175             separator = " ";
176         }
177         if ((modifiers & Modifier.TRANSIENT) != 0) {
178             sb.append(separator).append("transient");
179             separator = " ";
180         }
181         if ((modifiers & Modifier.VOLATILE) != 0) {
182             sb.append(separator).append("volatile");
183             separator = " ";
184         }
185         if ((modifiers & Modifier.SYNCHRONIZED) != 0) {
186             sb.append(separator).append("synchronized");
187             separator = " ";
188         }
189         if ((modifiers & Modifier.NATIVE) != 0) {
190             sb.append(separator).append("native");
191             separator = " ";
192         }
193         if ((modifiers & Modifier.STRICT) != 0) {
194             sb.append(separator).append("strictfp");
195         }
196 
197         return sb.toString();
198     }
199 
200     abstract static class JDiffElement {
201         final String mName;
202         int mModifier;
203 
JDiffElement(String name, int modifier)204         JDiffElement(String name, int modifier) {
205             mName = name;
206             mModifier = modifier;
207         }
208     }
209 
210     /**
211      * Represents a  field.
212      */
213     public static final class JDiffField extends JDiffElement {
214         final String mFieldType;
215         private final String mFieldValue;
216 
JDiffField(String name, String fieldType, int modifier, String value)217         public JDiffField(String name, String fieldType, int modifier, String value) {
218             super(name, modifier);
219 
220             mFieldType = fieldType;
221             mFieldValue = value;
222         }
223 
224         /**
225          * A string representation of the value within the field.
226          */
getValueString()227         public String getValueString() {
228             return mFieldValue;
229         }
230 
231         /**
232          * Make a readable string according to the class name specified.
233          *
234          * @param className The specified class name.
235          * @return A readable string to represent this field along with the class name.
236          */
toReadableString(String className)237         String toReadableString(String className) {
238             return className + "#" + mName + "(" + mFieldType + ")";
239         }
240 
toSignatureString()241         public String toSignatureString() {
242             StringBuilder sb = new StringBuilder();
243 
244             // access level
245             String accesLevel = convertModifiersToAccessLevel(mModifier);
246             if (!"".equals(accesLevel)) {
247                 sb.append(accesLevel).append(" ");
248             }
249 
250             String modifierString = convertModifersToModifierString(mModifier);
251             if (!"".equals(modifierString)) {
252                 sb.append(modifierString).append(" ");
253             }
254 
255             sb.append(mFieldType).append(" ");
256 
257             sb.append(mName);
258 
259             return sb.toString();
260         }
261     }
262 
263     /**
264      * Represents a method.
265      */
266     public static class JDiffMethod extends JDiffElement {
267         final String mReturnType;
268         final ArrayList<String> mParamList;
269         final ArrayList<String> mExceptionList;
270 
JDiffMethod(String name, int modifier, String returnType)271         public JDiffMethod(String name, int modifier, String returnType) {
272             super(name, modifier);
273 
274             if (returnType == null) {
275                 mReturnType = "void";
276             } else {
277                 mReturnType = scrubJdiffParamType(returnType);
278             }
279 
280             mParamList = new ArrayList<>();
281             mExceptionList = new ArrayList<>();
282         }
283 
284         /**
285          * Adds a parameter.
286          *
287          * @param param parameter type
288          */
addParam(String param)289         public void addParam(String param) {
290             mParamList.add(scrubJdiffParamType(param));
291         }
292 
293         /**
294          * Adds an exception.
295          *
296          * @param exceptionName name of exception
297          */
addException(String exceptionName)298         public void addException(String exceptionName) {
299             mExceptionList.add(exceptionName);
300         }
301 
302         /**
303          * Makes a readable string according to the class name specified.
304          *
305          * @param className The specified class name.
306          * @return A readable string to represent this method along with the class name.
307          */
toReadableString(String className)308         String toReadableString(String className) {
309             return className + "#" + mName + "(" + convertParamList(mParamList) + ")";
310         }
311 
312         /**
313          * Converts a parameter array to a string
314          *
315          * @param params the array to convert
316          * @return converted parameter string
317          */
convertParamList(final ArrayList<String> params)318         private static String convertParamList(final ArrayList<String> params) {
319 
320             StringBuilder paramList = new StringBuilder();
321 
322             if (params != null) {
323                 for (String str : params) {
324                     paramList.append(str).append(", ");
325                 }
326                 if (params.size() > 0) {
327                     paramList.delete(paramList.length() - 2, paramList.length());
328                 }
329             }
330 
331             return paramList.toString();
332         }
333 
toSignatureString()334         public String toSignatureString() {
335             StringBuilder sb = new StringBuilder();
336 
337             // access level
338             String accessLevel = convertModifiersToAccessLevel(mModifier);
339             if (!"".equals(accessLevel)) {
340                 sb.append(accessLevel).append(" ");
341             }
342 
343             String modifierString = convertModifersToModifierString(mModifier);
344             if (!"".equals(modifierString)) {
345                 sb.append(modifierString).append(" ");
346             }
347 
348             String returnType = getReturnType();
349             if (!"".equals(returnType)) {
350                 sb.append(returnType).append(" ");
351             }
352 
353             sb.append(mName);
354             sb.append("(");
355             for (int x = 0; x < mParamList.size(); x++) {
356                 sb.append(mParamList.get(x));
357                 if (x + 1 != mParamList.size()) {
358                     sb.append(", ");
359                 }
360             }
361             sb.append(")");
362 
363             // does it throw?
364             if (mExceptionList.size() > 0) {
365                 sb.append(" throws ");
366                 for (int x = 0; x < mExceptionList.size(); x++) {
367                     sb.append(mExceptionList.get(x));
368                     if (x + 1 != mExceptionList.size()) {
369                         sb.append(", ");
370                     }
371                 }
372             }
373 
374             return sb.toString();
375         }
376 
377         /**
378          * Gets the return type.
379          *
380          * @return the return type of this method.
381          */
getReturnType()382         String getReturnType() {
383             return mReturnType;
384         }
385     }
386 
387     /**
388      * Represents a constructor.
389      */
390     public static final class JDiffConstructor extends JDiffMethod {
JDiffConstructor(String name, int modifier)391         public JDiffConstructor(String name, int modifier) {
392             super(name, modifier, null);
393         }
394 
395         /**
396          * Gets the return type.
397          *
398          * @return the return type of this method.
399          */
400         @Override
getReturnType()401         protected String getReturnType() {
402             // Constructors have no return type.
403             return "";
404         }
405     }
406 
407     /**
408      * Gets the list of fields found within this class.
409      *
410      * @return the list of fields.
411      */
getFieldList()412     public Collection<JDiffField> getFieldList() {
413         return jDiffFields;
414     }
415 
416     /**
417      * Convert the class into a printable signature string.
418      *
419      * @return the signature string
420      */
toSignatureString()421     public String toSignatureString() {
422         StringBuilder sb = new StringBuilder();
423 
424         String accessLevel = convertModifiersToAccessLevel(mModifier);
425         if (!"".equals(accessLevel)) {
426             sb.append(accessLevel).append(" ");
427         }
428         if (!JDiffType.INTERFACE.equals(mClassType)) {
429             String modifierString = convertModifersToModifierString(mModifier);
430             if (!"".equals(modifierString)) {
431                 sb.append(modifierString).append(" ");
432             }
433             sb.append("class ");
434         } else {
435             sb.append("interface ");
436         }
437         // class name
438         sb.append(mShortClassName);
439 
440         // does it extends something?
441         if (mExtendedClass != null) {
442             sb.append(" extends ").append(mExtendedClass).append(" ");
443         }
444 
445         // implements something?
446         if (implInterfaces.size() > 0) {
447             sb.append(" implements ");
448             for (int x = 0; x < implInterfaces.size(); x++) {
449                 String interf = implInterfaces.get(x);
450                 sb.append(interf);
451                 // if not last elements
452                 if (x + 1 != implInterfaces.size()) {
453                     sb.append(", ");
454                 }
455             }
456         }
457         return sb.toString();
458     }
459 
460     /**
461      * Sees if the class under test is actually an enum.
462      *
463      * @return true if this class is enum
464      */
isEnumType()465     boolean isEnumType() {
466         return "java.lang.Enum".equals(mExtendedClass);
467     }
468 
469     /**
470      * Sees if the class under test is actually an annotation.
471      *
472      * @return true if this class is Annotation.
473      */
isAnnotation()474     boolean isAnnotation() {
475         return implInterfaces.contains("java.lang.annotation.Annotation");
476     }
477 
478     /**
479      * Gets the class name for the class under test.
480      *
481      * @return the class name.
482      */
getClassName()483     String getClassName() {
484         return mShortClassName;
485     }
486 
487     /**
488      * Gets the package name + short class name
489      *
490      * @return The package + short class name
491      */
getAbsoluteClassName()492     public String getAbsoluteClassName() {
493         return mAbsoluteClassName;
494     }
495 
496     /**
497      * Sets the modifier for the class under test.
498      *
499      * @param modifier the modifier
500      */
setModifier(int modifier)501     public void setModifier(int modifier) {
502         mModifier = modifier;
503     }
504 
505     /**
506      * Sets the return type for the class under test.
507      *
508      * @param type the return type
509      */
setType(JDiffType type)510     public void setType(JDiffType type) {
511         mClassType = type;
512     }
513 
514     /**
515      * Sets the class that is beign extended for the class under test.
516      *
517      * @param extendsClass the class being extended.
518      */
setExtendsClass(String extendsClass)519     void setExtendsClass(String extendsClass) {
520         mExtendedClass = extendsClass;
521     }
522 
523     /**
524      * Cleans up jdiff parameters to canonicalize them.
525      *
526      * @param paramType the parameter from jdiff.
527      * @return the scrubbed version of the parameter.
528      */
scrubJdiffParamType(String paramType)529     private static String scrubJdiffParamType(String paramType) {
530         // <? extends java.lang.Object and <?> are the same, so
531         // canonicalize them to one form.
532         return paramType
533             .replace("? extends java.lang.Object", "?")
534             .replace("? super java.lang.Object", "? super ?");
535     }
536 
537     @Override
toString()538     public String toString() {
539         return mAbsoluteClassName;
540     }
541 }
542