1 /* 2 * Copyright (C) 2009 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.refactorings.extractstring; 18 19 import com.android.sdklib.xml.AndroidXPathFactory; 20 21 import org.eclipse.core.resources.IFile; 22 import org.eclipse.core.resources.IProject; 23 import org.eclipse.core.resources.IResource; 24 import org.eclipse.core.runtime.CoreException; 25 import org.w3c.dom.NamedNodeMap; 26 import org.w3c.dom.Node; 27 import org.w3c.dom.NodeList; 28 import org.xml.sax.InputSource; 29 30 import java.util.HashMap; 31 import java.util.Map; 32 import java.util.TreeMap; 33 34 import javax.xml.xpath.XPath; 35 import javax.xml.xpath.XPathConstants; 36 import javax.xml.xpath.XPathExpressionException; 37 38 /** 39 * An helper utility to get IDs out of an Android XML resource file. 40 */ 41 class XmlStringFileHelper { 42 43 /** A temporary cache of R.string IDs defined by a given xml file. The key is the 44 * project path of the file, the data is a set of known string Ids for that file. 45 * 46 * Map type: map [String filename] => map [String id => String value]. 47 */ 48 private HashMap<String, Map<String, String>> mResIdCache = 49 new HashMap<String, Map<String, String>>(); 50 /** An instance of XPath, created lazily on demand. */ 51 private XPath mXPath; 52 XmlStringFileHelper()53 public XmlStringFileHelper() { 54 } 55 56 /** 57 * Utility method used by the wizard to retrieve the actual value definition of a given 58 * string ID. 59 * 60 * @param project The project contain the XML file. 61 * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml". 62 * The given file may or may not exist. 63 * @param stringId The string ID to find. 64 * @return The value string if the ID is defined, null otherwise. 65 */ valueOfStringId(IProject project, String xmlFileWsPath, String stringId)66 public String valueOfStringId(IProject project, String xmlFileWsPath, String stringId) { 67 Map<String, String> cache = getResIdsForFile(project, xmlFileWsPath); 68 return cache.get(stringId); 69 } 70 71 /** 72 * Utility method that retrieves all the *string* IDs defined in the given Android resource 73 * file. The instance maintains an internal cache so a given file is retrieved only once. 74 * Callers should consider the set to be read-only. 75 * 76 * @param project The project contain the XML file. 77 * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml". 78 * The given file may or may not exist. 79 * @return The map of string IDs => values defined in the given file. Cached. Never null. 80 */ getResIdsForFile(IProject project, String xmlFileWsPath)81 public Map<String, String> getResIdsForFile(IProject project, String xmlFileWsPath) { 82 Map<String, String> cache = mResIdCache.get(xmlFileWsPath); 83 if (cache == null) { 84 cache = internalGetResIdsForFile(project, xmlFileWsPath); 85 mResIdCache.put(xmlFileWsPath, cache); 86 } 87 return cache; 88 } 89 90 /** 91 * Extract all the defined string IDs from a given file using XPath. 92 * @param project The project contain the XML file. 93 * @param xmlFileWsPath The project path of the file to parse. It may not exist. 94 * @return The map of all string IDs => values defined in the file. 95 * The returned set is always non null. It is empty if the file does not exist. 96 */ internalGetResIdsForFile(IProject project, String xmlFileWsPath)97 private Map<String, String> internalGetResIdsForFile(IProject project, String xmlFileWsPath) { 98 TreeMap<String, String> ids = new TreeMap<String, String>(); 99 100 if (mXPath == null) { 101 mXPath = AndroidXPathFactory.newXPath(); 102 } 103 104 // Access the project that contains the resource that contains the compilation unit 105 IResource resource = project.getFile(xmlFileWsPath); 106 107 if (resource != null && resource.exists() && resource.getType() == IResource.FILE) { 108 InputSource source; 109 try { 110 source = new InputSource(((IFile) resource).getContents()); 111 112 // We want all the IDs in an XML structure like this: 113 // <resources> 114 // <string name="ID">something</string> 115 // </resources> 116 117 String xpathExpr = "/resources/string"; //$NON-NLS-1$ 118 119 Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET); 120 if (result instanceof NodeList) { 121 NodeList list = (NodeList) result; 122 for (int n = list.getLength() - 1; n >= 0; n--) { 123 Node strNode = list.item(n); 124 NamedNodeMap attrs = strNode.getAttributes(); 125 Node nameAttr = attrs.getNamedItem("name"); //$NON-NLS-1$ 126 if (nameAttr != null) { 127 String id = nameAttr.getNodeValue(); 128 String text = strNode.getTextContent(); 129 ids.put(id, text); 130 } 131 } 132 } 133 134 } catch (CoreException e1) { 135 // IFile.getContents failed. Ignore. 136 } catch (XPathExpressionException e2) { 137 // mXPath.evaluate failed. Ignore. 138 } 139 } 140 141 return ids; 142 } 143 144 } 145