• 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.editors.layout.gscripts.IAttributeInfo.Format;
20 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
21 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
22 import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
23 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
24 import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
25 import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor;
26 import com.android.ide.eclipse.adt.internal.resources.AttributeInfo;
27 import com.android.ide.eclipse.adt.internal.resources.ViewClassInfo;
28 import com.android.ide.eclipse.adt.internal.resources.ViewClassInfo.LayoutParamsInfo;
29 import com.android.sdklib.SdkConstants;
30 
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map.Entry;
36 
37 
38 /**
39  * Complete description of the layout structure.
40  */
41 public final class LayoutDescriptors implements IDescriptorProvider {
42 
43     // Public attributes names, attributes descriptors and elements descriptors
44     public static final String ID_ATTR = "id"; //$NON-NLS-1$
45 
46     /** The document descriptor. Contains all layouts and views linked together. */
47     private DocumentDescriptor mRootDescriptor =
48         new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
49 
50     /** The list of all known ViewLayout descriptors. */
51     private ArrayList<ElementDescriptor> mLayoutDescriptors = new ArrayList<ElementDescriptor>();
52 
53     /** Read-Only list of View Descriptors. */
54     private List<ElementDescriptor> mROLayoutDescriptors;
55 
56     /** The list of all known View (not ViewLayout) descriptors. */
57     private ArrayList<ElementDescriptor> mViewDescriptors = new ArrayList<ElementDescriptor>();
58 
59     /** Read-Only list of View Descriptors. */
60     private List<ElementDescriptor> mROViewDescriptors;
61 
62     /** The descriptor matching android.view.View. */
63     private ElementDescriptor mBaseViewDescriptor;
64 
65     /** Returns the document descriptor. Contains all layouts and views linked together. */
getDescriptor()66     public DocumentDescriptor getDescriptor() {
67         return mRootDescriptor;
68     }
69 
70     /** Returns the read-only list of all known ViewLayout descriptors. */
getLayoutDescriptors()71     public List<ElementDescriptor> getLayoutDescriptors() {
72         return mROLayoutDescriptors;
73     }
74 
75     /** Returns the read-only list of all known View (not ViewLayout) descriptors. */
getViewDescriptors()76     public List<ElementDescriptor> getViewDescriptors() {
77         return mROViewDescriptors;
78     }
79 
getRootElementDescriptors()80     public ElementDescriptor[] getRootElementDescriptors() {
81         return mRootDescriptor.getChildren();
82     }
83 
84     /**
85      * Returns the descriptor matching android.view.View.
86      */
getBaseViewDescriptor()87     public ElementDescriptor getBaseViewDescriptor() {
88         if (mBaseViewDescriptor == null) {
89             for (ElementDescriptor desc : mViewDescriptors) {
90                 if (desc instanceof ViewElementDescriptor) {
91                     ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
92                     if (SdkConstants.CLASS_VIEW.equals(viewDesc.getFullClassName())) {
93                         mBaseViewDescriptor = viewDesc;
94                         break;
95                     }
96                 }
97 
98             }
99         }
100         return mBaseViewDescriptor;
101     }
102 
103     /**
104      * Updates the document descriptor.
105      * <p/>
106      * It first computes the new children of the descriptor and then update them
107      * all at once.
108      * <p/>
109      *  TODO: differentiate groups from views in the tree UI? => rely on icons
110      * <p/>
111      *
112      * @param views The list of views in the framework.
113      * @param layouts The list of layouts in the framework.
114      */
updateDescriptors(ViewClassInfo[] views, ViewClassInfo[] layouts)115     public synchronized void updateDescriptors(ViewClassInfo[] views, ViewClassInfo[] layouts) {
116 
117         // This map links every ViewClassInfo to the ElementDescriptor we created.
118         // It is filled by convertView() and used later to fix the super-class hierarchy.
119         HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap =
120             new HashMap<ViewClassInfo, ViewElementDescriptor>();
121 
122         ArrayList<ElementDescriptor> newViews = new ArrayList<ElementDescriptor>();
123         if (views != null) {
124             for (ViewClassInfo info : views) {
125                 ElementDescriptor desc = convertView(info, infoDescMap);
126                 newViews.add(desc);
127             }
128         }
129 
130         // Create <include> as a synthetic regular view.
131         // Note: ViewStub is already described by attrs.xml
132         insertInclude(newViews);
133 
134         ArrayList<ElementDescriptor> newLayouts = new ArrayList<ElementDescriptor>();
135         if (layouts != null) {
136             for (ViewClassInfo info : layouts) {
137                 ElementDescriptor desc = convertView(info, infoDescMap);
138                 newLayouts.add(desc);
139             }
140         }
141 
142         ArrayList<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
143         newDescriptors.addAll(newLayouts);
144         newDescriptors.addAll(newViews);
145 
146         // Link all layouts to everything else here.. recursively
147         for (ElementDescriptor layoutDesc : newLayouts) {
148             layoutDesc.setChildren(newDescriptors);
149         }
150 
151         fixSuperClasses(infoDescMap);
152 
153         // The <merge> tag can only be a root tag, so it is added at the end.
154         // It gets everything else as children but it is not made a child itself.
155         ElementDescriptor mergeTag = createMerge(newLayouts);
156         mergeTag.setChildren(newDescriptors);  // mergeTag makes a copy of the list
157         newDescriptors.add(mergeTag);
158         newLayouts.add(mergeTag);
159 
160         mViewDescriptors = newViews;
161         mLayoutDescriptors  = newLayouts;
162         mRootDescriptor.setChildren(newDescriptors);
163 
164         mBaseViewDescriptor = null;
165         mROLayoutDescriptors = Collections.unmodifiableList(mLayoutDescriptors);
166         mROViewDescriptors = Collections.unmodifiableList(mViewDescriptors);
167     }
168 
169     /**
170      * Creates an element descriptor from a given {@link ViewClassInfo}.
171      *
172      * @param info The {@link ViewClassInfo} to convert into a new {@link ViewElementDescriptor}.
173      * @param infoDescMap This map links every ViewClassInfo to the ElementDescriptor it created.
174      *                    It is filled by here and used later to fix the super-class hierarchy.
175      */
convertView( ViewClassInfo info, HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap)176     private ElementDescriptor convertView(
177             ViewClassInfo info,
178             HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
179         String xml_name = info.getShortClassName();
180         String tooltip = info.getJavaDoc();
181 
182         ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>();
183 
184         // All views and groups have an implicit "style" attribute which is a reference.
185         AttributeInfo styleInfo = new AttributeInfo(
186                 "style",    //$NON-NLS-1$ xmlLocalName
187                 new Format[] { Format.REFERENCE });
188         styleInfo.setJavaDoc("A reference to a custom style"); //tooltip
189         DescriptorsUtils.appendAttribute(attributes,
190                 "style",    //$NON-NLS-1$
191                 null,       //nsUri
192                 styleInfo,
193                 false,      //required
194                 null);      // overrides
195 
196         // Process all View attributes
197         DescriptorsUtils.appendAttributes(attributes,
198                 null, // elementName
199                 SdkConstants.NS_RESOURCES,
200                 info.getAttributes(),
201                 null, // requiredAttributes
202                 null /* overrides */);
203 
204         for (ViewClassInfo link = info.getSuperClass();
205                 link != null;
206                 link = link.getSuperClass()) {
207             AttributeInfo[] attrList = link.getAttributes();
208             if (attrList.length > 0) {
209                 attributes.add(new SeparatorAttributeDescriptor(
210                         String.format("Attributes from %1$s", link.getShortClassName())));
211                 DescriptorsUtils.appendAttributes(attributes,
212                         null, // elementName
213                         SdkConstants.NS_RESOURCES,
214                         attrList,
215                         null, // requiredAttributes
216                         null /* overrides */);
217             }
218         }
219 
220         // Process all LayoutParams attributes
221         ArrayList<AttributeDescriptor> layoutAttributes = new ArrayList<AttributeDescriptor>();
222         LayoutParamsInfo layoutParams = info.getLayoutData();
223 
224         for(; layoutParams != null; layoutParams = layoutParams.getSuperClass()) {
225             boolean need_separator = true;
226             for (AttributeInfo attr_info : layoutParams.getAttributes()) {
227                 if (DescriptorsUtils.containsAttribute(layoutAttributes,
228                         SdkConstants.NS_RESOURCES, attr_info)) {
229                     continue;
230                 }
231                 if (need_separator) {
232                     String title;
233                     if (layoutParams.getShortClassName().equals(
234                             SdkConstants.CLASS_NAME_LAYOUTPARAMS)) {
235                         title = String.format("Layout Attributes from %1$s",
236                                     layoutParams.getViewLayoutClass().getShortClassName());
237                     } else {
238                         title = String.format("Layout Attributes from %1$s (%2$s)",
239                                 layoutParams.getViewLayoutClass().getShortClassName(),
240                                 layoutParams.getShortClassName());
241                     }
242                     layoutAttributes.add(new SeparatorAttributeDescriptor(title));
243                     need_separator = false;
244                 }
245                 DescriptorsUtils.appendAttribute(layoutAttributes,
246                         null, // elementName
247                         SdkConstants.NS_RESOURCES,
248                         attr_info,
249                         false, // required
250                         null /* overrides */);
251             }
252         }
253 
254         ViewElementDescriptor desc = new ViewElementDescriptor(xml_name,
255                 xml_name, // ui_name
256                 info.getFullClassName(),
257                 tooltip,
258                 null, // sdk_url
259                 attributes.toArray(new AttributeDescriptor[attributes.size()]),
260                 layoutAttributes.toArray(new AttributeDescriptor[layoutAttributes.size()]),
261                 null, // children
262                 false /* mandatory */);
263         infoDescMap.put(info, desc);
264         return desc;
265     }
266 
267     /**
268      * Creates a new <include> descriptor and adds it to the list of view descriptors.
269      *
270      * @param knownViews A list of view descriptors being populated. Also used to find the
271      *   View descriptor and extract its layout attributes.
272      */
insertInclude(ArrayList<ElementDescriptor> knownViews)273     private void insertInclude(ArrayList<ElementDescriptor> knownViews) {
274         String xml_name = "include";  //$NON-NLS-1$
275 
276         // Create the include custom attributes
277         ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>();
278 
279         // Note that the "layout" attribute does NOT have the Android namespace
280         DescriptorsUtils.appendAttribute(attributes,
281                 null, //elementXmlName
282                 null, //nsUri
283                 new AttributeInfo(
284                         "layout",       //$NON-NLS-1$
285                         new Format[] { Format.REFERENCE } ),
286                 true,  //required
287                 null); //overrides
288 
289         DescriptorsUtils.appendAttribute(attributes,
290                 null, //elementXmlName
291                 SdkConstants.NS_RESOURCES, //nsUri
292                 new AttributeInfo(
293                         "id",           //$NON-NLS-1$
294                         new Format[] { Format.REFERENCE } ),
295                 true,  //required
296                 null); //overrides
297 
298         // Find View and inherit all its layout attributes
299         AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(
300                 SdkConstants.CLASS_VIEW, knownViews);
301 
302         // Create the include descriptor
303         ViewElementDescriptor desc = new ViewElementDescriptor(xml_name,  // xml_name
304                 xml_name, // ui_name
305                 null,     // canonical class name, we don't have one
306                 "Lets you statically include XML layouts inside other XML layouts.",  // tooltip
307                 null, // sdk_url
308                 attributes.toArray(new AttributeDescriptor[attributes.size()]),
309                 viewLayoutAttribs,  // layout attributes
310                 null, // children
311                 false /* mandatory */);
312 
313         knownViews.add(desc);
314     }
315 
316     /**
317      * Creates and return a new <merge> descriptor.
318      * @param knownLayouts  A list of all known layout view descriptors, used to find the
319      *   FrameLayout descriptor and extract its layout attributes.
320      */
createMerge(ArrayList<ElementDescriptor> knownLayouts)321     private ElementDescriptor createMerge(ArrayList<ElementDescriptor> knownLayouts) {
322         String xml_name = "merge";  //$NON-NLS-1$
323 
324         // Find View and inherit all its layout attributes
325         AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(
326                 SdkConstants.CLASS_FRAMELAYOUT, knownLayouts);
327 
328         // Create the include descriptor
329         ViewElementDescriptor desc = new ViewElementDescriptor(xml_name,  // xml_name
330                 xml_name, // ui_name
331                 null,     // canonical class name, we don't have one
332                 "A root tag useful for XML layouts inflated using a ViewStub.",  // tooltip
333                 null,  // sdk_url
334                 null,  // attributes
335                 viewLayoutAttribs,  // layout attributes
336                 null,  // children
337                 false  /* mandatory */);
338 
339         return desc;
340     }
341 
342     /**
343      * Finds the descriptor and retrieves all its layout attributes.
344      */
findViewLayoutAttributes( String viewFqcn, ArrayList<ElementDescriptor> knownViews)345     private AttributeDescriptor[] findViewLayoutAttributes(
346             String viewFqcn,
347             ArrayList<ElementDescriptor> knownViews) {
348 
349         for (ElementDescriptor desc : knownViews) {
350             if (desc instanceof ViewElementDescriptor) {
351                 ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
352                 if (viewFqcn.equals(viewDesc.getFullClassName())) {
353                     return viewDesc.getLayoutAttributes();
354                 }
355             }
356         }
357 
358         return null;
359     }
360 
361     /**
362      * Set the super-class of each {@link ViewElementDescriptor} by using the super-class
363      * information available in the {@link ViewClassInfo}.
364      */
fixSuperClasses(HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap)365     private void fixSuperClasses(HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
366 
367         for (Entry<ViewClassInfo, ViewElementDescriptor> entry : infoDescMap.entrySet()) {
368             ViewClassInfo info = entry.getKey();
369             ViewElementDescriptor desc = entry.getValue();
370 
371             ViewClassInfo sup = info.getSuperClass();
372             if (sup != null) {
373                 ViewElementDescriptor supDesc = infoDescMap.get(sup);
374                 while (supDesc == null && sup != null) {
375                     // We don't have a descriptor for the super-class. That means the class is
376                     // probably abstract, so we just need to walk up the super-class chain till
377                     // we find one we have. All views derive from android.view.View so we should
378                     // surely find that eventually.
379                     sup = sup.getSuperClass();
380                     if (sup != null) {
381                         supDesc = infoDescMap.get(sup);
382                     }
383                 }
384                 if (supDesc != null) {
385                     desc.setSuperClass(supDesc);
386                 }
387             }
388         }
389     }
390 }
391