2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.eclipse.org/org/documents/epl-v10.php
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.
17 package com.android.ide.eclipse.adt.internal.sdk;
19 import com.android.sdklib.SdkConstants;
21 import org.eclipse.core.runtime.IProgressMonitor;
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;
31 import java.util.TreeMap;
33 import javax.management.InvalidAttributeValueException;
36 * Parser for the text file containing the list of widgets, layouts and layout params.
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.
44 public final class WidgetClassLoader implements IAndroidClassLoader {
47 * Basic class containing the class descriptions found in the text file.
49 private final static class ClassDescriptor implements IClassDescriptor {
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;
59 ClassDescriptor(String fqcn) {
61 mSimpleName = getSimpleName(fqcn);
64 public String getFullClassName() {
68 public String getSimpleName() {
72 public IClassDescriptor[] getDeclaredClasses() {
73 return mDeclaredClasses.toArray(new IClassDescriptor[mDeclaredClasses.size()]);
76 private void addDeclaredClass(ClassDescriptor declaredClass) {
77 mDeclaredClasses.add(declaredClass);
80 public IClassDescriptor getEnclosingClass() {
81 return mEnclosingClass;
84 void setEnclosingClass(ClassDescriptor enclosingClass) {
85 // set the enclosing class.
86 mEnclosingClass = enclosingClass;
88 // add this to the list of declared class in the enclosing class.
89 mEnclosingClass.addDeclaredClass(this);
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);
96 public IClassDescriptor getSuperclass() {
100 void setSuperClass(ClassDescriptor superClass) {
101 mSuperClass = superClass;
105 public boolean equals(Object clazz) {
106 if (clazz instanceof ClassDescriptor) {
107 return mFqcn.equals(((ClassDescriptor)clazz).mFqcn);
109 return super.equals(clazz);
113 public int hashCode() {
114 return mFqcn.hashCode();
117 public boolean isInstantiable() {
118 return mIsInstantiable;
121 void setInstantiable(boolean state) {
122 mIsInstantiable = state;
125 private String getSimpleName(String fqcn) {
126 String[] segments = fqcn.split("\\.");
127 return segments[segments.length-1];
131 private BufferedReader mReader;
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;
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.
150 WidgetClassLoader(String osFilePath) throws FileNotFoundException {
151 mOsFilePath = osFilePath;
152 mReader = new BufferedReader(new FileReader(osFilePath));
155 public String getSource() {
160 * Parses the text file and return true if the file was successfully parsed.
163 boolean parseWidgetList(IProgressMonitor monitor) {
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;
173 classes = line.substring(1).split(" ");
174 clazz = processClass(classes, 0, null /* map */);
176 clazz.setInstantiable(true);
177 mWidgetMap.put(classes[0], clazz);
181 classes = line.substring(1).split(" ");
182 clazz = processClass(classes, 0, null /* map */);
184 clazz.setInstantiable(true);
185 mLayoutMap.put(classes[0], clazz);
189 classes = line.substring(1).split(" ");
190 clazz = processClass(classes, 0, mLayoutParamsMap);
192 clazz.setInstantiable(true);
196 // comment, do nothing
199 throw new IllegalArgumentException();
204 // reconciliate the layout and their layout params
208 } catch (IOException e) {
212 } catch (IOException e) {
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.
226 private ClassDescriptor processClass(String[] classes, int index,
227 Map<String, ClassDescriptor> map) {
228 if (index >= classes.length) {
232 String fqcn = classes[index];
234 if ("java.lang.Object".equals(fqcn)) { //$NON-NLS-1$
238 // check if the ViewInfoClass has not yet been created.
239 if (mMap.containsKey(fqcn)) {
240 return mMap.get(fqcn);
243 // create the custom class.
244 ClassDescriptor clazz = new ClassDescriptor(fqcn);
245 mMap.put(fqcn, clazz);
247 map.put(fqcn, clazz);
250 // get the super class
251 ClassDescriptor superClass = processClass(classes, index+1, map);
252 if (superClass != null) {
253 clazz.setSuperClass(superClass);
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.
263 private void postProcess() {
264 Collection<ClassDescriptor> params = mLayoutParamsMap.values();
266 for (ClassDescriptor param : params) {
267 String fqcn = param.getFullClassName();
269 // get the enclosed name.
270 String enclosed = getEnclosedName(fqcn);
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
275 ClassDescriptor enclosingType = mMap.get(enclosed);
276 if (enclosingType != null) {
277 param.setEnclosingClass(enclosingType);
279 // remove the class from the map, and put it back with the fixed name
281 mMap.put(param.getFullClassName(), param);
286 private String getEnclosedName(String fqcn) {
287 int index = fqcn.lastIndexOf('.');
288 return fqcn.substring(0, index);
292 * Finds and loads all classes that derive from a given set of super classes.
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
300 * @throws IOException
301 * @throws InvalidAttributeValueException
302 * @throws ClassFormatError
304 public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom(String rootPackage,
305 String[] superClasses) throws IOException, InvalidAttributeValueException,
307 HashMap<String, ArrayList<IClassDescriptor>> map =
308 new HashMap<String, ArrayList<IClassDescriptor>>();
310 ArrayList<IClassDescriptor> list = new ArrayList<IClassDescriptor>();
311 list.addAll(mWidgetMap.values());
312 map.put(SdkConstants.CLASS_VIEW, list);
314 list = new ArrayList<IClassDescriptor>();
315 list.addAll(mLayoutMap.values());
316 map.put(SdkConstants.CLASS_VIEWGROUP, list);
318 list = new ArrayList<IClassDescriptor>();
319 list.addAll(mLayoutParamsMap.values());
320 map.put(SdkConstants.CLASS_VIEWGROUP_LAYOUTPARAMS, list);
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
330 public IClassDescriptor getClass(String className) throws ClassNotFoundException {
331 return mMap.get(className);