FileDocCategorySizeDatePackage
XmlStringFileHelper.javaAPI DocAndroid 1.5 API4661Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.adt.refactorings.extractstring

XmlStringFileHelper.java

/*
 * 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.refactorings.extractstring;

import com.android.ide.eclipse.common.project.AndroidXPathFactory;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import java.util.HashMap;
import java.util.HashSet;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

/**
 * 
 */
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. */
    private HashMap<String,HashSet<String>> mResIdCache;
    /** An instance of XPath, created lazily on demand. */
    private XPath mXPath;

    public XmlStringFileHelper() {
    }
    
    /**
     * Utility method used by the wizard to check whether the given string ID is already
     * defined in the XML file which path is given.
     * 
     * @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 True if such a string ID is already defined.
     */
    public boolean isResIdDuplicate(IProject project, String xmlFileWsPath, String stringId) {
        // This is going to be called many times on the same file.
        // Build a cache of the existing IDs for a given file.
        if (mResIdCache == null) {
            mResIdCache = new HashMap<String, HashSet<String>>();
        }
        HashSet<String> cache = mResIdCache.get(xmlFileWsPath);
        if (cache == null) {
            cache = getResIdsForFile(project, xmlFileWsPath);
            mResIdCache.put(xmlFileWsPath, cache);
        }
        
        return cache.contains(stringId);
    }

    /**
     * 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 set of all string IDs defined in the file. The returned set is always non
     *   null. It is empty if the file does not exist.
     */
    private HashSet<String> getResIdsForFile(IProject project, String xmlFileWsPath) {
        HashSet<String> ids = new HashSet<String>();
        
        if (mXPath == null) {
            mXPath = AndroidXPathFactory.newXPath();
        }

        // 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) {
            InputSource source;
            try {
                source = new InputSource(((IFile) resource).getContents());

                // We want all the IDs in an XML structure like this:
                // <resources>
                //    <string name="ID">something</string>
                // </resources>
                
                String xpathExpr = "/resources/string/@name";   //$NON-NLS-1$
                
                Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET);
                if (result instanceof NodeList) {
                    NodeList list = (NodeList) result;
                    for (int n = list.getLength() - 1; n >= 0; n--) {
                        String id = list.item(n).getNodeValue();
                        ids.add(id);
                    }
                }
                
            } catch (CoreException e1) {
                // IFile.getContents failed. Ignore.
            } catch (XPathExpressionException e) {
                // mXPath.evaluate failed. Ignore.
            }
        }
        
        return ids;
    }

}