• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.editors.layout.descriptors;
18 
19 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
20 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
21 import com.android.ide.eclipse.adt.internal.resources.ViewClassInfo;
22 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
23 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
24 import com.android.sdklib.IAndroidTarget;
25 
26 import org.eclipse.core.resources.IProject;
27 import org.eclipse.core.runtime.NullProgressMonitor;
28 import org.eclipse.jdt.core.IJavaProject;
29 import org.eclipse.jdt.core.IType;
30 import org.eclipse.jdt.core.ITypeHierarchy;
31 import org.eclipse.jdt.core.JavaCore;
32 import org.eclipse.jdt.core.JavaModelException;
33 
34 import java.util.HashMap;
35 import java.util.List;
36 
37 /**
38  * Service responsible for creating/managing {@link ElementDescriptor} objects for custom
39  * View classes per project.
40  * <p/>
41  * The service provides an on-demand monitoring of custom classes to check for changes. Monitoring
42  * starts once a request for an {@link ElementDescriptor} object has been done for a specific
43  * class.<br>
44  * The monitoring will notify a listen of any changes in the class triggering a change in its
45  * associated {@link ElementDescriptor} object.
46  * <p/>
47  * If the custom class does not exist, no monitoring is put in place to avoid having to listen
48  * to all class changes in the projects.
49  *
50  */
51 public final class CustomViewDescriptorService {
52 
53     private static CustomViewDescriptorService sThis = new CustomViewDescriptorService();
54 
55     /**
56      * Map where keys are the project, and values are another map containing all the known
57      * custom View class for this project. The custom View class are stored in a map
58      * where the keys are the fully qualified class name, and the values are their associated
59      * {@link ElementDescriptor}.
60      */
61     private HashMap<IProject, HashMap<String, ElementDescriptor>> mCustomDescriptorMap =
62         new HashMap<IProject, HashMap<String, ElementDescriptor>>();
63 
64     /**
65      * TODO will be used to update the ElementDescriptor of the custom view when it
66      * is modified (either the class itself or its attributes.xml)
67      */
68     @SuppressWarnings("unused")
69     private ICustomViewDescriptorListener mListener;
70 
71     /**
72      * Classes which implements this interface provide a method that deal with modifications
73      * in custom View class triggering a change in its associated {@link ViewClassInfo} object.
74      */
75     public interface ICustomViewDescriptorListener {
76         /**
77          * Sent when a custom View class has changed and its {@link ElementDescriptor} was modified.
78          * @param project the project containing the class.
79          * @param className the fully qualified class name.
80          * @param descriptor the updated ElementDescriptor.
81          */
updatedClassInfo(IProject project, String className, ElementDescriptor descriptor)82         public void updatedClassInfo(IProject project, String className, ElementDescriptor descriptor);
83     }
84 
85     /**
86      * Returns the singleton instance of {@link CustomViewDescriptorService}.
87      */
getInstance()88     public static CustomViewDescriptorService getInstance() {
89         return sThis;
90     }
91 
92     /**
93      * Sets the listener receiving custom View class modification notifications.
94      * @param listener the listener to receive the notifications.
95      *
96      * TODO will be used to update the ElementDescriptor of the custom view when it
97      * is modified (either the class itself or its attributes.xml)
98      */
setListener(ICustomViewDescriptorListener listener)99     public void setListener(ICustomViewDescriptorListener listener) {
100         mListener = listener;
101     }
102 
103     /**
104      * Returns the {@link ElementDescriptor} for a particular project/class.
105      * <p/>
106      * If it is the first time the <code>ElementDescriptor</code> is requested, the method
107      * will check that the specified class is in fact a custom View class. Once this is
108      * established, a monitoring for that particular class is initiated. Any change will
109      * trigger a notification to the {@link ICustomViewDescriptorListener}.
110      * @param project the project containing the class.
111      * @param fqClassName the fully qualified name of the class.
112      * @return a <code>ElementDescriptor</code> or <code>null</code> if the class was not
113      * a custom View class.
114      */
getDescriptor(IProject project, String fqClassName)115     public ElementDescriptor getDescriptor(IProject project, String fqClassName) {
116         // look in the map first
117         synchronized (mCustomDescriptorMap) {
118             HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
119 
120             if (map != null) {
121                 ElementDescriptor descriptor = map.get(fqClassName);
122                 if (descriptor != null) {
123                     return descriptor;
124                 }
125             }
126 
127             // if we step here, it looks like we haven't created it yet.
128             // First lets check this is in fact a valid type in the project
129 
130             try {
131                 // We expect the project to be both opened and of java type (since it's an android
132                 // project), so we can create a IJavaProject object from our IProject.
133                 IJavaProject javaProject = JavaCore.create(project);
134 
135                 // replace $ by . in the class name
136                 String javaClassName = fqClassName.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
137 
138                 // look for the IType object for this class
139                 IType type = javaProject.findType(javaClassName);
140                 if (type != null && type.exists()) {
141                     // the type exists. Let's get the parent class and its ViewClassInfo.
142 
143                     // get the type hierarchy
144                     ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
145                             new NullProgressMonitor());
146 
147                     ElementDescriptor parentDescriptor = getDescriptor(
148                             hierarchy.getSuperclass(type), project, hierarchy);
149 
150                     if (parentDescriptor != null) {
151                         // we have a valid parent, lets create a new ElementDescriptor.
152 
153                         ViewElementDescriptor descriptor = new ViewElementDescriptor(fqClassName,
154                                 fqClassName, // ui_name
155                                 fqClassName, // canonical class name
156                                 null, // tooltip
157                                 null, // sdk_url
158                                 getAttributeDescriptor(type, parentDescriptor),
159                                 null, // layout attributes
160                                 null, // children
161                                 false /* mandatory */);
162 
163                         synchronized (mCustomDescriptorMap) {
164                             map = mCustomDescriptorMap.get(project);
165                             if (map == null) {
166                                 map = new HashMap<String, ElementDescriptor>();
167                                 mCustomDescriptorMap.put(project, map);
168                             }
169 
170                             map.put(fqClassName, descriptor);
171                         }
172 
173                         //TODO setup listener on this resource change.
174 
175                         return descriptor;
176                     }
177                 }
178             } catch (JavaModelException e) {
179                 // there was an error accessing any of the IType, we'll just return null;
180             }
181         }
182 
183 
184         return null;
185     }
186 
187     /**
188      * Computes (if needed) and returns the {@link ElementDescriptor} for the specified type.
189      *
190      * @param type
191      * @param project
192      * @param typeHierarchy
193      * @return A ViewElementDescriptor or null if type or typeHierarchy is null.
194      */
getDescriptor(IType type, IProject project, ITypeHierarchy typeHierarchy)195     private ViewElementDescriptor getDescriptor(IType type, IProject project,
196             ITypeHierarchy typeHierarchy) {
197         // check if the type is a built-in View class.
198         List<ElementDescriptor> builtInList = null;
199 
200         Sdk currentSdk = Sdk.getCurrent();
201         IAndroidTarget target = currentSdk == null ? null : currentSdk.getTarget(project);
202         if (target != null) {
203             AndroidTargetData data = currentSdk.getTargetData(target);
204             builtInList = data.getLayoutDescriptors().getViewDescriptors();
205         }
206 
207         // give up if there's no type
208         if (type == null) {
209             return null;
210         }
211 
212         String canonicalName = type.getFullyQualifiedName();
213 
214         if (builtInList != null) {
215             for (ElementDescriptor desc : builtInList) {
216                 if (desc instanceof ViewElementDescriptor) {
217                     ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc;
218                     if (canonicalName.equals(viewDescriptor.getCanonicalClassName())) {
219                         return viewDescriptor;
220                     }
221                 }
222             }
223         }
224 
225         // it's not a built-in class? Lets look if the superclass is built-in
226         // give up if there's no type
227         if (typeHierarchy == null) {
228             return null;
229         }
230 
231         IType parentType = typeHierarchy.getSuperclass(type);
232         if (parentType != null) {
233             ViewElementDescriptor parentDescriptor = getDescriptor(parentType, project,
234                     typeHierarchy);
235 
236             if (parentDescriptor != null) {
237                 // parent class is a valid View class with a descriptor, so we create one
238                 // for this class.
239                 ViewElementDescriptor descriptor = new ViewElementDescriptor(canonicalName,
240                         canonicalName, // ui_name
241                         canonicalName, // canonical name
242                         null, // tooltip
243                         null, // sdk_url
244                         getAttributeDescriptor(type, parentDescriptor),
245                         null, // layout attributes
246                         null, // children
247                         false /* mandatory */);
248 
249                 // add it to the map
250                 synchronized (mCustomDescriptorMap) {
251                     HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
252 
253                     if (map == null) {
254                         map = new HashMap<String, ElementDescriptor>();
255                         mCustomDescriptorMap.put(project, map);
256                     }
257 
258                     map.put(canonicalName, descriptor);
259 
260                 }
261 
262                 //TODO setup listener on this resource change.
263 
264                 return descriptor;
265             }
266         }
267 
268         // class is neither a built-in view class, nor extend one. return null.
269         return null;
270     }
271 
272     /**
273      * Returns the array of {@link AttributeDescriptor} for the specified {@link IType}.
274      * <p/>
275      * The array should contain the descriptor for this type and all its supertypes.
276      * @param type the type for which the {@link AttributeDescriptor} are returned.
277      * @param parentDescriptor the {@link ElementDescriptor} of the direct superclass.
278      */
getAttributeDescriptor(IType type, ElementDescriptor parentDescriptor)279     private AttributeDescriptor[] getAttributeDescriptor(IType type,
280             ElementDescriptor parentDescriptor) {
281         // TODO add the class attribute descriptors to the parent descriptors.
282         return parentDescriptor.getAttributes();
283     }
284 }
285