aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java628
1 files changed, 628 insertions, 0 deletions
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
new file mode 100644
index 000000000..b0e3d43d0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java
@@ -0,0 +1,628 @@
+/*
+ * 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;
+ }
+}