1 /*
2  * Copyright (C) 2010 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 com.android.cts.apicoverage;
18 
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.List;
23 
24 /** Representation of a class in the API with constructors and methods. */
25 class ApiClass implements Comparable<ApiClass>, HasCoverage {
26 
27     private static final String VOID = "void";
28 
29     private final String mName;
30 
31     private final boolean mDeprecated;
32 
33     private final boolean mAbstract;
34 
35     private final List<ApiConstructor> mApiConstructors = new ArrayList<ApiConstructor>();
36 
37     private final List<ApiMethod> mApiMethods = new ArrayList<ApiMethod>();
38 
39     private final String mSuperClassName;
40 
41     private ApiClass mSuperClass;
42 
43     /**
44      * @param name The name of the class
45      * @param deprecated true iff the class is marked as deprecated
46      * @param classAbstract true iff the class is abstract
47      * @param superClassName The fully qualified name of the super class
48      */
ApiClass( String name, boolean deprecated, boolean classAbstract, String superClassName)49     ApiClass(
50             String name,
51             boolean deprecated,
52             boolean classAbstract,
53             String superClassName) {
54         mName = name;
55         mDeprecated = deprecated;
56         mAbstract = classAbstract;
57         mSuperClassName = superClassName;
58     }
59 
60     @Override
compareTo(ApiClass another)61     public int compareTo(ApiClass another) {
62         return mName.compareTo(another.mName);
63     }
64 
65     @Override
getName()66     public String getName() {
67         return mName;
68     }
69 
isDeprecated()70     public boolean isDeprecated() {
71         return mDeprecated;
72     }
73 
getSuperClassName()74     public String getSuperClassName() {
75         return mSuperClassName;
76     }
77 
isAbstract()78     public boolean isAbstract() {
79         return mAbstract;
80     }
81 
setSuperClass(ApiClass superClass)82     public void setSuperClass(ApiClass superClass) { mSuperClass = superClass; }
83 
addConstructor(ApiConstructor constructor)84     public void addConstructor(ApiConstructor constructor) {
85         mApiConstructors.add(constructor);
86     }
87 
88 
getConstructors()89     public Collection<ApiConstructor> getConstructors() {
90         return Collections.unmodifiableList(mApiConstructors);
91     }
92 
addMethod(ApiMethod method)93     public void addMethod(ApiMethod method) {
94         mApiMethods.add(method);
95     }
96 
97     /** Look for a matching constructor and mark it as covered */
markConstructorCovered(List<String> parameterTypes, String coveredbyApk)98     public void markConstructorCovered(List<String> parameterTypes, String coveredbyApk) {
99         if (mSuperClass != null) {
100             // Mark matching constructors in the superclass
101             mSuperClass.markConstructorCovered(parameterTypes, coveredbyApk);
102         }
103         ApiConstructor apiConstructor = getConstructor(parameterTypes);
104         if (apiConstructor != null) {
105             apiConstructor.setCovered(coveredbyApk);
106         }
107 
108     }
109 
110     /** Look for a matching method and if found and mark it as covered */
markMethodCovered( String name, List<String> parameterTypes, String returnType, String coveredbyApk)111     public void markMethodCovered(
112             String name, List<String> parameterTypes, String returnType, String coveredbyApk) {
113         if (mSuperClass != null) {
114             // Mark matching methods in the super class
115             mSuperClass.markMethodCovered(name, parameterTypes, returnType, coveredbyApk);
116         }
117         ApiMethod apiMethod = getMethod(name, parameterTypes, returnType);
118         if (apiMethod != null) {
119             apiMethod.setCovered(coveredbyApk);
120         }
121     }
122 
getMethods()123     public Collection<ApiMethod> getMethods() {
124         return Collections.unmodifiableList(mApiMethods);
125     }
126 
getNumCoveredMethods()127     public int getNumCoveredMethods() {
128         int numCovered = 0;
129         for (ApiConstructor constructor : mApiConstructors) {
130             if (constructor.isCovered()) {
131                 numCovered++;
132             }
133         }
134         for (ApiMethod method : mApiMethods) {
135             if (method.isCovered()) {
136                 numCovered++;
137             }
138         }
139         return numCovered;
140     }
141 
getTotalMethods()142     public int getTotalMethods() {
143         return mApiConstructors.size() + mApiMethods.size();
144     }
145 
146     @Override
getCoveragePercentage()147     public float getCoveragePercentage() {
148         if (getTotalMethods() == 0) {
149             return 100;
150         } else {
151             return (float) getNumCoveredMethods() / getTotalMethods() * 100;
152         }
153     }
154 
155     @Override
getMemberSize()156     public int getMemberSize() {
157         return getTotalMethods();
158     }
159 
getMethod(String name, List<String> parameterTypes, String returnType)160     private ApiMethod getMethod(String name, List<String> parameterTypes, String returnType) {
161         for (ApiMethod method : mApiMethods) {
162             boolean methodNameMatch = name.equals(method.getName());
163             boolean parameterTypeMatch =
164                     compareParameterTypes(method.getParameterTypes(), parameterTypes);
165             boolean returnTypeMatch = compareType(method.getReturnType(), returnType);
166             if (methodNameMatch && parameterTypeMatch && returnTypeMatch) {
167                 return method;
168             }
169         }
170         return null;
171     }
172 
173     /**
174      * The method compares two lists of parameters. If the {@code apiParameterTypeList} contains
175      * generic types, test parameter types are ignored.
176      *
177      * @param apiParameterTypeList The list of parameter types from the API
178      * @param testParameterTypeList The list of parameter types used in a test
179      * @return true iff the list of types are the same.
180      */
compareParameterTypes( List<String> apiParameterTypeList, List<String> testParameterTypeList)181     private static boolean compareParameterTypes(
182             List<String> apiParameterTypeList, List<String> testParameterTypeList) {
183         if (apiParameterTypeList.equals(testParameterTypeList)) {
184             return true;
185         }
186         if (apiParameterTypeList.size() != testParameterTypeList.size()) {
187             return false;
188         }
189 
190         for (int i = 0; i < apiParameterTypeList.size(); i++) {
191             String apiParameterType = apiParameterTypeList.get(i);
192             String testParameterType = testParameterTypeList.get(i);
193             if (!compareType(apiParameterType, testParameterType)) {
194                 return false;
195             }
196         }
197         return true;
198     }
199 
200     /**
201      * @return true iff the parameter is a var arg parameter.
202      */
isVarArg(String parameter)203     private static boolean isVarArg(String parameter) {
204         return parameter.endsWith("...");
205     }
206 
207     /**
208      * Compare class types.
209      * @param apiType The type as reported by the api
210      * @param testType The type as found used in a test
211      * @return true iff the strings are equal,
212      * or the apiType is generic and the test type is not void
213      */
compareType(String apiType, String testType)214     private static boolean compareType(String apiType, String testType) {
215         return apiType.equals(testType) ||
216                 isGenericType(apiType) && !testType.equals(VOID) ||
217                 isGenericArrayType(apiType) && isArrayType(testType) ||
218                 isVarArg(apiType) && isArrayType(testType) &&
219                         apiType.startsWith(testType.substring(0, testType.indexOf("[")));
220     }
221 
222     /**
223      * @return true iff the given parameterType is a generic type.
224      */
isGenericType(String type)225     private static boolean isGenericType(String type) {
226         return type.length() == 1 &&
227                 type.charAt(0) >= 'A' &&
228                 type.charAt(0) <= 'Z';
229     }
230 
231     /**
232      * @return true iff {@code type} ends with an [].
233      */
isArrayType(String type)234     private static boolean isArrayType(String type) {
235         return type.endsWith("[]");
236     }
237 
238     /**
239      * @return true iff the given parameterType is an array of generic type.
240      */
isGenericArrayType(String type)241     private static boolean isGenericArrayType(String type) {
242         return type.length() == 3 && isGenericType(type.substring(0, 1)) && isArrayType(type);
243     }
244 
getConstructor(List<String> parameterTypes)245     private ApiConstructor getConstructor(List<String> parameterTypes) {
246         for (ApiConstructor constructor : mApiConstructors) {
247             if (compareParameterTypes(constructor.getParameterTypes(), parameterTypes)) {
248                 return constructor;
249             }
250         }
251         return null;
252     }
253 }
254