diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/XmlStringFileHelper.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/XmlStringFileHelper.java | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/XmlStringFileHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/XmlStringFileHelper.java new file mode 100644 index 000000000..01e814ef2 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/XmlStringFileHelper.java @@ -0,0 +1,187 @@ +/* + * 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; + } + +} |