Home | History | Annotate | Download | only in resources
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.resources;
     18 
     19 import static com.android.SdkConstants.ANDROID_PREFIX;
     20 import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX;
     21 import static com.android.SdkConstants.ANDROID_URI;
     22 import static com.android.SdkConstants.ATTR_COLOR;
     23 import static com.android.SdkConstants.ATTR_NAME;
     24 import static com.android.SdkConstants.ATTR_TYPE;
     25 import static com.android.SdkConstants.DOT_XML;
     26 import static com.android.SdkConstants.EXT_XML;
     27 import static com.android.SdkConstants.FD_RESOURCES;
     28 import static com.android.SdkConstants.FD_RES_VALUES;
     29 import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
     30 import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
     31 import static com.android.SdkConstants.TAG_ITEM;
     32 import static com.android.SdkConstants.TAG_RESOURCES;
     33 import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
     34 
     35 import com.android.ide.common.rendering.api.ResourceValue;
     36 import com.android.ide.common.resources.ResourceDeltaKind;
     37 import com.android.ide.common.resources.ResourceRepository;
     38 import com.android.ide.common.resources.ResourceResolver;
     39 import com.android.ide.common.resources.configuration.CountryCodeQualifier;
     40 import com.android.ide.common.resources.configuration.DensityQualifier;
     41 import com.android.ide.common.resources.configuration.FolderConfiguration;
     42 import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
     43 import com.android.ide.common.resources.configuration.LanguageQualifier;
     44 import com.android.ide.common.resources.configuration.LayoutDirectionQualifier;
     45 import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
     46 import com.android.ide.common.resources.configuration.NavigationStateQualifier;
     47 import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
     48 import com.android.ide.common.resources.configuration.NightModeQualifier;
     49 import com.android.ide.common.resources.configuration.RegionQualifier;
     50 import com.android.ide.common.resources.configuration.ResourceQualifier;
     51 import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
     52 import com.android.ide.common.resources.configuration.ScreenHeightQualifier;
     53 import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
     54 import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
     55 import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
     56 import com.android.ide.common.resources.configuration.ScreenWidthQualifier;
     57 import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier;
     58 import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
     59 import com.android.ide.common.resources.configuration.TouchScreenQualifier;
     60 import com.android.ide.common.resources.configuration.UiModeQualifier;
     61 import com.android.ide.common.resources.configuration.VersionQualifier;
     62 import com.android.ide.eclipse.adt.AdtPlugin;
     63 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
     64 import com.android.ide.eclipse.adt.internal.editors.Hyperlinks;
     65 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     66 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils;
     67 import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.VisualRefactoring;
     68 import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
     69 import com.android.resources.FolderTypeRelationship;
     70 import com.android.resources.ResourceFolderType;
     71 import com.android.resources.ResourceType;
     72 import com.android.utils.Pair;
     73 
     74 import org.eclipse.core.resources.IFile;
     75 import org.eclipse.core.resources.IProject;
     76 import org.eclipse.core.resources.IResource;
     77 import org.eclipse.core.resources.IResourceDelta;
     78 import org.eclipse.core.runtime.CoreException;
     79 import org.eclipse.core.runtime.IPath;
     80 import org.eclipse.core.runtime.Path;
     81 import org.eclipse.jface.text.IRegion;
     82 import org.eclipse.jface.text.Region;
     83 import org.eclipse.swt.graphics.Image;
     84 import org.eclipse.swt.graphics.RGB;
     85 import org.eclipse.wst.sse.core.StructuredModelManager;
     86 import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
     87 import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
     88 import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
     89 import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
     90 import org.eclipse.wst.xml.core.internal.document.ElementImpl;
     91 import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
     92 import org.w3c.dom.Attr;
     93 import org.w3c.dom.Document;
     94 import org.w3c.dom.Element;
     95 import org.w3c.dom.NamedNodeMap;
     96 import org.w3c.dom.Node;
     97 import org.w3c.dom.NodeList;
     98 import org.w3c.dom.Text;
     99 import org.xml.sax.InputSource;
    100 
    101 import java.io.BufferedInputStream;
    102 import java.io.ByteArrayInputStream;
    103 import java.io.File;
    104 import java.io.FileInputStream;
    105 import java.io.IOException;
    106 import java.io.InputStream;
    107 import java.io.UnsupportedEncodingException;
    108 import java.util.HashMap;
    109 import java.util.List;
    110 import java.util.Map;
    111 import java.util.Set;
    112 
    113 import javax.xml.parsers.DocumentBuilder;
    114 import javax.xml.parsers.DocumentBuilderFactory;
    115 
    116 /**
    117  * Helper class to deal with SWT specifics for the resources.
    118  */
    119 @SuppressWarnings("restriction") // XML model
    120 public class ResourceHelper {
    121 
    122     private final static Map<Class<?>, Image> sIconMap = new HashMap<Class<?>, Image>(
    123             FolderConfiguration.getQualifierCount());
    124 
    125     static {
    126         try {
    127             IconFactory factory = IconFactory.getInstance();
    128             sIconMap.put(CountryCodeQualifier.class,        factory.getIcon("mcc")); //$NON-NLS-1$
    129             sIconMap.put(NetworkCodeQualifier.class,        factory.getIcon("mnc")); //$NON-NLS-1$
    130             sIconMap.put(LanguageQualifier.class,           factory.getIcon("language")); //$NON-NLS-1$
    131             sIconMap.put(RegionQualifier.class,             factory.getIcon("region")); //$NON-NLS-1$
    132             sIconMap.put(LayoutDirectionQualifier.class,    factory.getIcon("bidi")); //$NON-NLS-1$
    133             sIconMap.put(ScreenSizeQualifier.class,         factory.getIcon("size")); //$NON-NLS-1$
    134             sIconMap.put(ScreenRatioQualifier.class,        factory.getIcon("ratio")); //$NON-NLS-1$
    135             sIconMap.put(ScreenOrientationQualifier.class,  factory.getIcon("orientation")); //$NON-NLS-1$
    136             sIconMap.put(UiModeQualifier.class,             factory.getIcon("dockmode")); //$NON-NLS-1$
    137             sIconMap.put(NightModeQualifier.class,          factory.getIcon("nightmode")); //$NON-NLS-1$
    138             sIconMap.put(DensityQualifier.class,            factory.getIcon("dpi")); //$NON-NLS-1$
    139             sIconMap.put(TouchScreenQualifier.class,        factory.getIcon("touch")); //$NON-NLS-1$
    140             sIconMap.put(KeyboardStateQualifier.class,      factory.getIcon("keyboard")); //$NON-NLS-1$
    141             sIconMap.put(TextInputMethodQualifier.class,    factory.getIcon("text_input")); //$NON-NLS-1$
    142             sIconMap.put(NavigationStateQualifier.class,    factory.getIcon("navpad")); //$NON-NLS-1$
    143             sIconMap.put(NavigationMethodQualifier.class,   factory.getIcon("navpad")); //$NON-NLS-1$
    144             sIconMap.put(ScreenDimensionQualifier.class,    factory.getIcon("dimension")); //$NON-NLS-1$
    145             sIconMap.put(VersionQualifier.class,            factory.getIcon("version")); //$NON-NLS-1$
    146             sIconMap.put(ScreenWidthQualifier.class,        factory.getIcon("width")); //$NON-NLS-1$
    147             sIconMap.put(ScreenHeightQualifier.class,       factory.getIcon("height")); //$NON-NLS-1$
    148             sIconMap.put(SmallestScreenWidthQualifier.class,factory.getIcon("swidth")); //$NON-NLS-1$
    149         } catch (Throwable t) {
    150             AdtPlugin.log(t , null);
    151         }
    152     }
    153 
    154     /**
    155      * Returns the icon for the qualifier.
    156      */
    157     public static Image getIcon(Class<? extends ResourceQualifier> theClass) {
    158         return sIconMap.get(theClass);
    159     }
    160 
    161     /**
    162      * Returns a {@link ResourceDeltaKind} from an {@link IResourceDelta} value.
    163      * @param kind a {@link IResourceDelta} integer constant.
    164      * @return a matching {@link ResourceDeltaKind} or null.
    165      *
    166      * @see IResourceDelta#ADDED
    167      * @see IResourceDelta#REMOVED
    168      * @see IResourceDelta#CHANGED
    169      */
    170     public static ResourceDeltaKind getResourceDeltaKind(int kind) {
    171         switch (kind) {
    172             case IResourceDelta.ADDED:
    173                 return ResourceDeltaKind.ADDED;
    174             case IResourceDelta.REMOVED:
    175                 return ResourceDeltaKind.REMOVED;
    176             case IResourceDelta.CHANGED:
    177                 return ResourceDeltaKind.CHANGED;
    178         }
    179 
    180         return null;
    181     }
    182 
    183     /**
    184      * Is this a resource that can be defined in any file within the "values" folder?
    185      * <p>
    186      * Some resource types can be defined <b>both</b> as a separate XML file as well
    187      * as defined within a value XML file. This method will return true for these types
    188      * as well. In other words, a ResourceType can return true for both
    189      * {@link #isValueBasedResourceType} and {@link #isFileBasedResourceType}.
    190      *
    191      * @param type the resource type to check
    192      * @return true if the given resource type can be represented as a value under the
    193      *         values/ folder
    194      */
    195     public static boolean isValueBasedResourceType(ResourceType type) {
    196         List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
    197         for (ResourceFolderType folderType : folderTypes) {
    198             if (folderType == ResourceFolderType.VALUES) {
    199                 return true;
    200             }
    201         }
    202 
    203         return false;
    204     }
    205 
    206     /**
    207      * Is this a resource that is defined in a file named by the resource plus the XML
    208      * extension?
    209      * <p>
    210      * Some resource types can be defined <b>both</b> as a separate XML file as well as
    211      * defined within a value XML file along with other properties. This method will
    212      * return true for these resource types as well. In other words, a ResourceType can
    213      * return true for both {@link #isValueBasedResourceType} and
    214      * {@link #isFileBasedResourceType}.
    215      *
    216      * @param type the resource type to check
    217      * @return true if the given resource type is stored in a file named by the resource
    218      */
    219     public static boolean isFileBasedResourceType(ResourceType type) {
    220         List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
    221         for (ResourceFolderType folderType : folderTypes) {
    222             if (folderType != ResourceFolderType.VALUES) {
    223 
    224                 if (type == ResourceType.ID) {
    225                     // The folder types for ID is not only VALUES but also
    226                     // LAYOUT and MENU. However, unlike resources, they are only defined
    227                     // inline there so for the purposes of isFileBasedResourceType
    228                     // (where the intent is to figure out files that are uniquely identified
    229                     // by a resource's name) this method should return false anyway.
    230                     return false;
    231                 }
    232 
    233                 return true;
    234             }
    235         }
    236 
    237         return false;
    238     }
    239 
    240     /**
    241      * Returns true if this class can create the given resource
    242      *
    243      * @param resource the resource to be created
    244      * @return true if the {@link #createResource} method can create this resource
    245      */
    246     public static boolean canCreateResource(String resource) {
    247         // Cannot create framework resources
    248         if (resource.startsWith(ANDROID_PREFIX)) {
    249             return false;
    250         }
    251 
    252         Pair<ResourceType,String> parsed = ResourceRepository.parseResource(resource);
    253         if (parsed != null) {
    254             ResourceType type = parsed.getFirst();
    255             String name = parsed.getSecond();
    256 
    257             // Make sure the name is valid
    258             ResourceNameValidator validator =
    259                 ResourceNameValidator.create(false, (Set<String>) null /* existing */, type);
    260             if (validator.isValid(name) != null) {
    261                 return false;
    262             }
    263 
    264             return canCreateResourceType(type);
    265         }
    266 
    267         return false;
    268     }
    269 
    270     /**
    271      * Returns true if this class can create resources of the given resource
    272      * type
    273      *
    274      * @param type the type of resource to be created
    275      * @return true if the {@link #createResource} method can create resources
    276      *         of this type (provided the name parameter is also valid)
    277      */
    278     public static boolean canCreateResourceType(ResourceType type) {
    279         // We can create all value types
    280         if (isValueBasedResourceType(type)) {
    281             return true;
    282         }
    283 
    284         // We can create -some- file-based types - those supported by the New XML wizard:
    285         for (ResourceFolderType folderType : FolderTypeRelationship.getRelatedFolders(type)) {
    286             if (NewXmlFileWizard.canCreateXmlFile(folderType)) {
    287                 return true;
    288             }
    289         }
    290 
    291         return false;
    292     }
    293 
    294     /** Creates a file-based resource, like a layout. Used by {@link #createResource} */
    295     private static Pair<IFile,IRegion> createFileResource(IProject project, ResourceType type,
    296             String name) {
    297 
    298         ResourceFolderType folderType = null;
    299         for (ResourceFolderType f : FolderTypeRelationship.getRelatedFolders(type)) {
    300             if (NewXmlFileWizard.canCreateXmlFile(f)) {
    301                 folderType = f;
    302                 break;
    303             }
    304         }
    305         if (folderType == null) {
    306             return null;
    307         }
    308 
    309         // Find "dimens.xml" file in res/values/ (or corresponding name for other
    310         // value types)
    311         IPath projectPath = new Path(FD_RESOURCES + WS_SEP + folderType.getName() + WS_SEP
    312             + name + '.' + EXT_XML);
    313         IFile file = project.getFile(projectPath);
    314         return NewXmlFileWizard.createXmlFile(project, file, folderType);
    315     }
    316 
    317     /**
    318      * Creates a resource of a given type, name and (if applicable) value
    319      *
    320      * @param project the project to contain the resource
    321      * @param type the type of resource
    322      * @param name the name of the resource
    323      * @param value the value of the resource, if it is a value-type resource
    324      * @return a pair of the file containing the resource and a region where the value
    325      *         appears
    326      */
    327     public static Pair<IFile,IRegion> createResource(IProject project, ResourceType type,
    328             String name, String value) {
    329         if (!isValueBasedResourceType(type)) {
    330             return createFileResource(project, type, name);
    331         }
    332 
    333         // Find "dimens.xml" file in res/values/ (or corresponding name for other
    334         // value types)
    335         String typeName = type.getName();
    336         String fileName = typeName + 's';
    337         String projectPath = FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP
    338             + fileName + '.' + EXT_XML;
    339         Object editRequester = project;
    340         IResource member = project.findMember(projectPath);
    341         String tagName = Hyperlinks.getTagName(type);
    342         boolean createEmptyTag = type == ResourceType.ID;
    343         if (member != null) {
    344             if (member instanceof IFile) {
    345                 IFile file = (IFile) member;
    346                 // File exists: Must add item to the XML
    347                 IModelManager manager = StructuredModelManager.getModelManager();
    348                 IStructuredModel model = null;
    349                 try {
    350                     model = manager.getExistingModelForEdit(file);
    351                     if (model == null) {
    352                         model = manager.getModelForEdit(file);
    353                     }
    354                     if (model instanceof IDOMModel) {
    355                         model.beginRecording(editRequester, String.format("Add %1$s",
    356                                 type.getDisplayName()));
    357                         IDOMModel domModel = (IDOMModel) model;
    358                         Document document = domModel.getDocument();
    359                         Element root = document.getDocumentElement();
    360                         IStructuredDocument structuredDocument = model.getStructuredDocument();
    361                         Node lastElement = null;
    362                         NodeList childNodes = root.getChildNodes();
    363                         String indent = null;
    364                         for (int i = childNodes.getLength() - 1; i >= 0; i--) {
    365                             Node node = childNodes.item(i);
    366                             if (node.getNodeType() == Node.ELEMENT_NODE) {
    367                                 lastElement = node;
    368                                 indent = AndroidXmlEditor.getIndent(structuredDocument, node);
    369                                 break;
    370                             }
    371                         }
    372                         if (indent == null || indent.length() == 0) {
    373                             indent = "    "; //$NON-NLS-1$
    374                         }
    375                         Node nextChild = lastElement != null ? lastElement.getNextSibling() : null;
    376                         Text indentNode = document.createTextNode('\n' + indent);
    377                         root.insertBefore(indentNode, nextChild);
    378                         Element element = document.createElement(tagName);
    379                         if (createEmptyTag) {
    380                             if (element instanceof ElementImpl) {
    381                                 ElementImpl elementImpl = (ElementImpl) element;
    382                                 elementImpl.setEmptyTag(true);
    383                             }
    384                         }
    385                         element.setAttribute(ATTR_NAME, name);
    386                         if (!tagName.equals(typeName)) {
    387                             element.setAttribute(ATTR_TYPE, typeName);
    388                         }
    389                         root.insertBefore(element, nextChild);
    390                         IRegion region = null;
    391 
    392                         if (createEmptyTag) {
    393                             IndexedRegion domRegion = VisualRefactoring.getRegion(element);
    394                             int endOffset = domRegion.getEndOffset();
    395                             region = new Region(endOffset, 0);
    396                         } else {
    397                             Node valueNode = document.createTextNode(value);
    398                             element.appendChild(valueNode);
    399 
    400                             IndexedRegion domRegion = VisualRefactoring.getRegion(valueNode);
    401                             int startOffset = domRegion.getStartOffset();
    402                             int length = domRegion.getLength();
    403                             region = new Region(startOffset, length);
    404                         }
    405                         model.save();
    406                         return Pair.of(file, region);
    407                     }
    408                 } catch (Exception e) {
    409                     AdtPlugin.log(e, "Cannot access XML value model");
    410                 } finally {
    411                     if (model != null) {
    412                         model.endRecording(editRequester);
    413                         model.releaseFromEdit();
    414                     }
    415                 }
    416             }
    417 
    418             return null;
    419         } else {
    420             // No such file exists: just create it
    421             String prolog = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
    422             StringBuilder sb = new StringBuilder(prolog);
    423 
    424             String root = TAG_RESOURCES;
    425             sb.append('<').append(root).append('>').append('\n');
    426             sb.append("    "); //$NON-NLS-1$
    427             sb.append('<');
    428             sb.append(tagName);
    429             sb.append(" name=\""); //$NON-NLS-1$
    430             sb.append(name);
    431             sb.append('"');
    432             if (!tagName.equals(typeName)) {
    433                 sb.append(" type=\""); //$NON-NLS-1$
    434                 sb.append(typeName);
    435                 sb.append('"');
    436             }
    437             int start, end;
    438             if (createEmptyTag) {
    439                 sb.append("/>");                             //$NON-NLS-1$
    440                 start = sb.length();
    441                 end = sb.length();
    442             } else {
    443                 sb.append('>');
    444                 start = sb.length();
    445                 sb.append(value);
    446                 end = sb.length();
    447                 sb.append('<').append('/');
    448                 sb.append(tagName);
    449                 sb.append('>');
    450             }
    451             sb.append('\n').append('<').append('/').append(root).append('>').append('\n');
    452             String result = sb.toString();
    453             // TODO: Pretty print string (wait until that CL is integrated)
    454             String error = null;
    455             try {
    456                 byte[] buf = result.getBytes("UTF8");    //$NON-NLS-1$
    457                 InputStream stream = new ByteArrayInputStream(buf);
    458                 IFile file = project.getFile(new Path(projectPath));
    459                 file.create(stream, true /*force*/, null /*progress*/);
    460                 IRegion region = new Region(start, end - start);
    461                 return Pair.of(file, region);
    462             } catch (UnsupportedEncodingException e) {
    463                 error = e.getMessage();
    464             } catch (CoreException e) {
    465                 error = e.getMessage();
    466             }
    467 
    468             error = String.format("Failed to generate %1$s: %2$s", name, error);
    469             AdtPlugin.displayError("New Android XML File", error);
    470         }
    471         return null;
    472     }
    473 
    474     /**
    475      * Returns the theme name to be shown for theme styles, e.g. for "@style/Theme" it
    476      * returns "Theme"
    477      *
    478      * @param style a theme style string
    479      * @return the user visible theme name
    480      */
    481     public static String styleToTheme(String style) {
    482         if (style.startsWith(STYLE_RESOURCE_PREFIX)) {
    483             style = style.substring(STYLE_RESOURCE_PREFIX.length());
    484         } else if (style.startsWith(ANDROID_STYLE_RESOURCE_PREFIX)) {
    485             style = style.substring(ANDROID_STYLE_RESOURCE_PREFIX.length());
    486         } else if (style.startsWith(PREFIX_RESOURCE_REF)) {
    487             // @package:style/foo
    488             int index = style.indexOf('/');
    489             if (index != -1) {
    490                 style = style.substring(index + 1);
    491             }
    492         }
    493         return style;
    494     }
    495 
    496     /**
    497      * Returns true if the given style represents a project theme
    498      *
    499      * @param style a theme style string
    500      * @return true if the style string represents a project theme, as opposed
    501      *         to a framework theme
    502      */
    503     public static boolean isProjectStyle(String style) {
    504         assert style.startsWith(STYLE_RESOURCE_PREFIX)
    505             || style.startsWith(ANDROID_STYLE_RESOURCE_PREFIX) : style;
    506 
    507         return style.startsWith(STYLE_RESOURCE_PREFIX);
    508     }
    509 
    510     /**
    511      * Returns the layout resource name for the given layout file, e.g. for
    512      * /res/layout/foo.xml returns foo.
    513      *
    514      * @param layoutFile the layout file whose name we want to look up
    515      * @return the layout name
    516      */
    517     public static String getLayoutName(IFile layoutFile) {
    518         String layoutName = layoutFile.getName();
    519         int dotIndex = layoutName.indexOf('.');
    520         if (dotIndex != -1) {
    521             layoutName = layoutName.substring(0, dotIndex);
    522         }
    523         return layoutName;
    524     }
    525 
    526     /**
    527      * Tries to resolve the given resource value to an actual RGB color. For state lists
    528      * it will pick the simplest/fallback color.
    529      *
    530      * @param resources the resource resolver to use to follow color references
    531      * @param color the color to resolve
    532      * @return the corresponding {@link RGB} color, or null
    533      */
    534     public static RGB resolveColor(ResourceResolver resources, ResourceValue color) {
    535         color = resources.resolveResValue(color);
    536         if (color == null) {
    537             return null;
    538         }
    539         String value = color.getValue();
    540 
    541         while (value != null) {
    542             if (value.startsWith("#")) { //$NON-NLS-1$
    543                 try {
    544                     int rgba = ImageUtils.getColor(value);
    545                     // Drop alpha channel
    546                     return ImageUtils.intToRgb(rgba);
    547                 } catch (NumberFormatException nfe) {
    548                     // Pass
    549                 }
    550                 return null;
    551             }
    552             if (value.startsWith(PREFIX_RESOURCE_REF)) {
    553                 boolean isFramework = color.isFramework();
    554                 color = resources.findResValue(value, isFramework);
    555                 if (color != null) {
    556                     value = color.getValue();
    557                 } else {
    558                     break;
    559                 }
    560             } else {
    561                 File file = new File(value);
    562                 if (file.exists() && file.getName().endsWith(DOT_XML)) {
    563                     // Parse
    564                     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    565                     BufferedInputStream bis = null;
    566                     try {
    567                         bis = new BufferedInputStream(new FileInputStream(file));
    568                         InputSource is = new InputSource(bis);
    569                         factory.setNamespaceAware(true);
    570                         factory.setValidating(false);
    571                         DocumentBuilder builder = factory.newDocumentBuilder();
    572                         Document document = builder.parse(is);
    573                         NodeList items = document.getElementsByTagName(TAG_ITEM);
    574 
    575                         value = findColorValue(items);
    576                         continue;
    577                     } catch (Exception e) {
    578                         AdtPlugin.log(e, "Failed parsing color file %1$s", file.getName());
    579                     } finally {
    580                         if (bis != null) {
    581                             try {
    582                                 bis.close();
    583                             } catch (IOException e) {
    584                                 // Nothing useful can be done here
    585                             }
    586                         }
    587                     }
    588                 }
    589 
    590                 return null;
    591             }
    592         }
    593 
    594         return null;
    595     }
    596 
    597     /**
    598      * Searches a color XML file for the color definition element that does not
    599      * have an associated state and returns its color
    600      */
    601     private static String findColorValue(NodeList items) {
    602         for (int i = 0, n = items.getLength(); i < n; i++) {
    603             // Find non-state color definition
    604             Node item = items.item(i);
    605             boolean hasState = false;
    606             if (item.getNodeType() == Node.ELEMENT_NODE) {
    607                 Element element = (Element) item;
    608                 if (element.hasAttributeNS(ANDROID_URI, ATTR_COLOR)) {
    609                     NamedNodeMap attributes = element.getAttributes();
    610                     for (int j = 0, m = attributes.getLength(); j < m; j++) {
    611                         Attr attribute = (Attr) attributes.item(j);
    612                         if (attribute.getLocalName().startsWith("state_")) { //$NON-NLS-1$
    613                             hasState = true;
    614                             break;
    615                         }
    616                     }
    617 
    618                     if (!hasState) {
    619                         return element.getAttributeNS(ANDROID_URI, ATTR_COLOR);
    620                     }
    621                 }
    622             }
    623         }
    624 
    625         return null;
    626     }
    627 }
    628