diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java | 621 |
1 files changed, 0 insertions, 621 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java deleted file mode 100644 index 6df6929a7..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java +++ /dev/null @@ -1,621 +0,0 @@ -/* - * 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 static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.AUTO_URI; -import static com.android.SdkConstants.CLASS_VIEWGROUP; -import static com.android.SdkConstants.URI_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.ResourceFile; -import com.android.ide.common.resources.ResourceItem; -import com.android.ide.common.resources.platform.AttributeInfo; -import com.android.ide.common.resources.platform.AttrsXmlParser; -import com.android.ide.common.resources.platform.ViewClassInfo; -import com.android.ide.common.resources.platform.ViewClassInfo.LayoutParamsInfo; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -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.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; -import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.resources.ResourceType; -import com.android.sdklib.IAndroidTarget; -import com.google.common.collect.Maps; -import com.google.common.collect.ObjectArrays; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.IClassFile; -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 org.eclipse.swt.graphics.Image; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * 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 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 { - - 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 ViewElementDescriptor}. - */ - private HashMap<IProject, HashMap<String, ViewElementDescriptor>> mCustomDescriptorMap = - new HashMap<IProject, HashMap<String, ViewElementDescriptor>>(); - - /** - * 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") - 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 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, - ViewElementDescriptor 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 ViewElementDescriptor 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 ViewElementDescriptor} for a particular project/class when the - * fully qualified class name actually matches a class from the given project. - * <p/> - * 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 {@link ViewElementDescriptor} or <code>null</code> if the class was not - * a custom View class. - */ - public ViewElementDescriptor getDescriptor(IProject project, String fqcn) { - // look in the map first - synchronized (mCustomDescriptorMap) { - HashMap<String, ViewElementDescriptor> map = mCustomDescriptorMap.get(project); - - if (map != null) { - ViewElementDescriptor 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()); - - ViewElementDescriptor parentDescriptor = createViewDescriptor( - hierarchy.getSuperclass(type), project, hierarchy); - - if (parentDescriptor != null) { - // we have a valid parent, lets create a new ViewElementDescriptor. - List<AttributeDescriptor> attrList = new ArrayList<AttributeDescriptor>(); - List<AttributeDescriptor> paramList = new ArrayList<AttributeDescriptor>(); - Map<ResourceFile, Long> files = findCustomDescriptors(project, type, - attrList, paramList); - - AttributeDescriptor[] attributes = - getAttributeDescriptor(type, parentDescriptor); - if (!attrList.isEmpty()) { - attributes = join(attrList, attributes); - } - AttributeDescriptor[] layoutAttributes = - getLayoutAttributeDescriptors(type, parentDescriptor); - if (!paramList.isEmpty()) { - layoutAttributes = join(paramList, layoutAttributes); - } - String name = DescriptorsUtils.getBasename(fqcn); - ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn, - attributes, - layoutAttributes, - parentDescriptor.getChildren(), - project, files); - descriptor.setSuperClass(parentDescriptor); - - synchronized (mCustomDescriptorMap) { - map = mCustomDescriptorMap.get(project); - if (map == null) { - map = new HashMap<String, ViewElementDescriptor>(); - 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; - } - - private static AttributeDescriptor[] join( - @NonNull List<AttributeDescriptor> attributeList, - @NonNull AttributeDescriptor[] attributes) { - if (!attributeList.isEmpty()) { - return ObjectArrays.concat( - attributeList.toArray(new AttributeDescriptor[attributeList.size()]), - attributes, - AttributeDescriptor.class); - } else { - return attributes; - } - - } - - /** Cache used by {@link #getParser(ResourceFile)} */ - private Map<ResourceFile, AttrsXmlParser> mParserCache; - - private AttrsXmlParser getParser(ResourceFile file) { - if (mParserCache == null) { - mParserCache = new HashMap<ResourceFile, AttrsXmlParser>(); - } - - AttrsXmlParser parser = mParserCache.get(file); - if (parser == null) { - parser = new AttrsXmlParser( - file.getFile().getOsLocation(), - AdtPlugin.getDefault(), 20); - parser.preload(); - mParserCache.put(file, parser); - } - - return parser; - } - - /** Compute/find the styleable resources for the given type, if possible */ - private Map<ResourceFile, Long> findCustomDescriptors( - IProject project, - IType type, - List<AttributeDescriptor> customAttributes, - List<AttributeDescriptor> customLayoutAttributes) { - // Look up the project where the type is declared (could be a library project; - // we cannot use type.getJavaProject().getProject()) - IProject library = getProjectDeclaringType(type); - if (library == null) { - library = project; - } - - String className = type.getElementName(); - Set<ResourceFile> resourceFiles = findAttrsFiles(library, className); - if (resourceFiles != null && resourceFiles.size() > 0) { - String appUri = getAppResUri(project); - Map<ResourceFile, Long> timestamps = - Maps.newHashMapWithExpectedSize(resourceFiles.size()); - for (ResourceFile file : resourceFiles) { - AttrsXmlParser attrsXmlParser = getParser(file); - String fqcn = type.getFullyQualifiedName(); - - // Attributes - ViewClassInfo classInfo = new ViewClassInfo(true, fqcn, className); - attrsXmlParser.loadViewAttributes(classInfo); - appendAttributes(customAttributes, classInfo.getAttributes(), appUri); - - // Layout params - LayoutParamsInfo layoutInfo = new ViewClassInfo.LayoutParamsInfo( - classInfo, "Layout", null /*superClassInfo*/); //$NON-NLS-1$ - attrsXmlParser.loadLayoutParamsAttributes(layoutInfo); - appendAttributes(customLayoutAttributes, layoutInfo.getAttributes(), appUri); - - timestamps.put(file, file.getFile().getModificationStamp()); - } - - return timestamps; - } - - return null; - } - - /** - * Finds the set of XML files (if any) in the given library declaring - * attributes for the given class name - */ - @Nullable - private static Set<ResourceFile> findAttrsFiles(IProject library, String className) { - Set<ResourceFile> resourceFiles = null; - ResourceManager manager = ResourceManager.getInstance(); - ProjectResources resources = manager.getProjectResources(library); - if (resources != null) { - Collection<ResourceItem> items = - resources.getResourceItemsOfType(ResourceType.DECLARE_STYLEABLE); - for (ResourceItem item : items) { - String viewName = item.getName(); - if (viewName.equals(className) - || (viewName.startsWith(className) - && viewName.equals(className + "_Layout"))) { //$NON-NLS-1$ - if (resourceFiles == null) { - resourceFiles = new HashSet<ResourceFile>(); - } - resourceFiles.addAll(item.getSourceFileList()); - } - } - } - return resourceFiles; - } - - /** - * Find the project containing this type declaration. We cannot use - * {@link IType#getJavaProject()} since that will return the including - * project and we're after the library project such that we can find the - * attrs.xml file in the same project. - */ - @Nullable - private static IProject getProjectDeclaringType(IType type) { - IClassFile classFile = type.getClassFile(); - if (classFile != null) { - IPath path = classFile.getPath(); - IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); - IResource resource; - if (path.isAbsolute()) { - resource = AdtUtils.fileToResource(path.toFile()); - } else { - resource = workspace.findMember(path); - } - if (resource != null && resource.getProject() != null) { - return resource.getProject(); - } - } - - return null; - } - - /** Returns the name space to use for application attributes */ - private static String getAppResUri(IProject project) { - String appResource; - ProjectState projectState = Sdk.getProjectState(project); - if (projectState != null && projectState.isLibrary()) { - appResource = AUTO_URI; - } else { - ManifestInfo manifestInfo = ManifestInfo.get(project); - appResource = URI_PREFIX + manifestInfo.getPackage(); - } - return appResource; - } - - - /** Append the {@link AttributeInfo} objects converted {@link AttributeDescriptor} - * objects into the given attribute list. - * <p> - * This is nearly identical to - * {@link DescriptorsUtils#appendAttribute(List, String, String, AttributeInfo, boolean, Map)} - * but it handles namespace declarations in the attrs.xml file where the android: - * namespace is included in the names. - */ - private static void appendAttributes(List<AttributeDescriptor> attributes, - AttributeInfo[] attributeInfos, String appResource) { - // Custom attributes - for (AttributeInfo info : attributeInfos) { - String nsUri; - if (info.getName().startsWith(ANDROID_NS_NAME_PREFIX)) { - info.setName(info.getName().substring(ANDROID_NS_NAME_PREFIX.length())); - nsUri = ANDROID_URI; - } else { - nsUri = appResource; - } - - DescriptorsUtils.appendAttribute(attributes, - null /*elementXmlName*/, nsUri, info, false /*required*/, - null /*overrides*/); - } - } - - /** - * Computes (if needed) and returns the {@link ViewElementDescriptor} for the specified type. - * - * @return A {@link ViewElementDescriptor} or null if type or typeHierarchy is null. - */ - private ViewElementDescriptor createViewDescriptor(IType type, IProject project, - ITypeHierarchy typeHierarchy) { - // check if the type is a built-in View class. - List<ViewElementDescriptor> builtInList = null; - - // give up if there's no type - if (type == null) { - return null; - } - - String fqcn = type.getFullyQualifiedName(); - - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - IAndroidTarget target = currentSdk.getTarget(project); - if (target != null) { - AndroidTargetData data = currentSdk.getTargetData(target); - if (data != null) { - LayoutDescriptors descriptors = data.getLayoutDescriptors(); - ViewElementDescriptor d = descriptors.findDescriptorByClass(fqcn); - if (d != null) { - return d; - } - builtInList = descriptors.getViewDescriptors(); - } - } - } - - // 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 = createViewDescriptor(parentType, project, - typeHierarchy); - - if (parentDescriptor != null) { - // parent class is a valid View class with a descriptor, so we create one - // for this class. - String name = DescriptorsUtils.getBasename(fqcn); - // A custom view accepts children if its parent descriptor also does. - // The only exception to this is ViewGroup, which accepts children even though - // its parent does not. - boolean isViewGroup = fqcn.equals(CLASS_VIEWGROUP); - boolean hasChildren = isViewGroup || parentDescriptor.hasChildren(); - ViewElementDescriptor[] children = null; - if (hasChildren && builtInList != null) { - // We can't figure out what the allowable children are by just - // looking at the class, so assume any View is valid - children = builtInList.toArray(new ViewElementDescriptor[builtInList.size()]); - } - ViewElementDescriptor descriptor = new CustomViewDescriptor(name, fqcn, - getAttributeDescriptor(type, parentDescriptor), - getLayoutAttributeDescriptors(type, parentDescriptor), - children, project, null); - descriptor.setSuperClass(parentDescriptor); - - // add it to the map - synchronized (mCustomDescriptorMap) { - HashMap<String, ViewElementDescriptor> map = mCustomDescriptorMap.get(project); - - if (map == null) { - map = new HashMap<String, ViewElementDescriptor>(); - 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 ViewElementDescriptor} of the direct superclass. - */ - private static AttributeDescriptor[] getAttributeDescriptor(IType type, - ViewElementDescriptor parentDescriptor) { - // TODO add the class attribute descriptors to the parent descriptors. - return parentDescriptor.getAttributes(); - } - - private static AttributeDescriptor[] getLayoutAttributeDescriptors(IType type, - ViewElementDescriptor parentDescriptor) { - return parentDescriptor.getLayoutAttributes(); - } - - private class CustomViewDescriptor extends ViewElementDescriptor { - private Map<ResourceFile, Long> mTimeStamps; - private IProject mProject; - - public CustomViewDescriptor(String name, String fqcn, AttributeDescriptor[] attributes, - AttributeDescriptor[] layoutAttributes, - ElementDescriptor[] children, IProject project, - Map<ResourceFile, Long> timestamps) { - super( - fqcn, // xml name - name, // ui name - fqcn, // full class name - fqcn, // tooltip - null, // sdk_url - attributes, - layoutAttributes, - children, - false // mandatory - ); - mTimeStamps = timestamps; - mProject = project; - } - - @Override - public Image getGenericIcon() { - IconFactory iconFactory = IconFactory.getInstance(); - - int index = mXmlName.lastIndexOf('.'); - if (index != -1) { - return iconFactory.getIcon(mXmlName.substring(index + 1), - "customView"); //$NON-NLS-1$ - } - - return iconFactory.getIcon("customView"); //$NON-NLS-1$ - } - - @Override - public boolean syncAttributes() { - // Check if any of the descriptors - if (mTimeStamps != null) { - // Prevent checking actual file timestamps too frequently on rapid burst calls - long now = System.currentTimeMillis(); - if (now - sLastCheck < 1000) { - return true; - } - sLastCheck = now; - - // Check whether the resource files (typically just one) which defined - // custom attributes for this custom view have changed, and if so, - // refresh the attribute descriptors. - // This doesn't work the cases where you add descriptors for a custom - // view after using it, or add attributes in a separate file, but those - // scenarios aren't quite as common (and would require a bit more expensive - // analysis.) - for (Map.Entry<ResourceFile, Long> entry : mTimeStamps.entrySet()) { - ResourceFile file = entry.getKey(); - Long timestamp = entry.getValue(); - boolean recompute = false; - if (file.getFile().getModificationStamp() > timestamp.longValue()) { - // One or more attributes changed: recompute - recompute = true; - mParserCache.remove(file); - } - - if (recompute) { - IJavaProject javaProject = JavaCore.create(mProject); - String fqcn = getFullClassName(); - IType type = null; - try { - type = javaProject.findType(fqcn); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - if (type == null || !type.exists()) { - return true; - } - - List<AttributeDescriptor> attrList = new ArrayList<AttributeDescriptor>(); - List<AttributeDescriptor> paramList = new ArrayList<AttributeDescriptor>(); - - mTimeStamps = findCustomDescriptors(mProject, type, attrList, paramList); - - ViewElementDescriptor parentDescriptor = getSuperClassDesc(); - AttributeDescriptor[] attributes = - getAttributeDescriptor(type, parentDescriptor); - if (!attrList.isEmpty()) { - attributes = join(attrList, attributes); - } - attributes = attrList.toArray(new AttributeDescriptor[attrList.size()]); - setAttributes(attributes); - - return false; - } - } - } - - return true; - } - } - - /** Timestamp of the most recent {@link CustomViewDescriptor#syncAttributes} check */ - private static long sLastCheck; -} |