• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.tools.lint.checks;
18 
19 import com.android.util.Pair;
20 
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 
28 /**
29  * Represents a class and its methods/fields.
30  *
31  * {@link #getSince()} gives the API level it was introduced.
32  *
33  * {@link #getMethod(String)} returns when the method was introduced.
34  * {@link #getField(String)} returns when the field was introduced.
35  */
36 public class ApiClass {
37 
38     private final String mName;
39     private final int mSince;
40 
41     private final List<Pair<String, Integer>> mSuperClasses = new ArrayList<Pair<String, Integer>>();
42     private final List<Pair<String, Integer>> mInterfaces = new ArrayList<Pair<String, Integer>>();
43 
44     private final Map<String, Integer> mFields = new HashMap<String, Integer>();
45     private final Map<String, Integer> mMethods = new HashMap<String, Integer>();
46 
ApiClass(String name, int since)47     public ApiClass(String name, int since) {
48         mName = name;
49         mSince = since;
50     }
51 
52     /**
53      * Returns the name of the class.
54      * @return the name of the class
55      */
getName()56     public String getName() {
57         return mName;
58     }
59 
60     /**
61      * Returns when the class was introduced.
62      * @return the api level the class was introduced.
63      */
getSince()64     public int getSince() {
65         return mSince;
66     }
67 
68     /**
69      * Returns when a field was added, or null if it doesn't exist.
70      * @param name the name of the field.
71      * @return
72      */
getField(String name, Api info)73     public Integer getField(String name, Api info) {
74         // The field can come from this class or from a super class or an interface
75         // The value can never be lower than this introduction of this class.
76         // When looking at super classes and interfaces, it can never be lower than when the
77         // super class or interface was added as a super class or interface to this clas.
78         // Look at all the values and take the lowest.
79         // For instance:
80         // This class A is introduced in 5 with super class B.
81         // In 10, the interface C was added.
82         // Looking for SOME_FIELD we get the following:
83         // Present in A in API 15
84         // Present in B in API 11
85         // Present in C in API 7.
86         // The answer is 10, which is when C became an interface
87         int min = Integer.MAX_VALUE;
88         Integer i = mFields.get(name);
89         if (i != null) {
90             min = i;
91         }
92 
93         // now look at the super classes
94         for (Pair<String, Integer> superClassPair : mSuperClasses) {
95             ApiClass superClass = info.getClass(superClassPair.getFirst());
96             if (superClass != null) {
97                 i = superClass.getField(name, info);
98                 if (i != null) {
99                     int tmp = superClassPair.getSecond() > i ? superClassPair.getSecond() : i;
100                     if (tmp < min) {
101                         min = tmp;
102                     }
103                 }
104             }
105         }
106 
107         // now look at the interfaces
108         for (Pair<String, Integer> superClassPair : mInterfaces) {
109             ApiClass superClass = info.getClass(superClassPair.getFirst());
110             if (superClass != null) {
111                 i = superClass.getField(name, info);
112                 if (i != null) {
113                     int tmp = superClassPair.getSecond() > i ? superClassPair.getSecond() : i;
114                     if (tmp < min) {
115                         min = tmp;
116                     }
117                 }
118             }
119         }
120 
121         return min;
122     }
123 
124     /**
125      * Returns when a method was added, or null if it doesn't exist. This goes through the super
126      * class to find method only present there.
127      * @param methodSignature the method signature
128      * @return
129      */
getMethod(String methodSignature, Api info)130     public int getMethod(String methodSignature, Api info) {
131         // The method can come from this class or from a super class.
132         // The value can never be lower than this introduction of this class.
133         // When looking at super classes, it can never be lower than when the super class became
134         // a super class of this class.
135         // Look at all the values and take the lowest.
136         // For instance:
137         // This class A is introduced in 5 with super class B.
138         // In 10, the super class changes to C.
139         // Looking for foo() we get the following:
140         // Present in A in API 15
141         // Present in B in API 11
142         // Present in C in API 7.
143         // The answer is 10, which is when C became the super class.
144         int min = Integer.MAX_VALUE;
145         Integer i = mMethods.get(methodSignature);
146         if (i != null) {
147             min = i;
148         }
149 
150         // now look at the super classes
151         for (Pair<String, Integer> superClassPair : mSuperClasses) {
152             ApiClass superClass = info.getClass(superClassPair.getFirst());
153             if (superClass != null) {
154                 i = superClass.getMethod(methodSignature, info);
155                 if (i != null) {
156                     int tmp = superClassPair.getSecond() > i ? superClassPair.getSecond() : i;
157                     if (tmp < min) {
158                         min = tmp;
159                     }
160                 }
161             }
162         }
163 
164         return min;
165     }
166 
addField(String name, int since)167     public void addField(String name, int since) {
168         Integer i = mFields.get(name);
169         if (i == null || i.intValue() > since) {
170             mFields.put(name, Integer.valueOf(since));
171         }
172     }
173 
addMethod(String name, int since)174     public void addMethod(String name, int since) {
175         // Strip off the method type at the end to ensure that the code which
176         // produces inherited methods doesn't get confused and end up multiple entries.
177         // For example, java/nio/Buffer has the method "array()Ljava/lang/Object;",
178         // and the subclass java/nio/ByteBuffer has the method "array()[B". We want
179         // the lookup on mMethods to associate the ByteBuffer array method to be
180         // considered overriding the Buffer method.
181         int index = name.indexOf(')');
182         if (index != -1) {
183             name = name.substring(0, index + 1);
184         }
185 
186         Integer i = mMethods.get(name);
187         if (i == null || i.intValue() > since) {
188             mMethods.put(name, Integer.valueOf(since));
189         }
190     }
191 
addSuperClass(String superClass, int since)192     public void addSuperClass(String superClass, int since) {
193         addToArray(mSuperClasses, superClass, since);
194     }
195 
addInterface(String interfaceClass, int since)196     public void addInterface(String interfaceClass, int since) {
197         addToArray(mInterfaces, interfaceClass, since);
198     }
199 
addToArray(List<Pair<String, Integer>> list, String name, int value)200     void addToArray(List<Pair<String, Integer>> list, String name, int value) {
201         // check if we already have that name (at a lower level)
202         for (Pair<String, Integer> pair : list) {
203             if (name.equals(pair.getFirst())) {
204                 return;
205             }
206         }
207 
208         list.add(Pair.of(name, Integer.valueOf(value)));
209 
210     }
211 
212     @Override
toString()213     public String toString() {
214         return mName;
215     }
216 
217     /**
218      * Returns the set of all methods, including inherited
219      * ones.
220      *
221      * @param info the api to look up super classes from
222      * @return a set containing all the members fields
223      */
getAllMethods(Api info)224     public Set<String> getAllMethods(Api info) {
225         Set<String> members = new HashSet<String>(100);
226         addAllMethods(info, members);
227 
228         return members;
229     }
230 
addAllMethods(Api info, Set<String> set)231     private void addAllMethods(Api info, Set<String> set) {
232         for (String method : mMethods.keySet()) {
233             set.add(method);
234         }
235         for (Pair<String, Integer> superClass : mSuperClasses) {
236             ApiClass clz = info.getClass(superClass.getFirst());
237             assert clz != null : superClass.getSecond();
238             if (clz != null) {
239                 clz.addAllMethods(info, set);
240             }
241         }
242     }
243 
244     /**
245      * Returns the set of all fields, including inherited
246      * ones.
247      *
248      * @param info the api to look up super classes from
249      * @return a set containing all the fields
250      */
getAllFields(Api info)251     public Set<String> getAllFields(Api info) {
252         Set<String> members = new HashSet<String>(100);
253         addAllFields(info, members);
254 
255         return members;
256     }
257 
addAllFields(Api info, Set<String> set)258     private void addAllFields(Api info, Set<String> set) {
259         for (String field : mFields.keySet()) {
260             set.add(field);
261         }
262         for (Pair<String, Integer> superClass : mSuperClasses) {
263             ApiClass clz = info.getClass(superClass.getFirst());
264             assert clz != null : superClass.getSecond();
265             if (clz != null) {
266                 clz.addAllFields(info, set);
267             }
268         }
269     }
270 
271 }
272