| /* |
| * Copyright (C) 2009 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.refactorings.extractstring; |
| |
| import com.android.SdkConstants; |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| 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.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| /** |
| * An helper utility to get IDs out of an Android XML resource file. |
| */ |
| @SuppressWarnings("restriction") |
| class XmlStringFileHelper { |
| |
| /** A temporary cache of R.string IDs defined by a given xml file. The key is the |
| * project path of the file, the data is a set of known string Ids for that file. |
| * |
| * Map type: map [String filename] => map [String id => String value]. |
| */ |
| private HashMap<String, Map<String, String>> mResIdCache = |
| new HashMap<String, Map<String, String>>(); |
| |
| public XmlStringFileHelper() { |
| } |
| |
| /** |
| * Utility method used by the wizard to retrieve the actual value definition of a given |
| * string ID. |
| * |
| * @param project The project contain the XML file. |
| * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml". |
| * The given file may or may not exist. |
| * @param stringId The string ID to find. |
| * @return The value string if the ID is defined, null otherwise. |
| */ |
| public String valueOfStringId(IProject project, String xmlFileWsPath, String stringId) { |
| Map<String, String> cache = getResIdsForFile(project, xmlFileWsPath); |
| return cache.get(stringId); |
| } |
| |
| /** |
| * Utility method that retrieves all the *string* IDs defined in the given Android resource |
| * file. The instance maintains an internal cache so a given file is retrieved only once. |
| * Callers should consider the set to be read-only. |
| * |
| * @param project The project contain the XML file. |
| * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml". |
| * The given file may or may not exist. |
| * @return The map of string IDs => values defined in the given file. Cached. Never null. |
| */ |
| public Map<String, String> getResIdsForFile(IProject project, String xmlFileWsPath) { |
| Map<String, String> cache = mResIdCache.get(xmlFileWsPath); |
| if (cache == null) { |
| cache = internalGetResIdsForFile(project, xmlFileWsPath); |
| mResIdCache.put(xmlFileWsPath, cache); |
| } |
| return cache; |
| } |
| |
| /** |
| * Extract all the defined string IDs from a given file using XPath. |
| * @param project The project contain the XML file. |
| * @param xmlFileWsPath The project path of the file to parse. It may not exist. |
| * @return The map of all string IDs => values defined in the file. |
| * The returned set is always non null. It is empty if the file does not exist. |
| */ |
| private Map<String, String> internalGetResIdsForFile(IProject project, String xmlFileWsPath) { |
| |
| TreeMap<String, String> ids = new TreeMap<String, String>(); |
| |
| // Access the project that contains the resource that contains the compilation unit |
| IResource resource = project.getFile(xmlFileWsPath); |
| |
| if (resource != null && resource.exists() && resource.getType() == IResource.FILE) { |
| IStructuredModel smodel = null; |
| |
| try { |
| IFile file = (IFile) resource; |
| IModelManager modelMan = StructuredModelManager.getModelManager(); |
| smodel = modelMan.getExistingModelForRead(file); |
| if (smodel == null) { |
| smodel = modelMan.getModelForRead(file); |
| } |
| |
| if (smodel instanceof IDOMModel) { |
| IDOMDocument doc = ((IDOMModel) smodel).getDocument(); |
| |
| // We want all the IDs in an XML structure like this: |
| // <resources> |
| // <string name="ID">something</string> |
| // </resources> |
| |
| Node root = findChild(doc, null, SdkConstants.TAG_RESOURCES); |
| if (root != null) { |
| for (Node strNode = findChild(root, null, |
| SdkConstants.TAG_STRING); |
| strNode != null; |
| strNode = findChild(null, strNode, |
| SdkConstants.TAG_STRING)) { |
| NamedNodeMap attrs = strNode.getAttributes(); |
| Node nameAttr = attrs.getNamedItem(SdkConstants.ATTR_NAME); |
| if (nameAttr != null) { |
| String id = nameAttr.getNodeValue(); |
| |
| // Find the TEXT node right after the element. |
| // Whitespace matters so we don't try to normalize it. |
| String text = ""; //$NON-NLS-1$ |
| for (Node txtNode = strNode.getFirstChild(); |
| txtNode != null && txtNode.getNodeType() == Node.TEXT_NODE; |
| txtNode = txtNode.getNextSibling()) { |
| text += txtNode.getNodeValue(); |
| } |
| |
| ids.put(id, text); |
| } |
| } |
| } |
| } |
| |
| } catch (Throwable e) { |
| AdtPlugin.log(e, "GetResIds failed in %1$s", xmlFileWsPath); //$NON-NLS-1$ |
| } finally { |
| if (smodel != null) { |
| smodel.releaseFromRead(); |
| } |
| } |
| } |
| |
| return ids; |
| } |
| |
| /** |
| * Utility method that finds the next node of the requested element name. |
| * |
| * @param parent The parent node. If not null, will to start searching its children. |
| * Set to null when iterating through children. |
| * @param lastChild The last child returned. Use null when visiting a parent the first time. |
| * @param elementName The element name of the node to find. |
| * @return The next children or sibling nide with the requested element name or null. |
| */ |
| private Node findChild(Node parent, Node lastChild, String elementName) { |
| if (lastChild == null && parent != null) { |
| lastChild = parent.getFirstChild(); |
| } else if (lastChild != null) { |
| lastChild = lastChild.getNextSibling(); |
| } |
| |
| for ( ; lastChild != null ; lastChild = lastChild.getNextSibling()) { |
| if (lastChild.getNodeType() == Node.ELEMENT_NODE && |
| lastChild.getNamespaceURI() == null && // resources don't have any NS URI |
| elementName.equals(lastChild.getLocalName())) { |
| return lastChild; |
| } |
| } |
| |
| return null; |
| } |
| |
| } |