OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / sdk / eclipse / plugins / com.android.ide.eclipse.adt / src / com / android / ide / eclipse / adt / internal / sdk / WidgetClassLoader.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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.ide.eclipse.adt.internal.sdk;
18
19 import com.android.sdklib.SdkConstants;
20
21 import org.eclipse.core.runtime.IProgressMonitor;
22
23 import java.io.BufferedReader;
24 import java.io.FileNotFoundException;
25 import java.io.FileReader;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.TreeMap;
32
33 import javax.management.InvalidAttributeValueException;
34
35 /**
36  * Parser for the text file containing the list of widgets, layouts and layout params.
37  * <p/>
38  * The file is a straight text file containing one class per line.<br>
39  * Each line is in the following format<br>
40  * <code>[code][class name] [super class name] [super class name]...</code>
41  * where code is a single letter (W for widget, L for layout, P for layout params), and class names
42  * are the fully qualified name of the classes.
43  */
44 public final class WidgetClassLoader implements IAndroidClassLoader {
45
46     /**
47      * Basic class containing the class descriptions found in the text file.
48      */
49     private final static class ClassDescriptor implements IClassDescriptor {
50
51         private String mFqcn;
52         private String mSimpleName;
53         private ClassDescriptor mSuperClass;
54         private ClassDescriptor mEnclosingClass;
55         private final ArrayList<IClassDescriptor> mDeclaredClasses =
56                 new ArrayList<IClassDescriptor>();
57         private boolean mIsInstantiable = false;
58
59         ClassDescriptor(String fqcn) {
60             mFqcn = fqcn;
61             mSimpleName = getSimpleName(fqcn);
62         }
63
64         public String getFullClassName() {
65             return mFqcn;
66         }
67
68         public String getSimpleName() {
69             return mSimpleName;
70         }
71
72         public IClassDescriptor[] getDeclaredClasses() {
73             return mDeclaredClasses.toArray(new IClassDescriptor[mDeclaredClasses.size()]);
74         }
75
76         private void addDeclaredClass(ClassDescriptor declaredClass) {
77             mDeclaredClasses.add(declaredClass);
78         }
79
80         public IClassDescriptor getEnclosingClass() {
81             return mEnclosingClass;
82         }
83
84         void setEnclosingClass(ClassDescriptor enclosingClass) {
85             // set the enclosing class.
86             mEnclosingClass = enclosingClass;
87
88             // add this to the list of declared class in the enclosing class.
89             mEnclosingClass.addDeclaredClass(this);
90
91             // finally change the name of declared class to make sure it uses the
92             // convention: package.enclosing$declared instead of package.enclosing.declared
93             mFqcn = enclosingClass.mFqcn + "$" + mFqcn.substring(enclosingClass.mFqcn.length() + 1);
94         }
95
96         public IClassDescriptor getSuperclass() {
97             return mSuperClass;
98         }
99
100         void setSuperClass(ClassDescriptor superClass) {
101             mSuperClass = superClass;
102         }
103
104         @Override
105         public boolean equals(Object clazz) {
106             if (clazz instanceof ClassDescriptor) {
107                 return mFqcn.equals(((ClassDescriptor)clazz).mFqcn);
108             }
109             return super.equals(clazz);
110         }
111
112         @Override
113         public int hashCode() {
114             return mFqcn.hashCode();
115         }
116
117         public boolean isInstantiable() {
118             return mIsInstantiable;
119         }
120
121         void setInstantiable(boolean state) {
122             mIsInstantiable = state;
123         }
124
125         private String getSimpleName(String fqcn) {
126             String[] segments = fqcn.split("\\.");
127             return segments[segments.length-1];
128         }
129     }
130
131     private BufferedReader mReader;
132
133     /** Output map of FQCN => descriptor on all classes */
134     private final Map<String, ClassDescriptor> mMap = new TreeMap<String, ClassDescriptor>();
135     /** Output map of FQCN => descriptor on View classes */
136     private final Map<String, ClassDescriptor> mWidgetMap = new TreeMap<String, ClassDescriptor>();
137     /** Output map of FQCN => descriptor on ViewGroup classes */
138     private final Map<String, ClassDescriptor> mLayoutMap = new TreeMap<String, ClassDescriptor>();
139     /** Output map of FQCN => descriptor on LayoutParams classes */
140     private final Map<String, ClassDescriptor> mLayoutParamsMap =
141         new HashMap<String, ClassDescriptor>();
142     /** File path of the source text file */
143     private String mOsFilePath;
144
145     /**
146      * Creates a loader with a given file path.
147      * @param osFilePath the OS path of the file to load.
148      * @throws FileNotFoundException if the file is not found.
149      */
150     WidgetClassLoader(String osFilePath) throws FileNotFoundException {
151         mOsFilePath = osFilePath;
152         mReader = new BufferedReader(new FileReader(osFilePath));
153     }
154
155     public String getSource() {
156         return mOsFilePath;
157     }
158
159     /**
160      * Parses the text file and return true if the file was successfully parsed.
161      * @param monitor
162      */
163     boolean parseWidgetList(IProgressMonitor monitor) {
164         try {
165             String line;
166             while ((line = mReader.readLine()) != null) {
167                 if (line.length() > 0) {
168                     char prefix = line.charAt(0);
169                     String[] classes = null;
170                     ClassDescriptor clazz = null;
171                     switch (prefix) {
172                         case 'W':
173                             classes = line.substring(1).split(" ");
174                             clazz = processClass(classes, 0, null /* map */);
175                             if (clazz != null) {
176                                 clazz.setInstantiable(true);
177                                 mWidgetMap.put(classes[0], clazz);
178                             }
179                             break;
180                         case 'L':
181                             classes = line.substring(1).split(" ");
182                             clazz = processClass(classes, 0, null /* map */);
183                             if (clazz != null) {
184                                 clazz.setInstantiable(true);
185                                 mLayoutMap.put(classes[0], clazz);
186                             }
187                             break;
188                         case 'P':
189                             classes = line.substring(1).split(" ");
190                             clazz = processClass(classes, 0, mLayoutParamsMap);
191                             if (clazz != null) {
192                                 clazz.setInstantiable(true);
193                             }
194                             break;
195                         case '#':
196                             // comment, do nothing
197                             break;
198                         default:
199                                 throw new IllegalArgumentException();
200                     }
201                 }
202             }
203
204             // reconciliate the layout and their layout params
205             postProcess();
206
207             return true;
208         } catch (IOException e) {
209         } finally {
210             try {
211                 mReader.close();
212             } catch (IOException e) {
213             }
214         }
215
216         return false;
217     }
218
219     /**
220      * Parses a View class and adds a ViewClassInfo for it in mWidgetMap.
221      * It calls itself recursively to handle super classes which are also Views.
222      * @param classes the inheritance list of the class to process.
223      * @param index the index of the class to process in the <code>classes</code> array.
224      * @param map an optional map in which to put every {@link ClassDescriptor} created.
225      */
226     private ClassDescriptor processClass(String[] classes, int index,
227             Map<String, ClassDescriptor> map) {
228         if (index >= classes.length) {
229             return null;
230         }
231
232         String fqcn = classes[index];
233
234         if ("java.lang.Object".equals(fqcn)) { //$NON-NLS-1$
235             return null;
236         }
237
238         // check if the ViewInfoClass has not yet been created.
239         if (mMap.containsKey(fqcn)) {
240             return mMap.get(fqcn);
241         }
242
243         // create the custom class.
244         ClassDescriptor clazz = new ClassDescriptor(fqcn);
245         mMap.put(fqcn, clazz);
246         if (map != null) {
247             map.put(fqcn, clazz);
248         }
249
250         // get the super class
251         ClassDescriptor superClass = processClass(classes, index+1, map);
252         if (superClass != null) {
253             clazz.setSuperClass(superClass);
254         }
255
256         return clazz;
257     }
258
259     /**
260      * Goes through the layout params and look for the enclosed class. If the layout params
261      * has no known enclosed type it is dropped.
262      */
263     private void postProcess() {
264         Collection<ClassDescriptor> params = mLayoutParamsMap.values();
265
266         for (ClassDescriptor param : params) {
267             String fqcn = param.getFullClassName();
268
269             // get the enclosed name.
270             String enclosed = getEnclosedName(fqcn);
271
272             // look for a match in the layouts. We don't use the layout map as it only contains the
273             // end classes, but in this case we also need to process the layout params for the base
274             // layout classes.
275             ClassDescriptor enclosingType = mMap.get(enclosed);
276             if (enclosingType != null) {
277                 param.setEnclosingClass(enclosingType);
278
279                 // remove the class from the map, and put it back with the fixed name
280                 mMap.remove(fqcn);
281                 mMap.put(param.getFullClassName(), param);
282             }
283         }
284     }
285
286     private String getEnclosedName(String fqcn) {
287         int index = fqcn.lastIndexOf('.');
288         return fqcn.substring(0, index);
289     }
290
291     /**
292      * Finds and loads all classes that derive from a given set of super classes.
293      *
294      * @param rootPackage Root package of classes to find. Use an empty string to find everyting.
295      * @param superClasses The super classes of all the classes to find.
296      * @return An hash map which keys are the super classes looked for and which values are
297      *         ArrayList of the classes found. The array lists are always created for all the
298      *         valid keys, they are simply empty if no deriving class is found for a given
299      *         super class.
300      * @throws IOException
301      * @throws InvalidAttributeValueException
302      * @throws ClassFormatError
303      */
304     public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom(String rootPackage,
305             String[] superClasses) throws IOException, InvalidAttributeValueException,
306             ClassFormatError {
307         HashMap<String, ArrayList<IClassDescriptor>> map =
308                 new HashMap<String, ArrayList<IClassDescriptor>>();
309
310         ArrayList<IClassDescriptor> list = new ArrayList<IClassDescriptor>();
311         list.addAll(mWidgetMap.values());
312         map.put(SdkConstants.CLASS_VIEW, list);
313
314         list = new ArrayList<IClassDescriptor>();
315         list.addAll(mLayoutMap.values());
316         map.put(SdkConstants.CLASS_VIEWGROUP, list);
317
318         list = new ArrayList<IClassDescriptor>();
319         list.addAll(mLayoutParamsMap.values());
320         map.put(SdkConstants.CLASS_VIEWGROUP_LAYOUTPARAMS, list);
321
322         return map;
323     }
324
325     /**
326      * Returns a {@link IAndroidClassLoader.IClassDescriptor} by its fully-qualified name.
327      * @param className the fully-qualified name of the class to return.
328      * @throws ClassNotFoundException
329      */
330     public IClassDescriptor getClass(String className) throws ClassNotFoundException {
331         return mMap.get(className);
332     }
333
334 }