diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources')
10 files changed, 0 insertions, 3406 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/CyclicDependencyValidator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/CyclicDependencyValidator.java deleted file mode 100644 index a2b13c6a8..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/CyclicDependencyValidator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2011 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.resources; - -import com.android.annotations.Nullable; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.dialogs.IInputValidator; - -import java.util.Collection; - -/** A validator which checks for cyclic dependencies */ -public class CyclicDependencyValidator implements IInputValidator { - private final Collection<String> mInvalidIds; - - private CyclicDependencyValidator(Collection<String> invalid) { - this.mInvalidIds = invalid; - } - - @Override - public String isValid(String newText) { - if (mInvalidIds.contains(newText)) { - return "Cyclic include, not valid"; - } - return null; - } - - /** - * Creates a validator which ensures that the chosen id is not for a layout that is - * directly or indirectly including the given layout. Used to avoid cyclic - * dependencies when offering layouts to be included within a given file, etc. - * - * @param file the target file that candidate layouts should not directly or - * indirectly include - * @return a validator which checks whether resource ids are valid or whether they - * could result in a cyclic dependency - */ - @Nullable - public static IInputValidator create(@Nullable IFile file) { - if (file == null) { - return null; - } - - IProject project = file.getProject(); - IncludeFinder includeFinder = IncludeFinder.get(project); - final Collection<String> invalid = - includeFinder.getInvalidIncludes(file); - - return new CyclicDependencyValidator(invalid); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java deleted file mode 100644 index b0e3d43d0..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright (C) 2011 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.resources; - -import static com.android.SdkConstants.ANDROID_PREFIX; -import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_COLOR; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_TYPE; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.EXT_XML; -import static com.android.SdkConstants.FD_RESOURCES; -import static com.android.SdkConstants.FD_RES_VALUES; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.TAG_RESOURCES; -import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.ResourceDeltaKind; -import com.android.ide.common.resources.ResourceResolver; -import com.android.ide.common.resources.ResourceUrl; -import com.android.ide.common.resources.configuration.CountryCodeQualifier; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.KeyboardStateQualifier; -import com.android.ide.common.resources.configuration.LayoutDirectionQualifier; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.ide.common.resources.configuration.NavigationMethodQualifier; -import com.android.ide.common.resources.configuration.NavigationStateQualifier; -import com.android.ide.common.resources.configuration.NetworkCodeQualifier; -import com.android.ide.common.resources.configuration.NightModeQualifier; -import com.android.ide.common.resources.configuration.ResourceQualifier; -import com.android.ide.common.resources.configuration.ScreenDimensionQualifier; -import com.android.ide.common.resources.configuration.ScreenHeightQualifier; -import com.android.ide.common.resources.configuration.ScreenOrientationQualifier; -import com.android.ide.common.resources.configuration.ScreenRatioQualifier; -import com.android.ide.common.resources.configuration.ScreenSizeQualifier; -import com.android.ide.common.resources.configuration.ScreenWidthQualifier; -import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier; -import com.android.ide.common.resources.configuration.TextInputMethodQualifier; -import com.android.ide.common.resources.configuration.TouchScreenQualifier; -import com.android.ide.common.resources.configuration.UiModeQualifier; -import com.android.ide.common.resources.configuration.VersionQualifier; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.Hyperlinks; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; -import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.VisualRefactoring; -import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceDelta; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.Region; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.RGB; -import org.eclipse.wst.sse.core.StructuredModelManager; -import org.eclipse.wst.sse.core.internal.provisional.IModelManager; -import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; -import org.eclipse.wst.xml.core.internal.document.ElementImpl; -import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.Text; -import org.xml.sax.InputSource; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -/** - * Helper class to deal with SWT specifics for the resources. - */ -@SuppressWarnings("restriction") // XML model -public class ResourceHelper { - - private final static Map<Class<?>, Image> sIconMap = new HashMap<Class<?>, Image>( - FolderConfiguration.getQualifierCount()); - - static { - try { - IconFactory factory = IconFactory.getInstance(); - sIconMap.put(CountryCodeQualifier.class, factory.getIcon("mcc")); //$NON-NLS-1$ - sIconMap.put(NetworkCodeQualifier.class, factory.getIcon("mnc")); //$NON-NLS-1$ - sIconMap.put(LocaleQualifier.class, factory.getIcon("language")); //$NON-NLS-1$ - sIconMap.put(LayoutDirectionQualifier.class, factory.getIcon("bidi")); //$NON-NLS-1$ - sIconMap.put(ScreenSizeQualifier.class, factory.getIcon("size")); //$NON-NLS-1$ - sIconMap.put(ScreenRatioQualifier.class, factory.getIcon("ratio")); //$NON-NLS-1$ - sIconMap.put(ScreenOrientationQualifier.class, factory.getIcon("orientation")); //$NON-NLS-1$ - sIconMap.put(UiModeQualifier.class, factory.getIcon("dockmode")); //$NON-NLS-1$ - sIconMap.put(NightModeQualifier.class, factory.getIcon("nightmode")); //$NON-NLS-1$ - sIconMap.put(DensityQualifier.class, factory.getIcon("dpi")); //$NON-NLS-1$ - sIconMap.put(TouchScreenQualifier.class, factory.getIcon("touch")); //$NON-NLS-1$ - sIconMap.put(KeyboardStateQualifier.class, factory.getIcon("keyboard")); //$NON-NLS-1$ - sIconMap.put(TextInputMethodQualifier.class, factory.getIcon("text_input")); //$NON-NLS-1$ - sIconMap.put(NavigationStateQualifier.class, factory.getIcon("navpad")); //$NON-NLS-1$ - sIconMap.put(NavigationMethodQualifier.class, factory.getIcon("navpad")); //$NON-NLS-1$ - sIconMap.put(ScreenDimensionQualifier.class, factory.getIcon("dimension")); //$NON-NLS-1$ - sIconMap.put(VersionQualifier.class, factory.getIcon("version")); //$NON-NLS-1$ - sIconMap.put(ScreenWidthQualifier.class, factory.getIcon("width")); //$NON-NLS-1$ - sIconMap.put(ScreenHeightQualifier.class, factory.getIcon("height")); //$NON-NLS-1$ - sIconMap.put(SmallestScreenWidthQualifier.class,factory.getIcon("swidth")); //$NON-NLS-1$ - } catch (Throwable t) { - AdtPlugin.log(t , null); - } - } - - /** - * Returns the icon for the qualifier. - */ - public static Image getIcon(Class<? extends ResourceQualifier> theClass) { - return sIconMap.get(theClass); - } - - /** - * Returns a {@link ResourceDeltaKind} from an {@link IResourceDelta} value. - * @param kind a {@link IResourceDelta} integer constant. - * @return a matching {@link ResourceDeltaKind} or null. - * - * @see IResourceDelta#ADDED - * @see IResourceDelta#REMOVED - * @see IResourceDelta#CHANGED - */ - public static ResourceDeltaKind getResourceDeltaKind(int kind) { - switch (kind) { - case IResourceDelta.ADDED: - return ResourceDeltaKind.ADDED; - case IResourceDelta.REMOVED: - return ResourceDeltaKind.REMOVED; - case IResourceDelta.CHANGED: - return ResourceDeltaKind.CHANGED; - } - - return null; - } - - /** - * Is this a resource that can be defined in any file within the "values" folder? - * <p> - * Some resource types can be defined <b>both</b> as a separate XML file as well - * as defined within a value XML file. This method will return true for these types - * as well. In other words, a ResourceType can return true for both - * {@link #isValueBasedResourceType} and {@link #isFileBasedResourceType}. - * - * @param type the resource type to check - * @return true if the given resource type can be represented as a value under the - * values/ folder - */ - public static boolean isValueBasedResourceType(ResourceType type) { - List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type); - for (ResourceFolderType folderType : folderTypes) { - if (folderType == ResourceFolderType.VALUES) { - return true; - } - } - - return false; - } - - /** - * Is this a resource that is defined in a file named by the resource plus the XML - * extension? - * <p> - * Some resource types can be defined <b>both</b> as a separate XML file as well as - * defined within a value XML file along with other properties. This method will - * return true for these resource types as well. In other words, a ResourceType can - * return true for both {@link #isValueBasedResourceType} and - * {@link #isFileBasedResourceType}. - * - * @param type the resource type to check - * @return true if the given resource type is stored in a file named by the resource - */ - public static boolean isFileBasedResourceType(ResourceType type) { - List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type); - for (ResourceFolderType folderType : folderTypes) { - if (folderType != ResourceFolderType.VALUES) { - - if (type == ResourceType.ID) { - // The folder types for ID is not only VALUES but also - // LAYOUT and MENU. However, unlike resources, they are only defined - // inline there so for the purposes of isFileBasedResourceType - // (where the intent is to figure out files that are uniquely identified - // by a resource's name) this method should return false anyway. - return false; - } - - return true; - } - } - - return false; - } - - /** - * Returns true if this class can create the given resource - * - * @param resource the resource to be created - * @return true if the {@link #createResource} method can create this resource - */ - public static boolean canCreateResource(String resource) { - // Cannot create framework resources - if (resource.startsWith(ANDROID_PREFIX)) { - return false; - } - - ResourceUrl parsed = ResourceUrl.parse(resource); - if (parsed != null) { - if (parsed.framework) { - return false; - } - ResourceType type = parsed.type; - String name = parsed.name; - - // Make sure the name is valid - ResourceNameValidator validator = - ResourceNameValidator.create(false, (Set<String>) null /* existing */, type); - if (validator.isValid(name) != null) { - return false; - } - - return canCreateResourceType(type); - } - - return false; - } - - /** - * Returns true if this class can create resources of the given resource - * type - * - * @param type the type of resource to be created - * @return true if the {@link #createResource} method can create resources - * of this type (provided the name parameter is also valid) - */ - public static boolean canCreateResourceType(ResourceType type) { - // We can create all value types - if (isValueBasedResourceType(type)) { - return true; - } - - // We can create -some- file-based types - those supported by the New XML wizard: - for (ResourceFolderType folderType : FolderTypeRelationship.getRelatedFolders(type)) { - if (NewXmlFileWizard.canCreateXmlFile(folderType)) { - return true; - } - } - - return false; - } - - /** Creates a file-based resource, like a layout. Used by {@link #createResource} */ - private static Pair<IFile,IRegion> createFileResource(IProject project, ResourceType type, - String name) { - - ResourceFolderType folderType = null; - for (ResourceFolderType f : FolderTypeRelationship.getRelatedFolders(type)) { - if (NewXmlFileWizard.canCreateXmlFile(f)) { - folderType = f; - break; - } - } - if (folderType == null) { - return null; - } - - // Find "dimens.xml" file in res/values/ (or corresponding name for other - // value types) - IPath projectPath = new Path(FD_RESOURCES + WS_SEP + folderType.getName() + WS_SEP - + name + '.' + EXT_XML); - IFile file = project.getFile(projectPath); - return NewXmlFileWizard.createXmlFile(project, file, folderType); - } - - /** - * Creates a resource of a given type, name and (if applicable) value - * - * @param project the project to contain the resource - * @param type the type of resource - * @param name the name of the resource - * @param value the value of the resource, if it is a value-type resource - * @return a pair of the file containing the resource and a region where the value - * appears - */ - public static Pair<IFile,IRegion> createResource(IProject project, ResourceType type, - String name, String value) { - if (!isValueBasedResourceType(type)) { - return createFileResource(project, type, name); - } - - // Find "dimens.xml" file in res/values/ (or corresponding name for other - // value types) - String typeName = type.getName(); - String fileName = typeName + 's'; - String projectPath = FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP - + fileName + '.' + EXT_XML; - Object editRequester = project; - IResource member = project.findMember(projectPath); - String tagName = Hyperlinks.getTagName(type); - boolean createEmptyTag = type == ResourceType.ID; - if (member != null) { - if (member instanceof IFile) { - IFile file = (IFile) member; - // File exists: Must add item to the XML - IModelManager manager = StructuredModelManager.getModelManager(); - IStructuredModel model = null; - try { - model = manager.getExistingModelForEdit(file); - if (model == null) { - model = manager.getModelForEdit(file); - } - if (model instanceof IDOMModel) { - model.beginRecording(editRequester, String.format("Add %1$s", - type.getDisplayName())); - IDOMModel domModel = (IDOMModel) model; - Document document = domModel.getDocument(); - Element root = document.getDocumentElement(); - IStructuredDocument structuredDocument = model.getStructuredDocument(); - Node lastElement = null; - NodeList childNodes = root.getChildNodes(); - String indent = null; - for (int i = childNodes.getLength() - 1; i >= 0; i--) { - Node node = childNodes.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - lastElement = node; - indent = AndroidXmlEditor.getIndent(structuredDocument, node); - break; - } - } - if (indent == null || indent.length() == 0) { - indent = " "; //$NON-NLS-1$ - } - Node nextChild = lastElement != null ? lastElement.getNextSibling() : null; - Text indentNode = document.createTextNode('\n' + indent); - root.insertBefore(indentNode, nextChild); - Element element = document.createElement(tagName); - if (createEmptyTag) { - if (element instanceof ElementImpl) { - ElementImpl elementImpl = (ElementImpl) element; - elementImpl.setEmptyTag(true); - } - } - element.setAttribute(ATTR_NAME, name); - if (!tagName.equals(typeName)) { - element.setAttribute(ATTR_TYPE, typeName); - } - root.insertBefore(element, nextChild); - IRegion region = null; - - if (createEmptyTag) { - IndexedRegion domRegion = VisualRefactoring.getRegion(element); - int endOffset = domRegion.getEndOffset(); - region = new Region(endOffset, 0); - } else { - Node valueNode = document.createTextNode(value); - element.appendChild(valueNode); - - IndexedRegion domRegion = VisualRefactoring.getRegion(valueNode); - int startOffset = domRegion.getStartOffset(); - int length = domRegion.getLength(); - region = new Region(startOffset, length); - } - model.save(); - return Pair.of(file, region); - } - } catch (Exception e) { - AdtPlugin.log(e, "Cannot access XML value model"); - } finally { - if (model != null) { - model.endRecording(editRequester); - model.releaseFromEdit(); - } - } - } - - return null; - } else { - // No such file exists: just create it - String prolog = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$ - StringBuilder sb = new StringBuilder(prolog); - - String root = TAG_RESOURCES; - sb.append('<').append(root).append('>').append('\n'); - sb.append(" "); //$NON-NLS-1$ - sb.append('<'); - sb.append(tagName); - sb.append(" name=\""); //$NON-NLS-1$ - sb.append(name); - sb.append('"'); - if (!tagName.equals(typeName)) { - sb.append(" type=\""); //$NON-NLS-1$ - sb.append(typeName); - sb.append('"'); - } - int start, end; - if (createEmptyTag) { - sb.append("/>"); //$NON-NLS-1$ - start = sb.length(); - end = sb.length(); - } else { - sb.append('>'); - start = sb.length(); - sb.append(value); - end = sb.length(); - sb.append('<').append('/'); - sb.append(tagName); - sb.append('>'); - } - sb.append('\n').append('<').append('/').append(root).append('>').append('\n'); - String result = sb.toString(); - // TODO: Pretty print string (wait until that CL is integrated) - String error = null; - try { - byte[] buf = result.getBytes("UTF8"); //$NON-NLS-1$ - InputStream stream = new ByteArrayInputStream(buf); - IFile file = project.getFile(new Path(projectPath)); - file.create(stream, true /*force*/, null /*progress*/); - IRegion region = new Region(start, end - start); - return Pair.of(file, region); - } catch (UnsupportedEncodingException e) { - error = e.getMessage(); - } catch (CoreException e) { - error = e.getMessage(); - } - - error = String.format("Failed to generate %1$s: %2$s", name, error); - AdtPlugin.displayError("New Android XML File", error); - } - return null; - } - - /** - * Returns the theme name to be shown for theme styles, e.g. for "@style/Theme" it - * returns "Theme" - * - * @param style a theme style string - * @return the user visible theme name - */ - public static String styleToTheme(String style) { - if (style.startsWith(STYLE_RESOURCE_PREFIX)) { - style = style.substring(STYLE_RESOURCE_PREFIX.length()); - } else if (style.startsWith(ANDROID_STYLE_RESOURCE_PREFIX)) { - style = style.substring(ANDROID_STYLE_RESOURCE_PREFIX.length()); - } else if (style.startsWith(PREFIX_RESOURCE_REF)) { - // @package:style/foo - int index = style.indexOf('/'); - if (index != -1) { - style = style.substring(index + 1); - } - } - return style; - } - - /** - * Returns true if the given style represents a project theme - * - * @param style a theme style string - * @return true if the style string represents a project theme, as opposed - * to a framework theme - */ - public static boolean isProjectStyle(String style) { - assert style.startsWith(STYLE_RESOURCE_PREFIX) - || style.startsWith(ANDROID_STYLE_RESOURCE_PREFIX) : style; - - return style.startsWith(STYLE_RESOURCE_PREFIX); - } - - /** - * Returns the layout resource name for the given layout file, e.g. for - * /res/layout/foo.xml returns foo. - * - * @param layoutFile the layout file whose name we want to look up - * @return the layout name - */ - public static String getLayoutName(IFile layoutFile) { - String layoutName = layoutFile.getName(); - int dotIndex = layoutName.indexOf('.'); - if (dotIndex != -1) { - layoutName = layoutName.substring(0, dotIndex); - } - return layoutName; - } - - /** - * Tries to resolve the given resource value to an actual RGB color. For state lists - * it will pick the simplest/fallback color. - * - * @param resources the resource resolver to use to follow color references - * @param color the color to resolve - * @return the corresponding {@link RGB} color, or null - */ - public static RGB resolveColor(ResourceResolver resources, ResourceValue color) { - color = resources.resolveResValue(color); - if (color == null) { - return null; - } - String value = color.getValue(); - - while (value != null) { - if (value.startsWith("#")) { //$NON-NLS-1$ - try { - int rgba = ImageUtils.getColor(value); - // Drop alpha channel - return ImageUtils.intToRgb(rgba); - } catch (NumberFormatException nfe) { - // Pass - } - return null; - } - if (value.startsWith(PREFIX_RESOURCE_REF)) { - boolean isFramework = color.isFramework(); - color = resources.findResValue(value, isFramework); - if (color != null) { - value = color.getValue(); - } else { - break; - } - } else { - File file = new File(value); - if (file.exists() && file.getName().endsWith(DOT_XML)) { - // Parse - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - BufferedInputStream bis = null; - try { - bis = new BufferedInputStream(new FileInputStream(file)); - InputSource is = new InputSource(bis); - factory.setNamespaceAware(true); - factory.setValidating(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.parse(is); - NodeList items = document.getElementsByTagName(TAG_ITEM); - - value = findColorValue(items); - continue; - } catch (Exception e) { - AdtPlugin.log(e, "Failed parsing color file %1$s", file.getName()); - } finally { - if (bis != null) { - try { - bis.close(); - } catch (IOException e) { - // Nothing useful can be done here - } - } - } - } - - return null; - } - } - - return null; - } - - /** - * Searches a color XML file for the color definition element that does not - * have an associated state and returns its color - */ - private static String findColorValue(NodeList items) { - for (int i = 0, n = items.getLength(); i < n; i++) { - // Find non-state color definition - Node item = items.item(i); - boolean hasState = false; - if (item.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) item; - if (element.hasAttributeNS(ANDROID_URI, ATTR_COLOR)) { - NamedNodeMap attributes = element.getAttributes(); - for (int j = 0, m = attributes.getLength(); j < m; j++) { - Attr attribute = (Attr) attributes.item(j); - if (attribute.getLocalName().startsWith("state_")) { //$NON-NLS-1$ - hasState = true; - break; - } - } - - if (!hasState) { - return element.getAttributeNS(ANDROID_URI, ATTR_COLOR); - } - } - } - } - - return null; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java deleted file mode 100644 index 5ea1edc0e..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2010 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.resources; - -import static com.android.SdkConstants.DOT_XML; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.ResourceItem; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; -import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; -import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.jdt.core.JavaConventions; -import org.eclipse.jface.dialogs.IInputValidator; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/** - * Validator which ensures that new Android resource names are valid. - */ -public class ResourceNameValidator implements IInputValidator { - /** Set of existing names to check for conflicts with */ - private Set<String> mExisting; - - /** If true, the validated name must be unique */ - private boolean mUnique = true; - - /** If true, the validated name must exist */ - private boolean mExist; - - /** - * True if the resource name being considered is a "file" based resource (where the - * resource name is the actual file name, rather than just a value attribute inside an - * XML file name of arbitrary name - */ - private boolean mIsFileType; - - /** - * True if the resource type can point to image resources - */ - private boolean mIsImageType; - - /** If true, allow .xml as a name suffix */ - private boolean mAllowXmlExtension; - - private ResourceNameValidator(boolean allowXmlExtension, Set<String> existing, - boolean isFileType, boolean isImageType) { - mAllowXmlExtension = allowXmlExtension; - mExisting = existing; - mIsFileType = isFileType; - mIsImageType = isImageType; - } - - /** - * Makes the resource name validator require that names are unique. - * - * @return this, for construction chaining - */ - public ResourceNameValidator unique() { - mUnique = true; - mExist = false; - - return this; - } - - /** - * Makes the resource name validator require that names already exist - * - * @return this, for construction chaining - */ - public ResourceNameValidator exist() { - mExist = true; - mUnique = false; - - return this; - } - - @Override - public String isValid(String newText) { - // IValidator has the same interface as SWT's IInputValidator - try { - if (newText == null || newText.trim().length() == 0) { - return "Enter a new name"; - } - - if (mAllowXmlExtension && newText.endsWith(DOT_XML)) { - newText = newText.substring(0, newText.length() - DOT_XML.length()); - } - - if (mAllowXmlExtension && mIsImageType - && ImageUtils.hasImageExtension(newText)) { - newText = newText.substring(0, newText.lastIndexOf('.')); - } - - if (!mIsFileType) { - newText = newText.replace('.', '_'); - } - - if (newText.indexOf('.') != -1 && !newText.endsWith(DOT_XML)) { - if (mIsImageType) { - return "The filename must end with .xml or .png"; - } else { - return "The filename must end with .xml"; - } - } - - // Resource names must be valid Java identifiers, since they will - // be represented as Java identifiers in the R file: - if (!Character.isJavaIdentifierStart(newText.charAt(0))) { - return "The resource name must begin with a character"; - } - for (int i = 1, n = newText.length(); i < n; i++) { - char c = newText.charAt(i); - if (!Character.isJavaIdentifierPart(c)) { - return String.format("'%1$c' is not a valid resource name character", c); - } - } - - if (mIsFileType) { - char first = newText.charAt(0); - if (!(first >= 'a' && first <= 'z')) { - return String.format( - "File-based resource names must start with a lowercase letter."); - } - - // AAPT only allows lowercase+digits+_: - // "%s: Invalid file name: must contain only [a-z0-9_.]"," - for (int i = 0, n = newText.length(); i < n; i++) { - char c = newText.charAt(i); - if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')) { - return String.format( - "File-based resource names must contain only lowercase a-z, 0-9, or _."); - } - } - } - - String level = "1.5"; //$NON-NLS-1$ - IStatus validIdentifier = JavaConventions.validateIdentifier(newText, level, level); - if (!validIdentifier.isOK()) { - return String.format("%1$s is not a valid name (reserved Java keyword)", newText); - } - - - if (mExisting != null && (mUnique || mExist)) { - boolean exists = mExisting.contains(newText); - if (mUnique && exists) { - return String.format("%1$s already exists", newText); - } else if (mExist && !exists) { - return String.format("%1$s does not exist", newText); - } - } - - return null; - } catch (Exception e) { - AdtPlugin.log(e, "Validation failed: %s", e.toString()); - return ""; //$NON-NLS-1$ - } - } - - /** - * Creates a new {@link ResourceNameValidator} - * - * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the - * resource name - * @param type the resource type of the resource name being validated - * @return a new {@link ResourceNameValidator} - */ - public static ResourceNameValidator create(boolean allowXmlExtension, - ResourceFolderType type) { - boolean isFileType = type != ResourceFolderType.VALUES; - return new ResourceNameValidator(allowXmlExtension, null, isFileType, - type == ResourceFolderType.DRAWABLE); - } - - /** - * Creates a new {@link ResourceNameValidator} - * - * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the - * resource name - * @param existing An optional set of names that already exist (and therefore will not - * be considered valid if entered as the new name) - * @param type the resource type of the resource name being validated - * @return a new {@link ResourceNameValidator} - */ - public static ResourceNameValidator create(boolean allowXmlExtension, Set<String> existing, - ResourceType type) { - boolean isFileType = ResourceHelper.isFileBasedResourceType(type); - return new ResourceNameValidator(allowXmlExtension, existing, isFileType, - type == ResourceType.DRAWABLE).unique(); - } - - /** - * Creates a new {@link ResourceNameValidator}. By default, the name will need to be - * unique in the project. - * - * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the - * resource name - * @param project the project to validate new resource names for - * @param type the resource type of the resource name being validated - * @return a new {@link ResourceNameValidator} - */ - public static ResourceNameValidator create(boolean allowXmlExtension, - @Nullable IProject project, - @NonNull ResourceType type) { - Set<String> existing = null; - if (project != null) { - existing = new HashSet<String>(); - ResourceManager manager = ResourceManager.getInstance(); - ProjectResources projectResources = manager.getProjectResources(project); - Collection<ResourceItem> items = projectResources.getResourceItemsOfType(type); - for (ResourceItem item : items) { - existing.add(item.getName()); - } - } - - boolean isFileType = ResourceHelper.isFileBasedResourceType(type); - return new ResourceNameValidator(allowXmlExtension, existing, isFileType, - type == ResourceType.DRAWABLE); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java deleted file mode 100644 index ab5ae4070..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/CompiledResourcesMonitor.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2007 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.resources.manager; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.IntArrayWrapper; -import com.android.ide.common.xml.ManifestData; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; -import com.android.ide.eclipse.adt.internal.project.ProjectHelper; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IProjectListener; -import com.android.resources.ResourceType; -import com.android.util.Pair; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IMarkerDelta; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceDelta; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IStatus; - -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Pattern; - -/** - * A monitor for the compiled resources. This only monitors changes in the resources of type - * {@link ResourceType#ID}. - */ -public final class CompiledResourcesMonitor implements IFileListener, IProjectListener { - - private final static CompiledResourcesMonitor sThis = new CompiledResourcesMonitor(); - - /** - * Sets up the monitoring system. - * @param monitor The main Resource Monitor. - */ - public static void setupMonitor(GlobalProjectMonitor monitor) { - monitor.addFileListener(sThis, IResourceDelta.ADDED | IResourceDelta.CHANGED); - monitor.addProjectListener(sThis); - } - - /** - * private constructor to prevent construction. - */ - private CompiledResourcesMonitor() { - } - - - /* (non-Javadoc) - * Sent when a file changed : if the file is the R class, then it is parsed again to update - * the internal data. - * - * @param file The file that changed. - * @param markerDeltas The marker deltas for the file. - * @param kind The change kind. This is equivalent to - * {@link IResourceDelta#accept(IResourceDeltaVisitor)} - * - * @see IFileListener#fileChanged - */ - @Override - public void fileChanged(@NonNull IFile file, @NonNull IMarkerDelta[] markerDeltas, - int kind, @Nullable String extension, int flags, boolean isAndroidProject) { - if (!isAndroidProject || flags == IResourceDelta.MARKERS) { - // Not Android or only the markers changed: not relevant - return; - } - - IProject project = file.getProject(); - - if (file.getName().equals(SdkConstants.FN_COMPILED_RESOURCE_CLASS)) { - // create the classname - String className = getRClassName(project); - if (className == null) { - // We need to abort. - AdtPlugin.log(IStatus.ERROR, - "fileChanged: failed to find manifest package for project %1$s", //$NON-NLS-1$ - project.getName()); - return; - } - // path will begin with /projectName/bin/classes so we'll ignore that - IPath relativeClassPath = file.getFullPath().removeFirstSegments(3); - if (packagePathMatches(relativeClassPath.toString(), className)) { - loadAndParseRClass(project, className); - } - } - } - - /** - * Check to see if the package section of the given path matches the packageName. - * For example, /project/bin/classes/com/foo/app/R.class should match com.foo.app.R - * @param path the pathname of the file to look at - * @param packageName the package qualified name of the class - * @return true if the package section of the path matches the package qualified name - */ - private boolean packagePathMatches(String path, String packageName) { - // First strip the ".class" off the end of the path - String pathWithoutExtension = path.substring(0, path.indexOf(SdkConstants.DOT_CLASS)); - - // then split the components of each path by their separators - String [] pathArray = pathWithoutExtension.split(Pattern.quote(File.separator)); - String [] packageArray = packageName.split(AdtConstants.RE_DOT); - - - int pathIndex = 0; - int packageIndex = 0; - - while (pathIndex < pathArray.length && packageIndex < packageArray.length) { - if (pathArray[pathIndex].equals(packageArray[packageIndex]) == false) { - return false; - } - pathIndex++; - packageIndex++; - } - // We may have matched all the way up to this point, but we're not sure it's a match - // unless BOTH paths done - return (pathIndex == pathArray.length && packageIndex == packageArray.length); - } - - /** - * Processes project close event. - */ - @Override - public void projectClosed(IProject project) { - // the ProjectResources object will be removed by the ResourceManager. - } - - /** - * Processes project delete event. - */ - @Override - public void projectDeleted(IProject project) { - // the ProjectResources object will be removed by the ResourceManager. - } - - /** - * Processes project open event. - */ - @Override - public void projectOpened(IProject project) { - // when the project is opened, we get an ADDED event for each file, so we don't - // need to do anything here. - } - - @Override - public void projectRenamed(IProject project, IPath from) { - // renamed projects also trigger delete/open event, - // so nothing to be done here. - } - - /** - * Processes existing project at init time. - */ - @Override - public void projectOpenedWithWorkspace(IProject project) { - try { - // check this is an android project - if (project.hasNature(AdtConstants.NATURE_DEFAULT)) { - String className = getRClassName(project); - // Find the classname - if (className == null) { - // We need to abort. - AdtPlugin.log(IStatus.ERROR, - "projectOpenedWithWorkspace: failed to find manifest package for project %1$s", //$NON-NLS-1$ - project.getName()); - return; - } - loadAndParseRClass(project, className); - } - } catch (CoreException e) { - // pass - } - } - - @Override - public void allProjectsOpenedWithWorkspace() { - // nothing to do. - } - - - private void loadAndParseRClass(IProject project, String className) { - try { - // first check there's a ProjectResources to store the content - ProjectResources projectResources = ResourceManager.getInstance().getProjectResources( - project); - - if (projectResources != null) { - // create a temporary class loader to load the class - ProjectClassLoader loader = new ProjectClassLoader(null /* parentClassLoader */, - project); - - try { - Class<?> clazz = loader.loadClass(className); - - if (clazz != null) { - // create the maps to store the result of the parsing - Map<ResourceType, Map<String, Integer>> resourceValueMap = - new EnumMap<ResourceType, Map<String, Integer>>(ResourceType.class); - Map<Integer, Pair<ResourceType, String>> genericValueToNameMap = - new HashMap<Integer, Pair<ResourceType, String>>(); - Map<IntArrayWrapper, String> styleableValueToNameMap = - new HashMap<IntArrayWrapper, String>(); - - // parse the class - if (parseClass(clazz, genericValueToNameMap, styleableValueToNameMap, - resourceValueMap)) { - // now we associate the maps to the project. - projectResources.setCompiledResources(genericValueToNameMap, - styleableValueToNameMap, resourceValueMap); - } - } - } catch (Error e) { - // Log this error with the class name we're trying to load and abort. - AdtPlugin.log(e, "loadAndParseRClass failed to find class %1$s", className); //$NON-NLS-1$ - } - } - } catch (ClassNotFoundException e) { - // pass - } - } - - /** - * Parses a R class, and fills maps. - * @param rClass the class to parse - * @param genericValueToNameMap - * @param styleableValueToNameMap - * @param resourceValueMap - * @return True if we managed to parse the R class. - */ - private boolean parseClass(Class<?> rClass, - Map<Integer, Pair<ResourceType, String>> genericValueToNameMap, - Map<IntArrayWrapper, String> styleableValueToNameMap, Map<ResourceType, - Map<String, Integer>> resourceValueMap) { - try { - for (Class<?> inner : rClass.getDeclaredClasses()) { - String resTypeName = inner.getSimpleName(); - ResourceType resType = ResourceType.getEnum(resTypeName); - - if (resType != null) { - Map<String, Integer> fullMap = new HashMap<String, Integer>(); - resourceValueMap.put(resType, fullMap); - - for (Field f : inner.getDeclaredFields()) { - // only process static final fields. - int modifiers = f.getModifiers(); - if (Modifier.isStatic(modifiers)) { - Class<?> type = f.getType(); - if (type.isArray() && type.getComponentType() == int.class) { - // if the object is an int[] we put it in the styleable map - styleableValueToNameMap.put( - new IntArrayWrapper((int[]) f.get(null)), - f.getName()); - } else if (type == int.class) { - Integer value = (Integer) f.get(null); - genericValueToNameMap.put(value, Pair.of(resType, f.getName())); - fullMap.put(f.getName(), value); - } else { - assert false; - } - } - } - } - } - - return true; - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - } - return false; - } - - /** - * Returns the class name of the R class, based on the project's manifest's package. - * - * @return A class name (e.g. "my.app.R") or null if there's no valid package in the manifest. - */ - private String getRClassName(IProject project) { - IFile manifestFile = ProjectHelper.getManifest(project); - if (manifestFile != null && manifestFile.isSynchronized(IResource.DEPTH_ZERO)) { - ManifestData data = AndroidManifestHelper.parseForData(manifestFile); - if (data != null) { - String javaPackage = data.getPackage(); - return javaPackage + ".R"; //$NON-NLS-1$ - } - } - return null; - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/DynamicIdMap.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/DynamicIdMap.java deleted file mode 100644 index 7bab4fd54..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/DynamicIdMap.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2012 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.resources.manager; - -import com.android.resources.ResourceType; -import com.android.util.Pair; -import com.android.utils.SparseArray; - -import java.util.HashMap; -import java.util.Map; - -public class DynamicIdMap { - - private final Map<Pair<ResourceType, String>, Integer> mDynamicIds = new HashMap<Pair<ResourceType, String>, Integer>(); - private final SparseArray<Pair<ResourceType, String>> mRevDynamicIds = new SparseArray<Pair<ResourceType, String>>(); - private int mDynamicSeed; - - public DynamicIdMap(int seed) { - mDynamicSeed = seed; - } - - public void reset(int seed) { - mDynamicIds.clear(); - mRevDynamicIds.clear(); - mDynamicSeed = seed; - } - - /** - * Returns a dynamic integer for the given resource type/name, creating it if it doesn't - * already exist. - * - * @param type the type of the resource - * @param name the name of the resource - * @return an integer. - */ - public Integer getId(ResourceType type, String name) { - return getId(Pair.of(type, name)); - } - - /** - * Returns a dynamic integer for the given resource type/name, creating it if it doesn't - * already exist. - * - * @param resource the type/name of the resource - * @return an integer. - */ - public Integer getId(Pair<ResourceType, String> resource) { - Integer value = mDynamicIds.get(resource); - if (value == null) { - value = Integer.valueOf(++mDynamicSeed); - mDynamicIds.put(resource, value); - mRevDynamicIds.put(value, resource); - } - - return value; - } - - public Pair<ResourceType, String> resolveId(int id) { - return mRevDynamicIds.get(id); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java deleted file mode 100644 index 674a601d0..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright (C) 2007 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.resources.manager; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.ResourceFile; -import com.android.ide.common.resources.ResourceFolder; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IMarkerDelta; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceChangeEvent; -import org.eclipse.core.resources.IResourceChangeListener; -import org.eclipse.core.resources.IResourceDelta; -import org.eclipse.core.resources.IResourceDeltaVisitor; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.jdt.core.IJavaModel; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; - -import java.util.ArrayList; - -/** - * The Global Project Monitor tracks project file changes, and forward them to simple project, - * file, and folder listeners. - * Those listeners can be setup with masks to listen to particular events. - * <p/> - * To track project resource changes, use the monitor in the {@link ResourceManager}. It is more - * efficient and while the global ProjectMonitor can track any file, deleted resource files - * cannot be matched to previous {@link ResourceFile} or {@link ResourceFolder} objects by the - * time the listeners get the event notifications. - * - * @see IProjectListener - * @see IFolderListener - * @see IFileListener - */ -public final class GlobalProjectMonitor { - - private final static GlobalProjectMonitor sThis = new GlobalProjectMonitor(); - - /** - * Classes which implement this interface provide a method that deals - * with file change events. - */ - public interface IFileListener { - /** - * Sent when a file changed. - * - * @param file The file that changed. - * @param markerDeltas The marker deltas for the file. - * @param kind The change kind. This is equivalent to - * {@link IResourceDelta#accept(IResourceDeltaVisitor)} - * @param extension the extension of the file or null if the file does - * not have an extension - * @param flags the {@link IResourceDelta#getFlags()} value with details - * on what changed in the file - * @param isAndroidProject whether the parent project is an Android Project - */ - public void fileChanged(@NonNull IFile file, @NonNull IMarkerDelta[] markerDeltas, - int kind, @Nullable String extension, int flags, boolean isAndroidProject); - } - - /** - * Classes which implements this interface provide methods dealing with project events. - */ - public interface IProjectListener { - /** - * Sent for each opened android project at the time the listener is put in place. - * @param project the opened project. - */ - public void projectOpenedWithWorkspace(IProject project); - - /** - * Sent once after all Android projects have been opened, - * at the time the listener is put in place. - * <p/> - * This is called after {@link #projectOpenedWithWorkspace(IProject)} has - * been called on all known Android projects. - */ - public void allProjectsOpenedWithWorkspace(); - - /** - * Sent when a project is opened. - * @param project the project being opened. - */ - public void projectOpened(IProject project); - - /** - * Sent when a project is closed. - * @param project the project being closed. - */ - public void projectClosed(IProject project); - - /** - * Sent when a project is deleted. - * @param project the project about to be deleted. - */ - public void projectDeleted(IProject project); - - /** - * Sent when a project is renamed. During a project rename - * {@link #projectDeleted(IProject)} and {@link #projectOpened(IProject)} are also called. - * This is called last. - * - * @param project the new {@link IProject} object. - * @param from the path of the project before the rename action. - */ - public void projectRenamed(IProject project, IPath from); - } - - /** - * Classes which implement this interface provide a method that deals - * with folder change events - */ - public interface IFolderListener { - /** - * Sent when a folder changed. - * @param folder The file that was changed - * @param kind The change kind. This is equivalent to {@link IResourceDelta#getKind()} - * @param isAndroidProject whether the parent project is an Android Project - */ - public void folderChanged(IFolder folder, int kind, boolean isAndroidProject); - } - - /** - * Interface for a listener to be notified when resource change event starts and ends. - */ - public interface IResourceEventListener { - public void resourceChangeEventStart(); - public void resourceChangeEventEnd(); - } - - /** - * Interface for a listener that gets passed the raw delta without processing. - */ - public interface IRawDeltaListener { - public void visitDelta(IResourceDelta delta); - } - - /** - * Base listener bundle to associate a listener to an event mask. - */ - private static class ListenerBundle { - /** Mask value to accept all events */ - public final static int MASK_NONE = -1; - - /** - * Event mask. Values accepted are IResourceDelta.### - * @see IResourceDelta#ADDED - * @see IResourceDelta#REMOVED - * @see IResourceDelta#CHANGED - * @see IResourceDelta#ADDED_PHANTOM - * @see IResourceDelta#REMOVED_PHANTOM - * */ - int kindMask; - } - - /** - * Listener bundle for file event. - */ - private static class FileListenerBundle extends ListenerBundle { - - /** The file listener */ - IFileListener listener; - } - - /** - * Listener bundle for folder event. - */ - private static class FolderListenerBundle extends ListenerBundle { - /** The file listener */ - IFolderListener listener; - } - - private final ArrayList<FileListenerBundle> mFileListeners = - new ArrayList<FileListenerBundle>(); - - private final ArrayList<FolderListenerBundle> mFolderListeners = - new ArrayList<FolderListenerBundle>(); - - private final ArrayList<IProjectListener> mProjectListeners = new ArrayList<IProjectListener>(); - - private final ArrayList<IResourceEventListener> mEventListeners = - new ArrayList<IResourceEventListener>(); - - private final ArrayList<IRawDeltaListener> mRawDeltaListeners = - new ArrayList<IRawDeltaListener>(); - - private IWorkspace mWorkspace; - - private boolean mIsAndroidProject; - - /** - * Delta visitor for resource changes. - */ - private final class DeltaVisitor implements IResourceDeltaVisitor { - - @Override - public boolean visit(IResourceDelta delta) { - // Find the other resource listeners to notify - IResource r = delta.getResource(); - int type = r.getType(); - if (type == IResource.FILE) { - int kind = delta.getKind(); - // notify the listeners. - for (FileListenerBundle bundle : mFileListeners) { - if (bundle.kindMask == ListenerBundle.MASK_NONE - || (bundle.kindMask & kind) != 0) { - try { - bundle.listener.fileChanged((IFile)r, delta.getMarkerDeltas(), kind, - r.getFileExtension(), delta.getFlags(), mIsAndroidProject); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IFileListener.fileChanged"); - } - } - } - return false; - } else if (type == IResource.FOLDER) { - int kind = delta.getKind(); - // notify the listeners. - for (FolderListenerBundle bundle : mFolderListeners) { - if (bundle.kindMask == ListenerBundle.MASK_NONE - || (bundle.kindMask & kind) != 0) { - try { - bundle.listener.folderChanged((IFolder)r, kind, mIsAndroidProject); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IFileListener.folderChanged"); - } - } - } - return true; - } else if (type == IResource.PROJECT) { - IProject project = (IProject)r; - - try { - mIsAndroidProject = project.hasNature(AdtConstants.NATURE_DEFAULT); - } catch (CoreException e) { - // this can only happen if the project does not exist or is not open, neither - // of which can happen here since we are processing changes in the project - // or at worst a project post-open event. - return false; - } - - if (mIsAndroidProject == false) { - // for non android project, skip the project listeners but return true - // to visit the children and notify the IFileListeners - return true; - } - - int flags = delta.getFlags(); - - if ((flags & IResourceDelta.OPEN) != 0) { - // the project is opening or closing. - - if (project.isOpen()) { - // notify the listeners. - for (IProjectListener pl : mProjectListeners) { - try { - pl.projectOpened(project); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IProjectListener.projectOpened"); - } - } - } else { - // notify the listeners. - for (IProjectListener pl : mProjectListeners) { - try { - pl.projectClosed(project); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IProjectListener.projectClosed"); - } - } - } - - if ((flags & IResourceDelta.MOVED_FROM) != 0) { - IPath from = delta.getMovedFromPath(); - // notify the listeners. - for (IProjectListener pl : mProjectListeners) { - try { - pl.projectRenamed(project, from); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IProjectListener.projectRenamed"); - } - } - } - } - } - - return true; - } - } - - public static GlobalProjectMonitor getMonitor() { - return sThis; - } - - - /** - * Starts the resource monitoring. - * @param ws The current workspace. - * @return The monitor object. - */ - public static GlobalProjectMonitor startMonitoring(IWorkspace ws) { - if (sThis != null) { - ws.addResourceChangeListener(sThis.mResourceChangeListener, - IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE); - sThis.mWorkspace = ws; - } - return sThis; - } - - /** - * Stops the resource monitoring. - * @param ws The current workspace. - */ - public static void stopMonitoring(IWorkspace ws) { - if (sThis != null) { - ws.removeResourceChangeListener(sThis.mResourceChangeListener); - - synchronized (sThis) { - sThis.mFileListeners.clear(); - sThis.mProjectListeners.clear(); - } - } - } - - /** - * Adds a file listener. - * @param listener The listener to receive the events. - * @param kindMask The event mask to filter out specific events. - * {@link ListenerBundle#MASK_NONE} will forward all events. - * See {@link ListenerBundle#kindMask} for more values. - */ - public synchronized void addFileListener(IFileListener listener, int kindMask) { - FileListenerBundle bundle = new FileListenerBundle(); - bundle.listener = listener; - bundle.kindMask = kindMask; - - mFileListeners.add(bundle); - } - - /** - * Removes an existing file listener. - * @param listener the listener to remove. - */ - public synchronized void removeFileListener(IFileListener listener) { - for (int i = 0 ; i < mFileListeners.size() ; i++) { - FileListenerBundle bundle = mFileListeners.get(i); - if (bundle.listener == listener) { - mFileListeners.remove(i); - return; - } - } - } - - /** - * Adds a folder listener. - * @param listener The listener to receive the events. - * @param kindMask The event mask to filter out specific events. - * {@link ListenerBundle#MASK_NONE} will forward all events. - * See {@link ListenerBundle#kindMask} for more values. - */ - public synchronized void addFolderListener(IFolderListener listener, int kindMask) { - FolderListenerBundle bundle = new FolderListenerBundle(); - bundle.listener = listener; - bundle.kindMask = kindMask; - - mFolderListeners.add(bundle); - } - - /** - * Removes an existing folder listener. - * @param listener the listener to remove. - */ - public synchronized void removeFolderListener(IFolderListener listener) { - for (int i = 0 ; i < mFolderListeners.size() ; i++) { - FolderListenerBundle bundle = mFolderListeners.get(i); - if (bundle.listener == listener) { - mFolderListeners.remove(i); - return; - } - } - } - - /** - * Adds a project listener. - * @param listener The listener to receive the events. - */ - public synchronized void addProjectListener(IProjectListener listener) { - mProjectListeners.add(listener); - - // we need to look at the opened projects and give them to the listener. - - // get the list of opened android projects. - IWorkspaceRoot workspaceRoot = mWorkspace.getRoot(); - IJavaModel javaModel = JavaCore.create(workspaceRoot); - IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(javaModel, - null /*filter*/); - - - notifyResourceEventStart(); - - for (IJavaProject androidProject : androidProjects) { - listener.projectOpenedWithWorkspace(androidProject.getProject()); - } - - listener.allProjectsOpenedWithWorkspace(); - - notifyResourceEventEnd(); - } - - /** - * Removes an existing project listener. - * @param listener the listener to remove. - */ - public synchronized void removeProjectListener(IProjectListener listener) { - mProjectListeners.remove(listener); - } - - /** - * Adds a resource event listener. - * @param listener The listener to receive the events. - */ - public synchronized void addResourceEventListener(IResourceEventListener listener) { - mEventListeners.add(listener); - } - - /** - * Removes an existing Resource Event listener. - * @param listener the listener to remove. - */ - public synchronized void removeResourceEventListener(IResourceEventListener listener) { - mEventListeners.remove(listener); - } - - /** - * Adds a raw delta listener. - * @param listener The listener to receive the deltas. - */ - public synchronized void addRawDeltaListener(IRawDeltaListener listener) { - mRawDeltaListeners.add(listener); - } - - /** - * Removes an existing Raw Delta listener. - * @param listener the listener to remove. - */ - public synchronized void removeRawDeltaListener(IRawDeltaListener listener) { - mRawDeltaListeners.remove(listener); - } - - private void notifyResourceEventStart() { - for (IResourceEventListener listener : mEventListeners) { - try { - listener.resourceChangeEventStart(); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IResourceEventListener.resourceChangeEventStart"); - } - } - } - - private void notifyResourceEventEnd() { - for (IResourceEventListener listener : mEventListeners) { - try { - listener.resourceChangeEventEnd(); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IResourceEventListener.resourceChangeEventEnd"); - } - } - } - - private IResourceChangeListener mResourceChangeListener = new IResourceChangeListener() { - /** - * Processes the workspace resource change events. - * - * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent) - */ - @Override - public synchronized void resourceChanged(IResourceChangeEvent event) { - // notify the event listeners of a start. - notifyResourceEventStart(); - - if (event.getType() == IResourceChangeEvent.PRE_DELETE) { - // a project is being deleted. Lets get the project object and remove - // its compiled resource list. - IResource r = event.getResource(); - IProject project = r.getProject(); - - // notify the listeners. - for (IProjectListener pl : mProjectListeners) { - try { - if (project.hasNature(AdtConstants.NATURE_DEFAULT)) { - try { - pl.projectDeleted(project); - } catch (Throwable t) { - AdtPlugin.log(t,"Failed to call IProjectListener.projectDeleted"); - } - } - } catch (CoreException e) { - // just ignore this project. - } - } - } else { - // this a regular resource change. We get the delta and go through it with a visitor. - IResourceDelta delta = event.getDelta(); - - // notify the raw delta listeners - for (IRawDeltaListener listener : mRawDeltaListeners) { - listener.visitDelta(delta); - } - - DeltaVisitor visitor = new DeltaVisitor(); - try { - delta.accept(visitor); - } catch (CoreException e) { - } - } - - // we're done, notify the event listeners. - notifyResourceEventEnd(); - } - }; -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java deleted file mode 100644 index d61324937..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2011 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.resources.manager; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.ide.eclipse.adt.AdtConstants.MARKER_AAPT_COMPILE; -import static org.eclipse.core.resources.IResource.DEPTH_ONE; -import static org.eclipse.core.resources.IResource.DEPTH_ZERO; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.ResourceRepository; -import com.android.ide.common.resources.ScanningContext; -import com.android.ide.common.resources.platform.AttributeInfo; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.build.AaptParser; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * An {@link IdeScanningContext} is a specialized {@link ScanningContext} which - * carries extra information about the scanning state, such as which file is - * currently being scanned, and which files have been scanned in the past, such - * that at the end of a scan we can mark and clear errors, etc. - */ -public class IdeScanningContext extends ScanningContext { - private final IProject mProject; - private final List<IResource> mScannedResources = new ArrayList<IResource>(); - private IResource mCurrentFile; - private List<Pair<IResource, String>> mErrors; - private Set<IProject> mFullAaptProjects; - private boolean mValidate; - private Map<String, AttributeInfo> mAttributeMap; - private ResourceRepository mFrameworkResources; - - /** - * Constructs a new {@link IdeScanningContext} - * - * @param repository the associated {@link ResourceRepository} - * @param project the associated project - * @param validate if true, check that the attributes and resources are - * valid and if not request a full AAPT check - */ - public IdeScanningContext(@NonNull ResourceRepository repository, @NonNull IProject project, - boolean validate) { - super(repository); - mProject = project; - mValidate = validate; - - Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { - AndroidTargetData targetData = sdk.getTargetData(project); - if (targetData != null) { - mAttributeMap = targetData.getAttributeMap(); - mFrameworkResources = targetData.getFrameworkResources(); - } - } - } - - @Override - public void addError(@NonNull String error) { - super.addError(error); - - if (mErrors == null) { - mErrors = new ArrayList<Pair<IResource,String>>(); - } - mErrors.add(Pair.of(mCurrentFile, error)); - } - - /** - * Notifies the context that the given resource is about to be scanned. - * - * @param resource the resource about to be scanned - */ - public void startScanning(@NonNull IResource resource) { - assert mCurrentFile == null : mCurrentFile; - mCurrentFile = resource; - mScannedResources.add(resource); - } - - /** - * Notifies the context that the given resource has been scanned. - * - * @param resource the resource that was scanned - */ - public void finishScanning(@NonNull IResource resource) { - assert mCurrentFile != null; - mCurrentFile = null; - } - - /** - * Process any errors found to add error markers in the affected files (and - * also clear up any aapt errors in files that are no longer applicable) - * - * @param async if true, delay updating markers until the next display - * thread event loop update - */ - public void updateMarkers(boolean async) { - // Run asynchronously? This is necessary for example when adding markers - // as the result of a resource change notification, since at that point the - // resource tree is locked for modifications and attempting to create a - // marker will throw a org.eclipse.core.internal.resources.ResourceException. - if (async) { - AdtPlugin.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - updateMarkers(false); - } - }); - return; - } - - // First clear out old/previous markers - for (IResource resource : mScannedResources) { - try { - if (resource.exists()) { - int depth = resource instanceof IFolder ? DEPTH_ONE : DEPTH_ZERO; - resource.deleteMarkers(MARKER_AAPT_COMPILE, true, depth); - } - } catch (CoreException ce) { - // Pass - } - } - - // Add new errors - if (mErrors != null && mErrors.size() > 0) { - List<String> errors = new ArrayList<String>(); - for (Pair<IResource, String> pair : mErrors) { - errors.add(pair.getSecond()); - } - AaptParser.parseOutput(errors, mProject); - } - } - - @Override - public boolean needsFullAapt() { - // returns true if it was explicitly requested or if a file that has errors was modified. - // This handles the case where an edit doesn't add any new id but fix a compile error. - return super.needsFullAapt() || hasModifiedFilesWithErrors(); - } - - /** - * Returns true if any of the scanned resources has an error marker on it. - */ - private boolean hasModifiedFilesWithErrors() { - for (IResource resource : mScannedResources) { - try { - int depth = resource instanceof IFolder ? DEPTH_ONE : DEPTH_ZERO; - if (resource.exists()) { - IMarker[] markers = resource.findMarkers(IMarker.PROBLEM, - true /*includeSubtypes*/, depth); - for (IMarker marker : markers) { - if (marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) == - IMarker.SEVERITY_ERROR) { - return true; - } - } - } - } catch (CoreException ce) { - // Pass - } - } - - return false; - } - - @Override - protected void requestFullAapt() { - super.requestFullAapt(); - - if (mCurrentFile != null) { - if (mFullAaptProjects == null) { - mFullAaptProjects = new HashSet<IProject>(); - } - mFullAaptProjects.add(mCurrentFile.getProject()); - } else { - assert false : "No current context to apply IdeScanningContext to"; - } - } - - /** - * Returns the collection of projects that scanned resources have requested - * a full aapt for. - * - * @return a collection of projects that scanned resources requested full - * aapt runs for, or null - */ - public Collection<IProject> getAaptRequestedProjects() { - return mFullAaptProjects; - } - - @Override - public boolean checkValue(@Nullable String uri, @NonNull String name, @NonNull String value) { - if (!mValidate) { - return true; - } - - if (!needsFullAapt() && mAttributeMap != null && ANDROID_URI.equals(uri)) { - AttributeInfo info = mAttributeMap.get(name); - if (info != null && !info.isValid(value, mRepository, mFrameworkResources)) { - return false; - } - } - - return super.checkValue(uri, name, value); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java deleted file mode 100644 index e07f09927..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java +++ /dev/null @@ -1,376 +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.resources.manager; - -import com.android.SdkConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.build.BuildHelper; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; - -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.jdt.core.IClasspathContainer; -import org.eclipse.jdt.core.IClasspathEntry; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; - -/** - * ClassLoader able to load class from output of an Eclipse project. - */ -public final class ProjectClassLoader extends ClassLoader { - - private final IJavaProject mJavaProject; - private URLClassLoader mJarClassLoader; - private boolean mInsideJarClassLoader = false; - - public ProjectClassLoader(ClassLoader parentClassLoader, IProject project) { - super(parentClassLoader); - mJavaProject = JavaCore.create(project); - } - - @Override - protected Class<?> findClass(String name) throws ClassNotFoundException { - // if we are here through a child classloader, throw an exception. - if (mInsideJarClassLoader) { - throw new ClassNotFoundException(name); - } - - // attempt to load the class from the main project - Class<?> clazz = loadFromProject(mJavaProject, name); - - if (clazz != null) { - return clazz; - } - - // attempt to load the class from the jar dependencies - clazz = loadClassFromJar(name); - if (clazz != null) { - return clazz; - } - - // attempt to load the class from the libraries - try { - // get the project info - ProjectState projectState = Sdk.getProjectState(mJavaProject.getProject()); - - // this can happen if the project has no project.properties. - if (projectState != null) { - - List<IProject> libProjects = projectState.getFullLibraryProjects(); - List<IJavaProject> referencedJavaProjects = BuildHelper.getJavaProjects( - libProjects); - - for (IJavaProject javaProject : referencedJavaProjects) { - clazz = loadFromProject(javaProject, name); - - if (clazz != null) { - return clazz; - } - } - } - } catch (CoreException e) { - // log exception? - } - - throw new ClassNotFoundException(name); - } - - /** - * Attempts to load a class from a project output folder. - * @param project the project to load the class from - * @param name the name of the class - * @return a class object if found, null otherwise. - */ - private Class<?> loadFromProject(IJavaProject project, String name) { - try { - // get the project output folder. - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - IPath outputLocation = project.getOutputLocation(); - IResource outRes = root.findMember(outputLocation); - if (outRes == null) { - return null; - } - - File outFolder = new File(outRes.getLocation().toOSString()); - - // get the class name segments - String[] segments = name.split("\\."); //$NON-NLS-1$ - - // try to load the class from the bin folder of the project. - File classFile = getFile(outFolder, segments, 0); - if (classFile == null) { - return null; - } - - // load the content of the file and create the class. - FileInputStream fis = new FileInputStream(classFile); - byte[] data = new byte[(int)classFile.length()]; - int read = 0; - try { - read = fis.read(data); - } catch (IOException e) { - data = null; - } - fis.close(); - - if (data != null) { - try { - Class<?> clazz = defineClass(null, data, 0, read); - if (clazz != null) { - return clazz; - } - } catch (UnsupportedClassVersionError e) { - // Attempt to reload on lower version - int maxVersion = 50; // JDK 1.6 - try { - byte[] rewritten = rewriteClass(data, maxVersion, 0); - return defineClass(null, rewritten, 0, rewritten.length); - } catch (UnsupportedClassVersionError e2) { - throw e; // throw *original* exception, not attempt to rewrite - } - } - } - } catch (Exception e) { - // log the exception? - } - - return null; - } - - /** - * Rewrites the given class to the given target class file version. - */ - public static byte[] rewriteClass(byte[] classData, final int maxVersion, final int minVersion) { - assert maxVersion >= minVersion; - ClassWriter classWriter = new ClassWriter(0); - ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5, classWriter) { - @Override - public void visit(int version, int access, String name, String signature, - String superName, String[] interfaces) { - if (version > maxVersion) { - version = maxVersion; - } - if (version < minVersion) { - version = minVersion; - } - super.visit(version, access, name, signature, superName, interfaces); - } - }; - ClassReader reader = new ClassReader(classData); - reader.accept(classVisitor, 0); - return classWriter.toByteArray(); - } - - /** - * Returns the File matching the a certain path from a root {@link File}. - * <p/>The methods checks that the file ends in .class even though the last segment - * does not. - * @param parent the root of the file. - * @param segments the segments containing the path of the file - * @param index the offset at which to start looking into segments. - * @throws FileNotFoundException - */ - private File getFile(File parent, String[] segments, int index) - throws FileNotFoundException { - // reached the end with no match? - if (index == segments.length) { - throw new FileNotFoundException(); - } - - String toMatch = segments[index]; - File[] files = parent.listFiles(); - - // we're at the last segments. we look for a matching <file>.class - if (index == segments.length - 1) { - toMatch = toMatch + ".class"; - - if (files != null) { - for (File file : files) { - if (file.isFile() && file.getName().equals(toMatch)) { - return file; - } - } - } - - // no match? abort. - throw new FileNotFoundException(); - } - - String innerClassName = null; - - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - if (toMatch.equals(file.getName())) { - return getFile(file, segments, index+1); - } - } else if (file.getName().startsWith(toMatch)) { - if (innerClassName == null) { - StringBuilder sb = new StringBuilder(segments[index]); - for (int i = index + 1 ; i < segments.length ; i++) { - sb.append('$'); - sb.append(segments[i]); - } - sb.append(".class"); - - innerClassName = sb.toString(); - } - - if (file.getName().equals(innerClassName)) { - return file; - } - } - } - } - - return null; - } - - /** - * Loads a class from the 3rd party jar present in the project - * - * @return the class loader or null if not found. - */ - private Class<?> loadClassFromJar(String name) { - if (mJarClassLoader == null) { - // get the OS path to all the external jars - URL[] jars = getExternalJars(); - - mJarClassLoader = new URLClassLoader(jars, this /* parent */); - } - - try { - // because a class loader always look in its parent loader first, we need to know - // that we are querying the jar classloader. This will let us know to not query - // it again for classes we don't find, or this would create an infinite loop. - mInsideJarClassLoader = true; - return mJarClassLoader.loadClass(name); - } catch (ClassNotFoundException e) { - // not found? return null. - return null; - } finally { - mInsideJarClassLoader = false; - } - } - - /** - * Returns an array of external jar files used by the project. - * @return an array of OS-specific absolute file paths - */ - private final URL[] getExternalJars() { - // get a java project from it - IJavaProject javaProject = JavaCore.create(mJavaProject.getProject()); - - ArrayList<URL> oslibraryList = new ArrayList<URL>(); - IClasspathEntry[] classpaths = javaProject.readRawClasspath(); - if (classpaths != null) { - for (IClasspathEntry e : classpaths) { - if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY || - e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { - // if this is a classpath variable reference, we resolve it. - if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { - e = JavaCore.getResolvedClasspathEntry(e); - } - - handleClassPathEntry(e, oslibraryList); - } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { - // get the container. - try { - IClasspathContainer container = JavaCore.getClasspathContainer( - e.getPath(), javaProject); - // ignore the system and default_system types as they represent - // libraries that are part of the runtime. - if (container != null && - container.getKind() == IClasspathContainer.K_APPLICATION) { - IClasspathEntry[] entries = container.getClasspathEntries(); - for (IClasspathEntry entry : entries) { - // TODO: Xav -- is this necessary? - if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { - entry = JavaCore.getResolvedClasspathEntry(entry); - } - - handleClassPathEntry(entry, oslibraryList); - } - } - } catch (JavaModelException jme) { - // can't resolve the container? ignore it. - AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", - e.getPath()); - } - } - } - } - - return oslibraryList.toArray(new URL[oslibraryList.size()]); - } - - private void handleClassPathEntry(IClasspathEntry e, ArrayList<URL> oslibraryList) { - // get the IPath - IPath path = e.getPath(); - - // check the name ends with .jar - if (SdkConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { - boolean local = false; - IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path); - if (resource != null && resource.exists() && - resource.getType() == IResource.FILE) { - local = true; - try { - oslibraryList.add(new File(resource.getLocation().toOSString()) - .toURI().toURL()); - } catch (MalformedURLException mue) { - // pass - } - } - - if (local == false) { - // if the jar path doesn't match a workspace resource, - // then we get an OSString and check if this links to a valid file. - String osFullPath = path.toOSString(); - - File f = new File(osFullPath); - if (f.exists()) { - try { - oslibraryList.add(f.toURI().toURL()); - } catch (MalformedURLException mue) { - // pass - } - } - } - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java deleted file mode 100644 index 7c3fd4c13..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2007 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.resources.manager; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.IntArrayWrapper; -import com.android.ide.common.resources.ResourceFolder; -import com.android.ide.common.resources.ResourceItem; -import com.android.ide.common.resources.ResourceRepository; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.ide.eclipse.adt.io.IFolderWrapper; -import com.android.io.IAbstractFolder; -import com.android.resources.ResourceType; -import com.android.util.Pair; - -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; - -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Represents the resources of a project. - * On top of the regular {@link ResourceRepository} features it provides: - *<ul> - *<li>configured resources contain the resources coming from the libraries.</li> - *<li>resolution to and from resource integer (compiled value in R.java).</li> - *<li>handles resource integer for non existing values of type ID. This is used when rendering.</li> - *<li>layouts that have no been saved yet. This is handled by generating dynamic IDs - * on the fly.</li> - *</ul> - */ -@SuppressWarnings("deprecation") -public class ProjectResources extends ResourceRepository { - // project resources are defined as 0x7FXX#### where XX is the resource type (layout, drawable, - // etc...). Using FF as the type allows for 255 resource types before we get a collision - // which should be fine. - private final static int DYNAMIC_ID_SEED_START = 0x7fff0000; - - /** Map of (name, id) for resources of type {@link ResourceType#ID} coming from R.java */ - private Map<ResourceType, Map<String, Integer>> mResourceValueMap; - /** Map of (id, [name, resType]) for all resources coming from R.java */ - private Map<Integer, Pair<ResourceType, String>> mResIdValueToNameMap; - /** Map of (int[], name) for styleable resources coming from R.java */ - private Map<IntArrayWrapper, String> mStyleableValueToNameMap; - - private final DynamicIdMap mDynamicIdMap = new DynamicIdMap(DYNAMIC_ID_SEED_START); - private final IntArrayWrapper mWrapper = new IntArrayWrapper(null); - private final IProject mProject; - - public static ProjectResources create(IProject project) { - IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES); - - return new ProjectResources(project, new IFolderWrapper(resFolder)); - } - - /** - * Makes a ProjectResources for a given <var>project</var>. - * @param project the project. - */ - private ProjectResources(IProject project, IAbstractFolder resFolder) { - super(resFolder, false /*isFrameworkRepository*/); - mProject = project; - } - - /** - * Returns the resources values matching a given {@link FolderConfiguration}, this will - * include library dependency. - * - * @param referenceConfig the configuration that each value must match. - * @return a map with guaranteed to contain an entry for each {@link ResourceType} - */ - @Override - @NonNull - public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources( - @NonNull FolderConfiguration referenceConfig) { - ensureInitialized(); - - Map<ResourceType, Map<String, ResourceValue>> resultMap = - new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class); - - // if the project contains libraries, we need to add the libraries resources here - // so that they are accessible to the layout rendering. - if (mProject != null) { - ProjectState state = Sdk.getProjectState(mProject); - if (state != null) { - List<IProject> libraries = state.getFullLibraryProjects(); - - ResourceManager resMgr = ResourceManager.getInstance(); - - // because aapt put all the library in their order in this array, the first - // one will have priority over the 2nd one. So it's better to loop in the inverse - // order and fill the map with resources that will be overwritten by higher - // priority resources - for (int i = libraries.size() - 1 ; i >= 0 ; i--) { - IProject library = libraries.get(i); - - ProjectResources libRes = resMgr.getProjectResources(library); - if (libRes != null) { - // get the library resources, and only the library, not the dependencies - // so call doGetConfiguredResources() directly. - Map<ResourceType, Map<String, ResourceValue>> libMap = - libRes.doGetConfiguredResources(referenceConfig); - - // we don't want to simply replace the whole map, but instead merge the - // content of any sub-map - for (Entry<ResourceType, Map<String, ResourceValue>> libEntry : - libMap.entrySet()) { - - // get the map currently in the result map for this resource type - Map<String, ResourceValue> tempMap = resultMap.get(libEntry.getKey()); - if (tempMap == null) { - // since there's no current map for this type, just add the map - // directly coming from the library resources - resultMap.put(libEntry.getKey(), libEntry.getValue()); - } else { - // already a map for this type. add the resources from the - // library, this will override existing value, which is why - // we loop in a specific library order. - tempMap.putAll(libEntry.getValue()); - } - } - } - } - } - } - - // now the project resources themselves. - Map<ResourceType, Map<String, ResourceValue>> thisProjectMap = - doGetConfiguredResources(referenceConfig); - - // now merge the maps. - for (Entry<ResourceType, Map<String, ResourceValue>> entry : thisProjectMap.entrySet()) { - ResourceType type = entry.getKey(); - Map<String, ResourceValue> typeMap = resultMap.get(type); - if (typeMap == null) { - resultMap.put(type, entry.getValue()); - } else { - typeMap.putAll(entry.getValue()); - } - } - - return resultMap; - } - - /** - * Returns the {@link ResourceFolder} associated with a {@link IFolder}. - * @param folder The {@link IFolder} object. - * @return the {@link ResourceFolder} or null if it was not found. - * - * @see ResourceRepository#getResourceFolder(com.android.io.IAbstractFolder) - */ - public ResourceFolder getResourceFolder(IFolder folder) { - return getResourceFolder(new IFolderWrapper(folder)); - } - - /** - * Resolves a compiled resource id into the resource name and type - * @param id the resource integer id. - * @return a {@link Pair} of 2 strings { name, type } or null if the id could not be resolved - */ - public Pair<ResourceType, String> resolveResourceId(int id) { - Pair<ResourceType, String> result = null; - if (mResIdValueToNameMap != null) { - result = mResIdValueToNameMap.get(id); - } - - if (result == null) { - synchronized (mDynamicIdMap) { - result = mDynamicIdMap.resolveId(id); - } - } - - return result; - } - - /** - * Resolves a compiled styleable id of type int[] into the styleable name. - */ - public String resolveStyleable(int[] id) { - if (mStyleableValueToNameMap != null) { - mWrapper.set(id); - return mStyleableValueToNameMap.get(mWrapper); - } - - return null; - } - - /** - * Returns the integer id of a resource given its type and name. - * <p/>If the resource is of type {@link ResourceType#ID} and does not exist in the - * internal map, then new id values are dynamically generated (and stored so that queries - * with the same names will return the same value). - */ - public Integer getResourceId(ResourceType type, String name) { - Integer result = null; - if (mResourceValueMap != null) { - Map<String, Integer> map = mResourceValueMap.get(type); - if (map != null) { - result = map.get(name); - } - } - - if (result == null) { - synchronized (mDynamicIdMap) { - result = mDynamicIdMap.getId(type, name); - } - } - - return result; - } - - /** - * Resets the list of dynamic Ids. This list is used by - * {@link #getResourceId(String, String)} when the resource query is an ID that doesn't - * exist (for example for ID automatically generated in layout files that are not saved yet.) - * <p/>This method resets those dynamic ID and must be called whenever the actual list of IDs - * change. - */ - public void resetDynamicIds() { - synchronized (mDynamicIdMap) { - mDynamicIdMap.reset(DYNAMIC_ID_SEED_START); - } - } - - @Override - @NonNull - protected ResourceItem createResourceItem(@NonNull String name) { - return new ResourceItem(name); - } - - /** - * Sets compiled resource information. - * - * @param resIdValueToNameMap a map of compiled resource id to resource name. - * The map is acquired by the {@link ProjectResources} object. - * @param styleableValueMap a map of (int[], name) for the styleable information. The map is - * acquired by the {@link ProjectResources} object. - * @param resourceValueMap a map of (name, id) for resources of type {@link ResourceType#ID}. - * The list is acquired by the {@link ProjectResources} object. - */ - void setCompiledResources(Map<Integer, Pair<ResourceType, String>> resIdValueToNameMap, - Map<IntArrayWrapper, String> styleableValueMap, - Map<ResourceType, Map<String, Integer>> resourceValueMap) { - mResourceValueMap = resourceValueMap; - mResIdValueToNameMap = resIdValueToNameMap; - mStyleableValueToNameMap = styleableValueMap; - - resetDynamicIds(); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java deleted file mode 100644 index e407b6a78..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright (C) 2007 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.resources.manager; - -import com.android.SdkConstants; -import com.android.ide.common.resources.FrameworkResources; -import com.android.ide.common.resources.ResourceFile; -import com.android.ide.common.resources.ResourceFolder; -import com.android.ide.common.resources.ResourceRepository; -import com.android.ide.common.resources.ScanningContext; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IProjectListener; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IRawDeltaListener; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.ide.eclipse.adt.io.IFileWrapper; -import com.android.ide.eclipse.adt.io.IFolderWrapper; -import com.android.io.FolderWrapper; -import com.android.resources.ResourceFolderType; -import com.android.sdklib.IAndroidTarget; - -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IMarkerDelta; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceDelta; -import org.eclipse.core.resources.IResourceDeltaVisitor; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.QualifiedName; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * The ResourceManager tracks resources for all opened projects. - * <p/> - * It provide direct access to all the resources of a project as a {@link ProjectResources} - * object that allows accessing the resources through their file representation or as Android - * resources (similar to what is seen by an Android application). - * <p/> - * The ResourceManager automatically tracks file changes to update its internal representation - * of the resources so that they are always up to date. - * <p/> - * It also gives access to a monitor that is more resource oriented than the - * {@link GlobalProjectMonitor}. - * This monitor will let you track resource changes by giving you direct access to - * {@link ResourceFile}, or {@link ResourceFolder}. - * - * @see ProjectResources - */ -public final class ResourceManager { - public final static boolean DEBUG = false; - - private final static ResourceManager sThis = new ResourceManager(); - - /** - * Map associating project resource with project objects. - * <p/><b>All accesses must be inside a synchronized(mMap) block</b>, and do as a little as - * possible and <b>not call out to other classes</b>. - */ - private final Map<IProject, ProjectResources> mMap = - new HashMap<IProject, ProjectResources>(); - - /** - * Interface to be notified of resource changes. - * - * @see ResourceManager#addListener(IResourceListener) - * @see ResourceManager#removeListener(IResourceListener) - */ - public interface IResourceListener { - /** - * Notification for resource file change. - * @param project the project of the file. - * @param file the {@link ResourceFile} representing the file. - * @param eventType the type of event. See {@link IResourceDelta}. - */ - void fileChanged(IProject project, ResourceFile file, int eventType); - /** - * Notification for resource folder change. - * @param project the project of the file. - * @param folder the {@link ResourceFolder} representing the folder. - * @param eventType the type of event. See {@link IResourceDelta}. - */ - void folderChanged(IProject project, ResourceFolder folder, int eventType); - } - - private final ArrayList<IResourceListener> mListeners = new ArrayList<IResourceListener>(); - - /** - * Sets up the resource manager with the global project monitor. - * @param monitor The global project monitor - */ - public static void setup(GlobalProjectMonitor monitor) { - monitor.addProjectListener(sThis.mProjectListener); - monitor.addRawDeltaListener(sThis.mRawDeltaListener); - - CompiledResourcesMonitor.setupMonitor(monitor); - } - - /** - * Returns the singleton instance. - */ - public static ResourceManager getInstance() { - return sThis; - } - - /** - * Adds a new {@link IResourceListener} to be notified of resource changes. - * @param listener the listener to be added. - */ - public void addListener(IResourceListener listener) { - synchronized (mListeners) { - mListeners.add(listener); - } - } - - /** - * Removes an {@link IResourceListener}, so that it's not notified of resource changes anymore. - * @param listener the listener to be removed. - */ - public void removeListener(IResourceListener listener) { - synchronized (mListeners) { - mListeners.remove(listener); - } - } - - /** - * Returns the resources of a project. - * @param project The project - * @return a ProjectResources object - */ - public ProjectResources getProjectResources(IProject project) { - synchronized (mMap) { - ProjectResources resources = mMap.get(project); - - if (resources == null) { - resources = ProjectResources.create(project); - mMap.put(project, resources); - } - - return resources; - } - } - - /** - * Update the resource repository with a delta - * - * @param delta the resource changed delta to process. - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - */ - public void processDelta(IResourceDelta delta, IdeScanningContext context) { - doProcessDelta(delta, context); - - // when a project is added to the workspace it is possible this is called before the - // repo is actually created so this will return null. - ResourceRepository repo = context.getRepository(); - if (repo != null) { - repo.postUpdateCleanUp(); - } - } - - /** - * Update the resource repository with a delta - * - * @param delta the resource changed delta to process. - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - */ - private void doProcessDelta(IResourceDelta delta, IdeScanningContext context) { - // Skip over deltas that don't fit our mask - int mask = IResourceDelta.ADDED | IResourceDelta.REMOVED | IResourceDelta.CHANGED; - int kind = delta.getKind(); - if ( (mask & kind) == 0) { - return; - } - - // Process this delta first as we need to make sure new folders are created before - // we process their content - IResource r = delta.getResource(); - int type = r.getType(); - - if (type == IResource.FILE) { - context.startScanning(r); - updateFile((IFile)r, delta.getMarkerDeltas(), kind, context); - context.finishScanning(r); - } else if (type == IResource.FOLDER) { - updateFolder((IFolder)r, kind, context); - } // We only care about files and folders. - // Project deltas are handled by our project listener - - // Now, process children recursively - IResourceDelta[] children = delta.getAffectedChildren(); - for (IResourceDelta child : children) { - processDelta(child, context); - } - } - - /** - * Update a resource folder that we know about - * @param folder the folder that was updated - * @param kind the delta type (added/removed/updated) - */ - private void updateFolder(IFolder folder, int kind, IdeScanningContext context) { - ProjectResources resources; - - final IProject project = folder.getProject(); - - try { - if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) { - return; - } - } catch (CoreException e) { - // can't get the project nature? return! - return; - } - - switch (kind) { - case IResourceDelta.ADDED: - // checks if the folder is under res. - IPath path = folder.getFullPath(); - - // the path will be project/res/<something> - if (path.segmentCount() == 3) { - if (isInResFolder(path)) { - // get the project and its resource object. - synchronized (mMap) { - resources = mMap.get(project); - - // if it doesn't exist, we create it. - if (resources == null) { - resources = ProjectResources.create(project); - mMap.put(project, resources); - } - } - - ResourceFolder newFolder = resources.processFolder( - new IFolderWrapper(folder)); - if (newFolder != null) { - notifyListenerOnFolderChange(project, newFolder, kind); - } - } - } - break; - case IResourceDelta.CHANGED: - // only call the listeners. - synchronized (mMap) { - resources = mMap.get(folder.getProject()); - } - if (resources != null) { - ResourceFolder resFolder = resources.getResourceFolder(folder); - if (resFolder != null) { - notifyListenerOnFolderChange(project, resFolder, kind); - } - } - break; - case IResourceDelta.REMOVED: - synchronized (mMap) { - resources = mMap.get(folder.getProject()); - } - if (resources != null) { - // lets get the folder type - ResourceFolderType type = ResourceFolderType.getFolderType( - folder.getName()); - - context.startScanning(folder); - ResourceFolder removedFolder = resources.removeFolder(type, - new IFolderWrapper(folder), context); - context.finishScanning(folder); - if (removedFolder != null) { - notifyListenerOnFolderChange(project, removedFolder, kind); - } - } - break; - } - } - - /** - * Called when a delta indicates that a file has changed. Depending on the - * file being changed, and the type of change (ADDED, REMOVED, CHANGED), the - * file change is processed to update the resource manager data. - * - * @param file The file that changed. - * @param markerDeltas The marker deltas for the file. - * @param kind The change kind. This is equivalent to - * {@link IResourceDelta#accept(IResourceDeltaVisitor)} - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - */ - private void updateFile(IFile file, IMarkerDelta[] markerDeltas, int kind, - ScanningContext context) { - final IProject project = file.getProject(); - - try { - if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) { - return; - } - } catch (CoreException e) { - // can't get the project nature? return! - return; - } - - // get the project resources - ProjectResources resources; - synchronized (mMap) { - resources = mMap.get(project); - } - - if (resources == null) { - return; - } - - // checks if the file is under res/something or bin/res/something - IPath path = file.getFullPath(); - - if (path.segmentCount() == 4 || path.segmentCount() == 5) { - if (isInResFolder(path)) { - IContainer container = file.getParent(); - if (container instanceof IFolder) { - - ResourceFolder folder = resources.getResourceFolder( - (IFolder)container); - - // folder can be null as when the whole folder is deleted, the - // REMOVED event for the folder comes first. In this case, the - // folder will have taken care of things. - if (folder != null) { - ResourceFile resFile = folder.processFile( - new IFileWrapper(file), - ResourceHelper.getResourceDeltaKind(kind), context); - notifyListenerOnFileChange(project, resFile, kind); - } - } - } - } - } - - /** - * Implementation of the {@link IProjectListener} as an internal class so that the methods - * do not appear in the public API of {@link ResourceManager}. - */ - private final IProjectListener mProjectListener = new IProjectListener() { - @Override - public void projectClosed(IProject project) { - synchronized (mMap) { - mMap.remove(project); - } - } - - @Override - public void projectDeleted(IProject project) { - synchronized (mMap) { - mMap.remove(project); - } - } - - @Override - public void projectOpened(IProject project) { - createProject(project); - } - - @Override - public void projectOpenedWithWorkspace(IProject project) { - createProject(project); - } - - @Override - public void allProjectsOpenedWithWorkspace() { - // nothing to do. - } - - @Override - public void projectRenamed(IProject project, IPath from) { - // renamed project get a delete/open event too, so this can be ignored. - } - }; - - /** - * Implementation of {@link IRawDeltaListener} as an internal class so that the methods - * do not appear in the public API of {@link ResourceManager}. Delta processing can be - * accessed through the {@link ResourceManager#visitDelta(IResourceDelta delta)} method. - */ - private final IRawDeltaListener mRawDeltaListener = new IRawDeltaListener() { - @Override - public void visitDelta(IResourceDelta workspaceDelta) { - // If we're auto-building, then PreCompilerBuilder will pass us deltas and - // they will be processed as part of the build. - if (isAutoBuilding()) { - return; - } - - // When *not* auto building, we need to process the deltas immediately on save, - // even if the user is not building yet, such that for example resource ids - // are updated in the resource repositories so rendering etc. can work for - // those new ids. - - IResourceDelta[] projectDeltas = workspaceDelta.getAffectedChildren(); - for (IResourceDelta delta : projectDeltas) { - if (delta.getResource() instanceof IProject) { - IProject project = (IProject) delta.getResource(); - - try { - if (project.hasNature(AdtConstants.NATURE_DEFAULT) == false) { - continue; - } - } catch (CoreException e) { - // only happens if the project is closed or doesn't exist. - } - - IdeScanningContext context = - new IdeScanningContext(getProjectResources(project), project, true); - - processDelta(delta, context); - - Collection<IProject> projects = context.getAaptRequestedProjects(); - if (projects != null) { - for (IProject p : projects) { - markAaptRequested(p); - } - } - } else { - AdtPlugin.log(IStatus.WARNING, "Unexpected delta type: %1$s", - delta.getResource().toString()); - } - } - } - }; - - /** - * Returns the {@link ResourceFolder} for the given file or <code>null</code> if none exists. - */ - public ResourceFolder getResourceFolder(IFile file) { - IContainer container = file.getParent(); - if (container.getType() == IResource.FOLDER) { - IFolder parent = (IFolder)container; - IProject project = file.getProject(); - - ProjectResources resources = getProjectResources(project); - if (resources != null) { - return resources.getResourceFolder(parent); - } - } - - return null; - } - - /** - * Returns the {@link ResourceFolder} for the given folder or <code>null</code> if none exists. - */ - public ResourceFolder getResourceFolder(IFolder folder) { - IProject project = folder.getProject(); - - ProjectResources resources = getProjectResources(project); - if (resources != null) { - return resources.getResourceFolder(folder); - } - - return null; - } - - /** - * Loads and returns the resources for a given {@link IAndroidTarget} - * @param androidTarget the target from which to load the framework resources - */ - public ResourceRepository loadFrameworkResources(IAndroidTarget androidTarget) { - String osResourcesPath = androidTarget.getPath(IAndroidTarget.RESOURCES); - - FolderWrapper frameworkRes = new FolderWrapper(osResourcesPath); - if (frameworkRes.exists()) { - FrameworkResources resources = new FrameworkResources(frameworkRes); - - resources.loadResources(); - resources.loadPublicResources(AdtPlugin.getDefault()); - return resources; - } - - return null; - } - - /** - * Initial project parsing to gather resource info. - * @param project - */ - private void createProject(IProject project) { - if (project.isOpen()) { - synchronized (mMap) { - ProjectResources projectResources = mMap.get(project); - if (projectResources == null) { - projectResources = ProjectResources.create(project); - mMap.put(project, projectResources); - } - } - } - } - - - /** - * Returns true if the path is under /project/res/ - * @param path a workspace relative path - * @return true if the path is under /project res/ - */ - private boolean isInResFolder(IPath path) { - return SdkConstants.FD_RESOURCES.equalsIgnoreCase(path.segment(1)); - } - - private void notifyListenerOnFolderChange(IProject project, ResourceFolder folder, - int eventType) { - synchronized (mListeners) { - for (IResourceListener listener : mListeners) { - try { - listener.folderChanged(project, folder, eventType); - } catch (Throwable t) { - AdtPlugin.log(t, - "Failed to execute ResourceManager.IResouceListener.folderChanged()"); //$NON-NLS-1$ - } - } - } - } - - private void notifyListenerOnFileChange(IProject project, ResourceFile file, int eventType) { - synchronized (mListeners) { - for (IResourceListener listener : mListeners) { - try { - listener.fileChanged(project, file, eventType); - } catch (Throwable t) { - AdtPlugin.log(t, - "Failed to execute ResourceManager.IResouceListener.fileChanged()"); //$NON-NLS-1$ - } - } - } - } - - /** - * Private constructor to enforce singleton design. - */ - private ResourceManager() { - } - - // debug only - @SuppressWarnings("unused") - private String getKindString(int kind) { - if (DEBUG) { - switch (kind) { - case IResourceDelta.ADDED: return "ADDED"; - case IResourceDelta.REMOVED: return "REMOVED"; - case IResourceDelta.CHANGED: return "CHANGED"; - } - } - - return Integer.toString(kind); - } - - /** - * Returns true if the Project > Build Automatically option is turned on - * (default). - * - * @return true if the Project > Build Automatically option is turned on - * (default). - */ - public static boolean isAutoBuilding() { - return ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding(); - } - - /** Qualified name for the per-project persistent property "needs aapt" */ - private final static QualifiedName NEED_AAPT = new QualifiedName(AdtPlugin.PLUGIN_ID, - "aapt");//$NON-NLS-1$ - - /** - * Mark the given project, and any projects which depend on it as a library - * project, as needing a full aapt build the next time the project is built. - * - * @param project the project to mark as needing aapt - */ - public static void markAaptRequested(IProject project) { - try { - String needsAapt = Boolean.TRUE.toString(); - project.setPersistentProperty(NEED_AAPT, needsAapt); - - ProjectState state = Sdk.getProjectState(project); - if (state.isLibrary()) { - // For library projects also mark the dependent projects as needing full aapt - for (ProjectState parent : state.getFullParentProjects()) { - IProject parentProject = parent.getProject(); - // Mark the project, but only if it's open. Resource#setPersistentProperty - // only works on open projects. - if (parentProject.isOpen()) { - parentProject.setPersistentProperty(NEED_AAPT, needsAapt); - } - } - } - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - } - - /** - * Clear the "needs aapt" flag set by {@link #markAaptRequested(IProject)}. - * This is usually called when a project is built. Note that this will only - * clean the build flag on the given project, not on any downstream projects - * that depend on this project as a library project. - * - * @param project the project to clear from the needs aapt list - */ - public static void clearAaptRequest(IProject project) { - try { - project.setPersistentProperty(NEED_AAPT, null); - // Note that even if this project is a library project, we -don't- clear - // the aapt flags on the dependent projects since they may still depend - // on other dirty projects. When they are built, they will issue their - // own clear flag requests. - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - } - - /** - * Returns whether the given project needs a full aapt build. - * - * @param project the project to check - * @return true if the project needs a full aapt run - */ - public static boolean isAaptRequested(IProject project) { - try { - String b = project.getPersistentProperty(NEED_AAPT); - return b != null && Boolean.valueOf(b); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - - return false; - } -} |