blob: b855587c0053040685c53984a386fdb2eb4f2aa1 [file] [log] [blame]
/*
* 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.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.resources.DeclareStyleableInfo;
import com.android.ide.eclipse.adt.internal.resources.ViewClassInfo;
import com.android.ide.eclipse.adt.internal.resources.DeclareStyleableInfo.AttributeInfo;
import com.android.ide.eclipse.adt.internal.resources.ViewClassInfo.LayoutParamsInfo;
import com.android.sdklib.SdkConstants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Complete description of the layout structure.
*/
public final class LayoutDescriptors implements IDescriptorProvider {
// Public attributes names, attributes descriptors and elements descriptors
public static final String ID_ATTR = "id"; //$NON-NLS-1$
/** The document descriptor. Contains all layouts and views linked together. */
private DocumentDescriptor mRootDescriptor =
new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
/** The list of all known ViewLayout descriptors. */
private ArrayList<ElementDescriptor> mLayoutDescriptors = new ArrayList<ElementDescriptor>();
/** Read-Only list of View Descriptors. */
private List<ElementDescriptor> mROLayoutDescriptors;
/** The list of all known View (not ViewLayout) descriptors. */
private ArrayList<ElementDescriptor> mViewDescriptors = new ArrayList<ElementDescriptor>();
/** Read-Only list of View Descriptors. */
private List<ElementDescriptor> mROViewDescriptors;
/** @return the document descriptor. Contains all layouts and views linked together. */
public DocumentDescriptor getDescriptor() {
return mRootDescriptor;
}
/** @return The read-only list of all known ViewLayout descriptors. */
public List<ElementDescriptor> getLayoutDescriptors() {
return mROLayoutDescriptors;
}
/** @return The read-only list of all known View (not ViewLayout) descriptors. */
public List<ElementDescriptor> getViewDescriptors() {
return mROViewDescriptors;
}
public ElementDescriptor[] getRootElementDescriptors() {
return mRootDescriptor.getChildren();
}
/**
* Updates the document descriptor.
* <p/>
* It first computes the new children of the descriptor and then update them
* all at once.
* <p/>
* TODO: differentiate groups from views in the tree UI? => rely on icons
* <p/>
*
* @param views The list of views in the framework.
* @param layouts The list of layouts in the framework.
*/
public synchronized void updateDescriptors(ViewClassInfo[] views, ViewClassInfo[] layouts) {
ArrayList<ElementDescriptor> newViews = new ArrayList<ElementDescriptor>();
if (views != null) {
for (ViewClassInfo info : views) {
ElementDescriptor desc = convertView(info);
newViews.add(desc);
}
}
// Create <include> as a synthetic regular view.
// Note: ViewStub is already described by attrs.xml
insertInclude(newViews);
ArrayList<ElementDescriptor> newLayouts = new ArrayList<ElementDescriptor>();
if (layouts != null) {
for (ViewClassInfo info : layouts) {
ElementDescriptor desc = convertView(info);
newLayouts.add(desc);
}
}
ArrayList<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
newDescriptors.addAll(newLayouts);
newDescriptors.addAll(newViews);
// Link all layouts to everything else here.. recursively
for (ElementDescriptor layoutDesc : newLayouts) {
layoutDesc.setChildren(newDescriptors);
}
// The <merge> tag can only be a root tag, so it is added at the end.
// It gets everything else as children but it is not made a child itself.
ElementDescriptor mergeTag = createMerge(newLayouts);
mergeTag.setChildren(newDescriptors); // mergeTag makes a copy of the list
newDescriptors.add(mergeTag);
newLayouts.add(mergeTag);
mViewDescriptors = newViews;
mLayoutDescriptors = newLayouts;
mRootDescriptor.setChildren(newDescriptors);
mROLayoutDescriptors = Collections.unmodifiableList(mLayoutDescriptors);
mROViewDescriptors = Collections.unmodifiableList(mViewDescriptors);
}
/**
* Creates an element descriptor from a given {@link ViewClassInfo}.
*/
private ElementDescriptor convertView(ViewClassInfo info) {
String xml_name = info.getShortClassName();
String tooltip = info.getJavaDoc();
ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>();
// All views and groups have an implicit "style" attribute which is a reference.
AttributeInfo styleInfo = new DeclareStyleableInfo.AttributeInfo(
"style", //$NON-NLS-1$ xmlLocalName
new DeclareStyleableInfo.AttributeInfo.Format[] {
DeclareStyleableInfo.AttributeInfo.Format.REFERENCE
});
styleInfo.setJavaDoc("A reference to a custom style"); //tooltip
DescriptorsUtils.appendAttribute(attributes,
"style", //$NON-NLS-1$
null, //nsUri
styleInfo,
false, //required
null); // overrides
// Process all View attributes
DescriptorsUtils.appendAttributes(attributes,
null, // elementName
SdkConstants.NS_RESOURCES,
info.getAttributes(),
null, // requiredAttributes
null /* overrides */);
for (ViewClassInfo link = info.getSuperClass();
link != null;
link = link.getSuperClass()) {
AttributeInfo[] attrList = link.getAttributes();
if (attrList.length > 0) {
attributes.add(new SeparatorAttributeDescriptor(
String.format("Attributes from %1$s", link.getShortClassName())));
DescriptorsUtils.appendAttributes(attributes,
null, // elementName
SdkConstants.NS_RESOURCES,
attrList,
null, // requiredAttributes
null /* overrides */);
}
}
// Process all LayoutParams attributes
ArrayList<AttributeDescriptor> layoutAttributes = new ArrayList<AttributeDescriptor>();
LayoutParamsInfo layoutParams = info.getLayoutData();
for(; layoutParams != null; layoutParams = layoutParams.getSuperClass()) {
boolean need_separator = true;
for (AttributeInfo attr_info : layoutParams.getAttributes()) {
if (DescriptorsUtils.containsAttribute(layoutAttributes,
SdkConstants.NS_RESOURCES, attr_info)) {
continue;
}
if (need_separator) {
String title;
if (layoutParams.getShortClassName().equals(
AndroidConstants.CLASS_NAME_LAYOUTPARAMS)) {
title = String.format("Layout Attributes from %1$s",
layoutParams.getViewLayoutClass().getShortClassName());
} else {
title = String.format("Layout Attributes from %1$s (%2$s)",
layoutParams.getViewLayoutClass().getShortClassName(),
layoutParams.getShortClassName());
}
layoutAttributes.add(new SeparatorAttributeDescriptor(title));
need_separator = false;
}
DescriptorsUtils.appendAttribute(layoutAttributes,
null, // elementName
SdkConstants.NS_RESOURCES,
attr_info,
false, // required
null /* overrides */);
}
}
return new ViewElementDescriptor(xml_name,
xml_name, // ui_name
info.getCanonicalClassName(),
tooltip,
null, // sdk_url
attributes.toArray(new AttributeDescriptor[attributes.size()]),
layoutAttributes.toArray(new AttributeDescriptor[layoutAttributes.size()]),
null, // children
false /* mandatory */);
}
/**
* Creates a new <include> descriptor and adds it to the list of view descriptors.
*
* @param knownViews A list of view descriptors being populated. Also used to find the
* View descriptor and extract its layout attributes.
*/
private void insertInclude(ArrayList<ElementDescriptor> knownViews) {
String xml_name = "include"; //$NON-NLS-1$
// Create the include custom attributes
ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>();
// Note that the "layout" attribute does NOT have the Android namespace
DescriptorsUtils.appendAttribute(attributes,
null, //elementXmlName
null, //nsUri
new AttributeInfo(
"layout", //$NON-NLS-1$
new AttributeInfo.Format[] { AttributeInfo.Format.REFERENCE }
),
true, //required
null); //overrides
DescriptorsUtils.appendAttribute(attributes,
null, //elementXmlName
SdkConstants.NS_RESOURCES, //nsUri
new AttributeInfo(
"id", //$NON-NLS-1$
new AttributeInfo.Format[] { AttributeInfo.Format.REFERENCE }
),
true, //required
null); //overrides
// Find View and inherit all its layout attributes
AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(
AndroidConstants.CLASS_VIEW, knownViews);
// Create the include descriptor
ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name
xml_name, // ui_name
null, // canonical class name, we don't have one
"Lets you statically include XML layouts inside other XML layouts.", // tooltip
null, // sdk_url
attributes.toArray(new AttributeDescriptor[attributes.size()]),
viewLayoutAttribs, // layout attributes
null, // children
false /* mandatory */);
knownViews.add(desc);
}
/**
* Creates and return a new <merge> descriptor.
* @param knownLayouts A list of all known layout view descriptors, used to find the
* FrameLayout descriptor and extract its layout attributes.
*/
private ElementDescriptor createMerge(ArrayList<ElementDescriptor> knownLayouts) {
String xml_name = "merge"; //$NON-NLS-1$
// Find View and inherit all its layout attributes
AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(
AndroidConstants.CLASS_FRAMELAYOUT, knownLayouts);
// Create the include descriptor
ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name
xml_name, // ui_name
null, // canonical class name, we don't have one
"A root tag useful for XML layouts inflated using a ViewStub.", // tooltip
null, // sdk_url
null, // attributes
viewLayoutAttribs, // layout attributes
null, // children
false /* mandatory */);
return desc;
}
/**
* Finds the descriptor and retrieves all its layout attributes.
*/
private AttributeDescriptor[] findViewLayoutAttributes(
String viewFqcn,
ArrayList<ElementDescriptor> knownViews) {
for (ElementDescriptor desc : knownViews) {
if (desc instanceof ViewElementDescriptor) {
ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
if (viewFqcn.equals(viewDesc.getCanonicalClassName())) {
return viewDesc.getLayoutAttributes();
}
}
}
return null;
}
}