blob: 72fee7489b8a17fd83af34f45eb6429300b63718 [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.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.resources.ViewClassInfo;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import java.util.HashMap;
import java.util.List;
/**
* Service responsible for creating/managing {@link ElementDescriptor} 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.
* <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 {
private static CustomViewDescriptorService sThis = new 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}.
*/
private HashMap<IProject, HashMap<String, ElementDescriptor>> mCustomDescriptorMap =
new HashMap<IProject, HashMap<String, ElementDescriptor>>();
/**
* TODO will be used to update the ElementDescriptor of the custom view when it
* is modified (either the class itself or its attributes.xml)
*/
@SuppressWarnings("unused")
private ICustomViewDescriptorListener mListener;
/**
* Classes which implements this interface provide a method that deal with modifications
* in custom View class triggering a change in its associated {@link ViewClassInfo} object.
*/
public interface ICustomViewDescriptorListener {
/**
* Sent when a custom View class has changed and its {@link ElementDescriptor} 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);
}
/**
* Returns the singleton instance of {@link CustomViewDescriptorService}.
*/
public static CustomViewDescriptorService getInstance() {
return sThis;
}
/**
* 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
* is modified (either the class itself or its attributes.xml)
*/
public void setListener(ICustomViewDescriptorListener listener) {
mListener = listener;
}
/**
* Returns the {@link ElementDescriptor} for a particular project/class.
* <p/>
* If it is the first time the <code>ElementDescriptor</code> 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.
*/
public ElementDescriptor getDescriptor(IProject project, String fqcn) {
// look in the map first
synchronized (mCustomDescriptorMap) {
HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
if (map != null) {
ElementDescriptor descriptor = map.get(fqcn);
if (descriptor != null) {
return descriptor;
}
}
// if we step here, it looks like we haven't created it yet.
// First lets check this is in fact a valid type in the project
try {
// We expect the project to be both opened and of java type (since it's an android
// project), so we can create a IJavaProject object from our IProject.
IJavaProject javaProject = JavaCore.create(project);
// replace $ by . in the class name
String javaClassName = fqcn.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
// look for the IType object for this class
IType type = javaProject.findType(javaClassName);
if (type != null && type.exists()) {
// the type exists. Let's get the parent class and its ViewClassInfo.
// get the type hierarchy
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
new NullProgressMonitor());
ElementDescriptor parentDescriptor = getDescriptor(
hierarchy.getSuperclass(type), project, hierarchy);
if (parentDescriptor != null) {
// we have a valid parent, lets create a new ElementDescriptor.
ViewElementDescriptor descriptor = new ViewElementDescriptor(fqcn,
fqcn, // ui_name
fqcn, // canonical class name
null, // tooltip
null, // sdk_url
getAttributeDescriptor(type, parentDescriptor),
null, // layout attributes
null, // children
false /* mandatory */);
synchronized (mCustomDescriptorMap) {
map = mCustomDescriptorMap.get(project);
if (map == null) {
map = new HashMap<String, ElementDescriptor>();
mCustomDescriptorMap.put(project, map);
}
map.put(fqcn, descriptor);
}
//TODO setup listener on this resource change.
return descriptor;
}
}
} catch (JavaModelException e) {
// there was an error accessing any of the IType, we'll just return null;
}
}
return null;
}
/**
* Computes (if needed) and returns the {@link ElementDescriptor} for the specified type.
*
* @param type
* @param project
* @param typeHierarchy
* @return A ViewElementDescriptor or null if type or typeHierarchy is null.
*/
private ViewElementDescriptor getDescriptor(IType type, IProject project,
ITypeHierarchy typeHierarchy) {
// check if the type is a built-in View class.
List<ElementDescriptor> builtInList = null;
Sdk currentSdk = Sdk.getCurrent();
IAndroidTarget target = currentSdk == null ? null : currentSdk.getTarget(project);
if (target != null) {
AndroidTargetData data = currentSdk.getTargetData(target);
builtInList = data.getLayoutDescriptors().getViewDescriptors();
}
// give up if there's no type
if (type == null) {
return null;
}
String fqcn = type.getFullyQualifiedName();
if (builtInList != null) {
for (ElementDescriptor desc : builtInList) {
if (desc instanceof ViewElementDescriptor) {
ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc;
if (fqcn.equals(viewDescriptor.getFullClassName())) {
return viewDescriptor;
}
}
}
}
// it's not a built-in class? Lets look if the superclass is built-in
// give up if there's no type
if (typeHierarchy == null) {
return null;
}
IType parentType = typeHierarchy.getSuperclass(type);
if (parentType != null) {
ViewElementDescriptor parentDescriptor = getDescriptor(parentType, project,
typeHierarchy);
if (parentDescriptor != null) {
// parent class is a valid View class with a descriptor, so we create one
// for this class.
ViewElementDescriptor descriptor = new ViewElementDescriptor(fqcn,
fqcn, // ui_name
fqcn, // canonical name
null, // tooltip
null, // sdk_url
getAttributeDescriptor(type, parentDescriptor),
null, // layout attributes
null, // children
false /* mandatory */);
// add it to the map
synchronized (mCustomDescriptorMap) {
HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
if (map == null) {
map = new HashMap<String, ElementDescriptor>();
mCustomDescriptorMap.put(project, map);
}
map.put(fqcn, descriptor);
}
//TODO setup listener on this resource change.
return descriptor;
}
}
// class is neither a built-in view class, nor extend one. return null.
return null;
}
/**
* Returns the array of {@link AttributeDescriptor} for the specified {@link IType}.
* <p/>
* 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.
*/
private AttributeDescriptor[] getAttributeDescriptor(IType type,
ElementDescriptor parentDescriptor) {
// TODO add the class attribute descriptors to the parent descriptors.
return parentDescriptor.getAttributes();
}
}