diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/WidgetClassLoader.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/WidgetClassLoader.java | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/WidgetClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/WidgetClassLoader.java new file mode 100644 index 000000000..682d6e538 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/WidgetClassLoader.java @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.adt.internal.sdk; + +import com.android.SdkConstants; + +import org.eclipse.core.runtime.IProgressMonitor; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import javax.management.InvalidAttributeValueException; + +/** + * Parser for the text file containing the list of widgets, layouts and layout params. + * <p/> + * The file is a straight text file containing one class per line.<br> + * Each line is in the following format<br> + * <code>[code][class name] [super class name] [super class name]...</code> + * where code is a single letter (W for widget, L for layout, P for layout params), and class names + * are the fully qualified name of the classes. + */ +public final class WidgetClassLoader implements IAndroidClassLoader { + + /** + * Basic class containing the class descriptions found in the text file. + */ + private final static class ClassDescriptor implements IClassDescriptor { + + private String mFqcn; + private String mSimpleName; + private ClassDescriptor mSuperClass; + private ClassDescriptor mEnclosingClass; + private final ArrayList<IClassDescriptor> mDeclaredClasses = + new ArrayList<IClassDescriptor>(); + private boolean mIsInstantiable = false; + + ClassDescriptor(String fqcn) { + mFqcn = fqcn; + mSimpleName = getSimpleName(fqcn); + } + + @Override + public String getFullClassName() { + return mFqcn; + } + + @Override + public String getSimpleName() { + return mSimpleName; + } + + @Override + public IClassDescriptor[] getDeclaredClasses() { + return mDeclaredClasses.toArray(new IClassDescriptor[mDeclaredClasses.size()]); + } + + private void addDeclaredClass(ClassDescriptor declaredClass) { + mDeclaredClasses.add(declaredClass); + } + + @Override + public IClassDescriptor getEnclosingClass() { + return mEnclosingClass; + } + + void setEnclosingClass(ClassDescriptor enclosingClass) { + // set the enclosing class. + mEnclosingClass = enclosingClass; + + // add this to the list of declared class in the enclosing class. + mEnclosingClass.addDeclaredClass(this); + + // finally change the name of declared class to make sure it uses the + // convention: package.enclosing$declared instead of package.enclosing.declared + mFqcn = enclosingClass.mFqcn + "$" + mFqcn.substring(enclosingClass.mFqcn.length() + 1); + } + + @Override + public IClassDescriptor getSuperclass() { + return mSuperClass; + } + + void setSuperClass(ClassDescriptor superClass) { + mSuperClass = superClass; + } + + @Override + public boolean equals(Object clazz) { + if (clazz instanceof ClassDescriptor) { + return mFqcn.equals(((ClassDescriptor)clazz).mFqcn); + } + return super.equals(clazz); + } + + @Override + public int hashCode() { + return mFqcn.hashCode(); + } + + @Override + public boolean isInstantiable() { + return mIsInstantiable; + } + + void setInstantiable(boolean state) { + mIsInstantiable = state; + } + + private String getSimpleName(String fqcn) { + String[] segments = fqcn.split("\\."); + return segments[segments.length-1]; + } + } + + private BufferedReader mReader; + + /** Output map of FQCN => descriptor on all classes */ + private final Map<String, ClassDescriptor> mMap = new TreeMap<String, ClassDescriptor>(); + /** Output map of FQCN => descriptor on View classes */ + private final Map<String, ClassDescriptor> mWidgetMap = new TreeMap<String, ClassDescriptor>(); + /** Output map of FQCN => descriptor on ViewGroup classes */ + private final Map<String, ClassDescriptor> mLayoutMap = new TreeMap<String, ClassDescriptor>(); + /** Output map of FQCN => descriptor on LayoutParams classes */ + private final Map<String, ClassDescriptor> mLayoutParamsMap = + new HashMap<String, ClassDescriptor>(); + /** File path of the source text file */ + private String mOsFilePath; + + /** + * Creates a loader with a given file path. + * @param osFilePath the OS path of the file to load. + * @throws FileNotFoundException if the file is not found. + */ + WidgetClassLoader(String osFilePath) throws FileNotFoundException { + mOsFilePath = osFilePath; + mReader = new BufferedReader(new FileReader(osFilePath)); + } + + @Override + public String getSource() { + return mOsFilePath; + } + + /** + * Parses the text file and return true if the file was successfully parsed. + * @param monitor + */ + boolean parseWidgetList(IProgressMonitor monitor) { + try { + String line; + while ((line = mReader.readLine()) != null) { + if (line.length() > 0) { + char prefix = line.charAt(0); + String[] classes = null; + ClassDescriptor clazz = null; + switch (prefix) { + case 'W': + classes = line.substring(1).split(" "); + clazz = processClass(classes, 0, null /* map */); + if (clazz != null) { + clazz.setInstantiable(true); + mWidgetMap.put(classes[0], clazz); + } + break; + case 'L': + classes = line.substring(1).split(" "); + clazz = processClass(classes, 0, null /* map */); + if (clazz != null) { + clazz.setInstantiable(true); + mLayoutMap.put(classes[0], clazz); + } + break; + case 'P': + classes = line.substring(1).split(" "); + clazz = processClass(classes, 0, mLayoutParamsMap); + if (clazz != null) { + clazz.setInstantiable(true); + } + break; + case '#': + // comment, do nothing + break; + default: + throw new IllegalArgumentException(); + } + } + } + + // reconciliate the layout and their layout params + postProcess(); + + return true; + } catch (IOException e) { + } finally { + try { + mReader.close(); + } catch (IOException e) { + } + } + + return false; + } + + /** + * Parses a View class and adds a ViewClassInfo for it in mWidgetMap. + * It calls itself recursively to handle super classes which are also Views. + * @param classes the inheritance list of the class to process. + * @param index the index of the class to process in the <code>classes</code> array. + * @param map an optional map in which to put every {@link ClassDescriptor} created. + */ + private ClassDescriptor processClass(String[] classes, int index, + Map<String, ClassDescriptor> map) { + if (index >= classes.length) { + return null; + } + + String fqcn = classes[index]; + + if ("java.lang.Object".equals(fqcn)) { //$NON-NLS-1$ + return null; + } + + // check if the ViewInfoClass has not yet been created. + if (mMap.containsKey(fqcn)) { + return mMap.get(fqcn); + } + + // create the custom class. + ClassDescriptor clazz = new ClassDescriptor(fqcn); + mMap.put(fqcn, clazz); + if (map != null) { + map.put(fqcn, clazz); + } + + // get the super class + ClassDescriptor superClass = processClass(classes, index+1, map); + if (superClass != null) { + clazz.setSuperClass(superClass); + } + + return clazz; + } + + /** + * Goes through the layout params and look for the enclosed class. If the layout params + * has no known enclosed type it is dropped. + */ + private void postProcess() { + Collection<ClassDescriptor> params = mLayoutParamsMap.values(); + + for (ClassDescriptor param : params) { + String fqcn = param.getFullClassName(); + + // get the enclosed name. + String enclosed = getEnclosedName(fqcn); + + // look for a match in the layouts. We don't use the layout map as it only contains the + // end classes, but in this case we also need to process the layout params for the base + // layout classes. + ClassDescriptor enclosingType = mMap.get(enclosed); + if (enclosingType != null) { + param.setEnclosingClass(enclosingType); + + // remove the class from the map, and put it back with the fixed name + mMap.remove(fqcn); + mMap.put(param.getFullClassName(), param); + } + } + } + + private String getEnclosedName(String fqcn) { + int index = fqcn.lastIndexOf('.'); + return fqcn.substring(0, index); + } + + /** + * Finds and loads all classes that derive from a given set of super classes. + * + * @param rootPackage Root package of classes to find. Use an empty string to find everyting. + * @param superClasses The super classes of all the classes to find. + * @return An hash map which keys are the super classes looked for and which values are + * ArrayList of the classes found. The array lists are always created for all the + * valid keys, they are simply empty if no deriving class is found for a given + * super class. + * @throws IOException + * @throws InvalidAttributeValueException + * @throws ClassFormatError + */ + @Override + public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom(String rootPackage, + String[] superClasses) throws IOException, InvalidAttributeValueException, + ClassFormatError { + HashMap<String, ArrayList<IClassDescriptor>> map = + new HashMap<String, ArrayList<IClassDescriptor>>(); + + ArrayList<IClassDescriptor> list = new ArrayList<IClassDescriptor>(); + list.addAll(mWidgetMap.values()); + map.put(SdkConstants.CLASS_VIEW, list); + + list = new ArrayList<IClassDescriptor>(); + list.addAll(mLayoutMap.values()); + map.put(SdkConstants.CLASS_VIEWGROUP, list); + + list = new ArrayList<IClassDescriptor>(); + list.addAll(mLayoutParamsMap.values()); + map.put(SdkConstants.CLASS_VIEWGROUP_LAYOUTPARAMS, list); + + return map; + } + + /** + * Returns a {@link IAndroidClassLoader.IClassDescriptor} by its fully-qualified name. + * @param className the fully-qualified name of the class to return. + * @throws ClassNotFoundException + */ + @Override + public IClassDescriptor getClass(String className) throws ClassNotFoundException { + return mMap.get(className); + } + +} |