import com.android.ide.eclipse.adt.internal.editors.layout.gle1.GraphicalLayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle1.UiContentOutlinePage;
import com.android.ide.eclipse.adt.internal.editors.layout.gle1.UiPropertySheetPage;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.OutlinePage2;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.OutlinePage2;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.PropertySheetPage2;
import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiActions;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
}
/**
- * Creates a new {@link ElementDescriptor} for an unknown XML local name
+ * Creates a new {@link ViewElementDescriptor} for an unknown XML local name
* (i.e. one that was not mapped by the current descriptors).
* <p/>
* Since we deal with layouts, we returns either a descriptor for a custom view
* or one for the base View.
*
* @param xmlLocalName The XML local name to match.
- * @return A non-null {@link ElementDescriptor}.
+ * @return A non-null {@link ViewElementDescriptor}.
*/
- private ElementDescriptor createUnknownDescriptor(String xmlLocalName) {
+ private ViewElementDescriptor createUnknownDescriptor(String xmlLocalName) {
+ ViewElementDescriptor desc = null;
IEditorInput editorInput = getEditorInput();
if (editorInput instanceof IFileEditorInput) {
IFileEditorInput fileInput = (IFileEditorInput)editorInput;
IProject project = fileInput.getFile().getProject();
// Check if we can find a custom view specific to this project.
- ElementDescriptor desc = CustomViewDescriptorService.getInstance().getDescriptor(
- project, xmlLocalName);
-
- if (desc != null) {
- return desc;
- }
-
- // If we didn't find a custom view, reuse the base View descriptor.
- // This is a layout after all, so every XML node should represent
- // a view.
-
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(project);
- if (target != null) {
- AndroidTargetData data = currentSdk.getTargetData(target);
- if (data != null) {
- // data can be null when the target is still loading
- desc = data.getLayoutDescriptors().getBaseViewDescriptor();
+ // This only works if there's an actual matching custom class in the project.
+ desc = CustomViewDescriptorService.getInstance().getDescriptor(project, xmlLocalName);
+
+ if (desc == null) {
+ // If we didn't find a custom view, create a synthetic one using the
+ // the base View descriptor as a model.
+ // This is a layout after all, so every XML node should represent
+ // a view.
+
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(project);
+ if (target != null) {
+ AndroidTargetData data = currentSdk.getTargetData(target);
+ if (data != null) {
+ // data can be null when the target is still loading
+ ViewElementDescriptor viewDesc =
+ data.getLayoutDescriptors().getBaseViewDescriptor();
+
+ desc = new ViewElementDescriptor(
+ xmlLocalName, // xml local name
+ xmlLocalName, // ui_name
+ xmlLocalName, // canonical class name
+ null, // tooltip
+ null, // sdk_url
+ viewDesc.getAttributes(),
+ viewDesc.getLayoutAttributes(),
+ null, // children
+ false /* mandatory */);
+ desc.setSuperClass(viewDesc);
+ }
}
}
}
-
- if (desc != null) {
- return desc;
- }
}
- // We get here if the editor input is not of the right type or if the
- // SDK hasn't finished loading. In either case, return something just
- // because we should not return null.
- return new ViewElementDescriptor(xmlLocalName, xmlLocalName);
+ if (desc == null) {
+ // We can only arrive here if the SDK's android target has not finished
+ // loading. Just create a dummy descriptor with no attributes to be able
+ // to continue.
+ desc = new ViewElementDescriptor(xmlLocalName, xmlLocalName);
+ }
+ return desc;
}
private void onDescriptorsChanged(Document document) {
* Will return null if we can't find that FQCN or we lack the editor/data/descriptors info.
*/
public ViewElementDescriptor getFqcnViewDescritor(String fqcn) {
+ ViewElementDescriptor desc = null;
+
AndroidTargetData data = getTargetData();
if (data != null) {
LayoutDescriptors layoutDesc = data.getLayoutDescriptors();
if (layoutDesc != null) {
DocumentDescriptor docDesc = layoutDesc.getDescriptor();
if (docDesc != null) {
- return internalFindFqcnViewDescritor(fqcn, docDesc.getChildren(), null);
+ desc = internalFindFqcnViewDescritor(fqcn, docDesc.getChildren(), null);
}
}
}
- return null;
+ if (desc == null) {
+ // We failed to find a descriptor for the given FQCN.
+ // Let's consider custom classes and create one as needed.
+ desc = createUnknownDescriptor(fqcn);
+ }
+
+ return desc;
}
/**
import java.util.List;
/**
- * Service responsible for creating/managing {@link ElementDescriptor} objects for custom
+ * 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 ElementDescriptor} object has been done for a specific
- * class.<br>
- * The monitoring will notify a listen of any changes in the class triggering a change in its
- * associated {@link ElementDescriptor} object.
+ * 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 {
* 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 ElementDescriptor}.
+ * {@link ViewElementDescriptor}.
*/
- private HashMap<IProject, HashMap<String, ElementDescriptor>> mCustomDescriptorMap =
- new HashMap<IProject, HashMap<String, ElementDescriptor>>();
+ private HashMap<IProject, HashMap<String, ViewElementDescriptor>> mCustomDescriptorMap =
+ new HashMap<IProject, HashMap<String, ViewElementDescriptor>>();
/**
- * TODO will be used to update the ElementDescriptor of the custom view when it
+ * 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")
*/
public interface ICustomViewDescriptorListener {
/**
- * Sent when a custom View class has changed and its {@link ElementDescriptor} was modified.
+ * 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, ElementDescriptor descriptor);
+ public void updatedClassInfo(IProject project,
+ String className,
+ ViewElementDescriptor descriptor);
}
/**
* Sets the listener receiving custom View class modification notifications.
* @param listener the listener to receive the notifications.
*
- * TODO will be used to update the ElementDescriptor of the custom view when it
+ * 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) {
}
/**
- * Returns the {@link ElementDescriptor} for a particular project/class.
+ * Returns the {@link ViewElementDescriptor} for a particular project/class when the
+ * fully qualified class name actually matches a class from the given project.
* <p/>
- * If it is the first time the <code>ElementDescriptor</code> is requested, the method
+ * 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 <code>ElementDescriptor</code> or <code>null</code> if the class was not
- * a custom View class.
+ * @return a {@link ViewElementDescriptor} or <code>null</code> if the class was not
+ * a custom View class.
*/
- public ElementDescriptor getDescriptor(IProject project, String fqcn) {
+ public ViewElementDescriptor getDescriptor(IProject project, String fqcn) {
// look in the map first
synchronized (mCustomDescriptorMap) {
- HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
+ HashMap<String, ViewElementDescriptor> map = mCustomDescriptorMap.get(project);
if (map != null) {
- ElementDescriptor descriptor = map.get(fqcn);
+ ViewElementDescriptor descriptor = map.get(fqcn);
if (descriptor != null) {
return descriptor;
}
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
new NullProgressMonitor());
- ElementDescriptor parentDescriptor = getDescriptor(
+ ViewElementDescriptor parentDescriptor = createViewDescriptor(
hierarchy.getSuperclass(type), project, hierarchy);
if (parentDescriptor != null) {
- // we have a valid parent, lets create a new ElementDescriptor.
+ // we have a valid parent, lets create a new ViewElementDescriptor.
ViewElementDescriptor descriptor = new ViewElementDescriptor(fqcn,
fqcn, // ui_name
null, // children
false /* mandatory */);
+ descriptor.setSuperClass(parentDescriptor);
+
synchronized (mCustomDescriptorMap) {
map = mCustomDescriptorMap.get(project);
if (map == null) {
- map = new HashMap<String, ElementDescriptor>();
+ map = new HashMap<String, ViewElementDescriptor>();
mCustomDescriptorMap.put(project, map);
}
}
}
-
return null;
}
/**
- * Computes (if needed) and returns the {@link ElementDescriptor} for the specified type.
+ * Computes (if needed) and returns the {@link ViewElementDescriptor} for the specified type.
*
- * @param type
- * @param project
- * @param typeHierarchy
- * @return A ViewElementDescriptor or null if type or typeHierarchy is null.
+ * @return A {@link ViewElementDescriptor} or null if type or typeHierarchy is null.
*/
- private ViewElementDescriptor getDescriptor(IType type, IProject project,
+ private ViewElementDescriptor createViewDescriptor(IType type, IProject project,
ITypeHierarchy typeHierarchy) {
// check if the type is a built-in View class.
List<ElementDescriptor> builtInList = null;
IType parentType = typeHierarchy.getSuperclass(type);
if (parentType != null) {
- ViewElementDescriptor parentDescriptor = getDescriptor(parentType, project,
+ ViewElementDescriptor parentDescriptor = createViewDescriptor(parentType, project,
typeHierarchy);
if (parentDescriptor != null) {
null, // children
false /* mandatory */);
+ descriptor.setSuperClass(parentDescriptor);
+
// add it to the map
synchronized (mCustomDescriptorMap) {
- HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
+ HashMap<String, ViewElementDescriptor> map = mCustomDescriptorMap.get(project);
if (map == null) {
- map = new HashMap<String, ElementDescriptor>();
+ map = new HashMap<String, ViewElementDescriptor>();
mCustomDescriptorMap.put(project, map);
}
* 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 ElementDescriptor} of the direct superclass.
+ * @param parentDescriptor the {@link ViewElementDescriptor} of the direct superclass.
*/
private AttributeDescriptor[] getAttributeDescriptor(IType type,
- ElementDescriptor parentDescriptor) {
+ ViewElementDescriptor parentDescriptor) {
// TODO add the class attribute descriptors to the parent descriptors.
return parentDescriptor.getAttributes();
}
private List<ElementDescriptor> mROViewDescriptors;
/** The descriptor matching android.view.View. */
- private ElementDescriptor mBaseViewDescriptor;
+ private ViewElementDescriptor mBaseViewDescriptor;
/** Returns the document descriptor. Contains all layouts and views linked together. */
public DocumentDescriptor getDescriptor() {
}
/**
- * Returns the descriptor matching android.view.View.
+ * Returns the descriptor matching android.view.View, which is guaranteed
+ * to be a {@link ViewElementDescriptor}.
*/
- public ElementDescriptor getBaseViewDescriptor() {
+ public ViewElementDescriptor getBaseViewDescriptor() {
if (mBaseViewDescriptor == null) {
for (ElementDescriptor desc : mViewDescriptors) {
if (desc instanceof ViewElementDescriptor) {
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor;
import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFolderListener;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import org.codehaus.groovy.control.CompilationFailedException;
// ---- private ---
+ /**
+ * Returns the descriptor for the base View class.
+ * This could be null if the SDK or the given platform target hasn't loaded yet.
+ */
+ private ViewElementDescriptor getBaseViewDescriptor() {
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(mProject);
+ if (target != null) {
+ AndroidTargetData data = currentSdk.getTargetData(target);
+ return data.getLayoutDescriptors().getBaseViewDescriptor();
+ }
+ }
+ return null;
+ }
+
private class ProjectFolderListener implements IFolderListener {
public void folderChanged(IFolder folder, int kind) {
if (folder.getProject() == mProject &&
private IViewRule loadRule(UiViewElementNode element) {
if (element == null) {
return null;
- } else {
- // sanity check. this can't fail.
- ElementDescriptor d = element.getDescriptor();
- if (d == null || !(d instanceof ViewElementDescriptor)) {
- return null;
- }
}
String targetFqcn = null;
- ViewElementDescriptor targetDesc = (ViewElementDescriptor) element.getDescriptor();
+ ViewElementDescriptor targetDesc = null;
+
+ ElementDescriptor d = element.getDescriptor();
+ if (d instanceof ViewElementDescriptor) {
+ targetDesc = (ViewElementDescriptor) d;
+ }
+ if (d == null || !(d instanceof ViewElementDescriptor)) {
+ // This should not happen. All views should have some kind of *view* element
+ // descriptor. Maybe the project is not complete and doesn't build or something.
+ // In this case, we'll use the descriptor of the base android View class.
+ targetDesc = getBaseViewDescriptor();
+ }
+
// Return the rule if we find it in the cache, even if it was stored as null
// (which means we didn't find it earlier, so don't look for it again)
// Get the FQCN of this View
String fqcn = desc.getFullClassName();
if (fqcn == null) {
+ // Shouldn't be happening.
return null;
}