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