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