OSDN Git Service

original
[gb-231r1-is01/GB_2.3_IS01.git] / sdk / eclipse / plugins / com.android.ide.eclipse.adt / src / com / android / ide / eclipse / adt / internal / editors / layout / descriptors / CustomViewDescriptorService.java
diff --git a/sdk/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java b/sdk/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
new file mode 100644 (file)
index 0000000..bfc8bb0
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * 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.editors.layout.descriptors;
+
+import com.android.ide.common.resources.platform.ViewClassInfo;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Service responsible for creating/managing {@link ViewElementDescriptor} objects for custom
+ * View classes per project.
+ * <p/>
+ * The service provides an on-demand monitoring of custom classes to check for changes. Monitoring
+ * starts once a request for an {@link ViewElementDescriptor} object has been done for a specific
+ * class.
+ * <p/>
+ * The monitoring will notify a listener of any changes in the class triggering a change in its
+ * associated {@link ViewElementDescriptor} object.
+ * <p/>
+ * If the custom class does not exist, no monitoring is put in place to avoid having to listen
+ * to all class changes in the projects.
+ */
+public final class CustomViewDescriptorService {
+
+    private static CustomViewDescriptorService sThis = new CustomViewDescriptorService();
+
+    /**
+     * Map where keys are the project, and values are another map containing all the known
+     * custom View class for this project. The custom View class are stored in a map
+     * where the keys are the fully qualified class name, and the values are their associated
+     * {@link ViewElementDescriptor}.
+     */
+    private HashMap<IProject, HashMap<String, ViewElementDescriptor>> mCustomDescriptorMap =
+        new HashMap<IProject, HashMap<String, ViewElementDescriptor>>();
+
+    /**
+     * TODO will be used to update the ViewElementDescriptor of the custom view when it
+     * is modified (either the class itself or its attributes.xml)
+     */
+    @SuppressWarnings("unused")
+    private ICustomViewDescriptorListener mListener;
+
+    /**
+     * Classes which implements this interface provide a method that deal with modifications
+     * in custom View class triggering a change in its associated {@link ViewClassInfo} object.
+     */
+    public interface ICustomViewDescriptorListener {
+        /**
+         * Sent when a custom View class has changed and
+         * its {@link ViewElementDescriptor} was modified.
+         *
+         * @param project the project containing the class.
+         * @param className the fully qualified class name.
+         * @param descriptor the updated ElementDescriptor.
+         */
+        public void updatedClassInfo(IProject project,
+                                     String className,
+                                     ViewElementDescriptor descriptor);
+    }
+
+    /**
+     * Returns the singleton instance of {@link CustomViewDescriptorService}.
+     */
+    public static CustomViewDescriptorService getInstance() {
+        return sThis;
+    }
+
+    /**
+     * Sets the listener receiving custom View class modification notifications.
+     * @param listener the listener to receive the notifications.
+     *
+     * TODO will be used to update the ViewElementDescriptor of the custom view when it
+     * is modified (either the class itself or its attributes.xml)
+     */
+    public void setListener(ICustomViewDescriptorListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Returns the {@link ViewElementDescriptor} for a particular project/class when the
+     * fully qualified class name actually matches a class from the given project.
+     * <p/>
+     * Custom descriptors are created as needed.
+     * <p/>
+     * If it is the first time the {@link ViewElementDescriptor} is requested, the method
+     * will check that the specified class is in fact a custom View class. Once this is
+     * established, a monitoring for that particular class is initiated. Any change will
+     * trigger a notification to the {@link ICustomViewDescriptorListener}.
+     *
+     * @param project the project containing the class.
+     * @param fqcn the fully qualified name of the class.
+     * @return a {@link ViewElementDescriptor} or <code>null</code> if the class was not
+     *         a custom View class.
+     */
+    public ViewElementDescriptor getDescriptor(IProject project, String fqcn) {
+        // look in the map first
+        synchronized (mCustomDescriptorMap) {
+            HashMap<String, ViewElementDescriptor> map = mCustomDescriptorMap.get(project);
+
+            if (map != null) {
+                ViewElementDescriptor descriptor = map.get(fqcn);
+                if (descriptor != null) {
+                    return descriptor;
+                }
+            }
+
+            // if we step here, it looks like we haven't created it yet.
+            // First lets check this is in fact a valid type in the project
+
+            try {
+                // We expect the project to be both opened and of java type (since it's an android
+                // project), so we can create a IJavaProject object from our IProject.
+                IJavaProject javaProject = JavaCore.create(project);
+
+                // replace $ by . in the class name
+                String javaClassName = fqcn.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
+
+                // look for the IType object for this class
+                IType type = javaProject.findType(javaClassName);
+                if (type != null && type.exists()) {
+                    // the type exists. Let's get the parent class and its ViewClassInfo.
+
+                    // get the type hierarchy
+                    ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
+                            new NullProgressMonitor());
+
+                    ViewElementDescriptor parentDescriptor = createViewDescriptor(
+                            hierarchy.getSuperclass(type), project, hierarchy);
+
+                    if (parentDescriptor != null) {
+                        // we have a valid parent, lets create a new ViewElementDescriptor.
+
+                        ViewElementDescriptor descriptor = new ViewElementDescriptor(fqcn,
+                                fqcn, // ui_name
+                                fqcn, // canonical class name
+                                null, // tooltip
+                                null, // sdk_url
+                                getAttributeDescriptor(type, parentDescriptor),
+                                null, // layout attributes
+                                null, // children
+                                false /* mandatory */);
+
+                        descriptor.setSuperClass(parentDescriptor);
+
+                        synchronized (mCustomDescriptorMap) {
+                            map = mCustomDescriptorMap.get(project);
+                            if (map == null) {
+                                map = new HashMap<String, ViewElementDescriptor>();
+                                mCustomDescriptorMap.put(project, map);
+                            }
+
+                            map.put(fqcn, descriptor);
+                        }
+
+                        //TODO setup listener on this resource change.
+
+                        return descriptor;
+                    }
+                }
+            } catch (JavaModelException e) {
+                // there was an error accessing any of the IType, we'll just return null;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Computes (if needed) and returns the {@link ViewElementDescriptor} for the specified type.
+     *
+     * @return A {@link ViewElementDescriptor} or null if type or typeHierarchy is null.
+     */
+    private ViewElementDescriptor createViewDescriptor(IType type, IProject project,
+            ITypeHierarchy typeHierarchy) {
+        // check if the type is a built-in View class.
+        List<ViewElementDescriptor> builtInList = null;
+
+        Sdk currentSdk = Sdk.getCurrent();
+        if (currentSdk != null) {
+            IAndroidTarget target = currentSdk.getTarget(project);
+            if (target != null) {
+                AndroidTargetData data = currentSdk.getTargetData(target);
+                if (data != null) {
+                    builtInList = data.getLayoutDescriptors().getViewDescriptors();
+                }
+            }
+        }
+
+        // give up if there's no type
+        if (type == null) {
+            return null;
+        }
+
+        String fqcn = type.getFullyQualifiedName();
+
+        if (builtInList != null) {
+            for (ViewElementDescriptor viewDescriptor : builtInList) {
+                if (fqcn.equals(viewDescriptor.getFullClassName())) {
+                    return viewDescriptor;
+                }
+            }
+        }
+
+        // it's not a built-in class? Lets look if the superclass is built-in
+        // give up if there's no type
+        if (typeHierarchy == null) {
+            return null;
+        }
+
+        IType parentType = typeHierarchy.getSuperclass(type);
+        if (parentType != null) {
+            ViewElementDescriptor parentDescriptor = createViewDescriptor(parentType, project,
+                    typeHierarchy);
+
+            if (parentDescriptor != null) {
+                // parent class is a valid View class with a descriptor, so we create one
+                // for this class.
+                ViewElementDescriptor descriptor = new ViewElementDescriptor(fqcn,
+                        fqcn, // ui_name
+                        fqcn, // canonical name
+                        null, // tooltip
+                        null, // sdk_url
+                        getAttributeDescriptor(type, parentDescriptor),
+                        null, // layout attributes
+                        null, // children
+                        false /* mandatory */);
+
+                descriptor.setSuperClass(parentDescriptor);
+
+                // add it to the map
+                synchronized (mCustomDescriptorMap) {
+                    HashMap<String, ViewElementDescriptor> map = mCustomDescriptorMap.get(project);
+
+                    if (map == null) {
+                        map = new HashMap<String, ViewElementDescriptor>();
+                        mCustomDescriptorMap.put(project, map);
+                    }
+
+                    map.put(fqcn, descriptor);
+
+                }
+
+                //TODO setup listener on this resource change.
+
+                return descriptor;
+            }
+        }
+
+        // class is neither a built-in view class, nor extend one. return null.
+        return null;
+    }
+
+    /**
+     * Returns the array of {@link AttributeDescriptor} for the specified {@link IType}.
+     * <p/>
+     * The array should contain the descriptor for this type and all its supertypes.
+     *
+     * @param type the type for which the {@link AttributeDescriptor} are returned.
+     * @param parentDescriptor the {@link ViewElementDescriptor} of the direct superclass.
+     */
+    private AttributeDescriptor[] getAttributeDescriptor(IType type,
+            ViewElementDescriptor parentDescriptor) {
+        // TODO add the class attribute descriptors to the parent descriptors.
+        return parentDescriptor.getAttributes();
+    }
+}