14517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye/*
24517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * Copyright (C) 2010 The Android Open Source Project
34517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye *
44517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * Licensed under the Eclipse Public License, Version 1.0 (the "License");
54517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * you may not use this file except in compliance with the License.
64517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * You may obtain a copy of the License at
74517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye *
84517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye *      http://www.eclipse.org/org/documents/epl-v10.php
94517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye *
104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * Unless required by applicable law or agreed to in writing, software
114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * distributed under the License is distributed on an "AS IS" BASIS,
124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * See the License for the specific language governing permissions and
144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * limitations under the License.
154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye */
164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyepackage com.android.ide.eclipse.adt.internal.editors.layout.gle2;
184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_LAYOUT;
2012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.EXT_XML;
2112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FD_RESOURCES;
2212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FD_RES_LAYOUT;
2312d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.VIEW_INCLUDE;
24c3105b949cd2a0f6cbf8a12ec4f30e49b5b5a502Xavier Ducrohetimport static com.android.ide.eclipse.adt.AdtConstants.WS_LAYOUTS;
25c3105b949cd2a0f6cbf8a12ec4f30e49b5b5a502Xavier Ducrohetimport static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
26da02c18ad5b54d97a1fcfd5f6633062b0c873c22Xavier Ducrohetimport static com.android.resources.ResourceType.LAYOUT;
274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport static org.eclipse.core.resources.IResourceDelta.ADDED;
284517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport static org.eclipse.core.resources.IResourceDelta.CHANGED;
294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport static org.eclipse.core.resources.IResourceDelta.CONTENT;
304517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport static org.eclipse.core.resources.IResourceDelta.REMOVED;
314517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
321f72cb7cb032538b79e79d6fc7ff3905e9766ce1Xavier Ducrohetimport com.android.annotations.VisibleForTesting;
33026ba97e98e0527d910e15c4e1512893a777a8d2Xavier Ducrohetimport com.android.ide.common.resources.ResourceFile;
34026ba97e98e0527d910e15c4e1512893a777a8d2Xavier Ducrohetimport com.android.ide.common.resources.ResourceFolder;
35026ba97e98e0527d910e15c4e1512893a777a8d2Xavier Ducrohetimport com.android.ide.common.resources.ResourceItem;
364517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport com.android.ide.eclipse.adt.AdtPlugin;
374517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
394517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager.IResourceListener;
414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport com.android.ide.eclipse.adt.io.IFileWrapper;
429aa538ffaf7abdcf4fe56c51da75666e60c67a90Xavier Ducrohetimport com.android.io.IAbstractFile;
433bd45f0b16f5ebfafd8080a0f17f71d85c9840edXavier Ducrohetimport com.android.resources.ResourceType;
444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.core.resources.IFile;
464517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.core.resources.IMarker;
474517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.core.resources.IProject;
484517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.core.resources.IResource;
494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.core.runtime.CoreException;
504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.core.runtime.IStatus;
514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.core.runtime.QualifiedName;
524517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.swt.widgets.Display;
534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.wst.sse.core.StructuredModelManager;
544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IModelManager;
554517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.w3c.dom.Document;
584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.w3c.dom.Element;
594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport org.w3c.dom.NodeList;
604517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
614517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport java.util.ArrayList;
62da02c18ad5b54d97a1fcfd5f6633062b0c873c22Xavier Ducrohetimport java.util.Collection;
634517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport java.util.Collections;
644517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport java.util.HashMap;
654517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport java.util.HashSet;
66adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbyeimport java.util.LinkedList;
674517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport java.util.List;
684517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport java.util.Map;
694517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyeimport java.util.Set;
704517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
714517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye/**
724517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * The include finder finds other XML files that are including a given XML file, and does
734517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye * so efficiently (caching results across IDE sessions etc).
744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye */
75eaf6870a9e0f1cf2a8cc18f2904dc05c7192ac20Tor Norbye@SuppressWarnings("restriction") // XML model
764517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbyepublic class IncludeFinder {
774517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Qualified name for the per-project persistent property include-map */
784517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private final static QualifiedName CONFIG_INCLUDES = new QualifiedName(AdtPlugin.PLUGIN_ID,
794517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            "includes");//$NON-NLS-1$
804517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
814517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
824517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Qualified name for the per-project non-persistent property storing the
834517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * {@link IncludeFinder} for this project
844517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
854517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private final static QualifiedName INCLUDE_FINDER = new QualifiedName(AdtPlugin.PLUGIN_ID,
864563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye            "includefinder"); //$NON-NLS-1$
874517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
884517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Project that the include finder locates includes for */
894517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private final IProject mProject;
904517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
914517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Map from a layout resource name to a set of layouts included by the given resource */
924517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private Map<String, List<String>> mIncludes = null;
934517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
944517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
954517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Reverse map of {@link #mIncludes}; points to other layouts that are including a
964517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * given layouts
974517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
984517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private Map<String, List<String>> mIncludedBy = null;
994517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1004517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Flag set during a refresh; ignore updates when this is true */
1014517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private static boolean sRefreshing;
1024517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1034517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Global (cross-project) resource listener */
1044517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private static ResourceListener sListener;
1054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
1074517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Constructs an {@link IncludeFinder} for the given project. Don't use this method;
1084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * use the {@link #get} factory method instead.
1094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
1104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param project project to create an {@link IncludeFinder} for
1114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
1124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private IncludeFinder(IProject project) {
1134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        mProject = project;
1144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
1154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
1174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Returns the {@link IncludeFinder} for the given project
1184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
1194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param project the project the finder is associated with
1204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return an {@IncludeFinder} for the given project, never null
1214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
1224517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    public static IncludeFinder get(IProject project) {
1234517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        IncludeFinder finder = null;
1244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        try {
1254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            finder = (IncludeFinder) project.getSessionProperty(INCLUDE_FINDER);
1264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        } catch (CoreException e) {
1274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            // Not a problem; we will just create a new one
1284517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
1294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1304517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (finder == null) {
1314517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            finder = new IncludeFinder(project);
1324517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            try {
1334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                project.setSessionProperty(INCLUDE_FINDER, finder);
1344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            } catch (CoreException e) {
1354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                AdtPlugin.log(e, "Can't store IncludeFinder");
1364517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
1374517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
1384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1394517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return finder;
1404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
1414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1424517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
1434517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Returns a list of resource names that are included by the given resource
1444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
1454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param includer the resource name to return included layouts for
1464517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return the layouts included by the given resource
1474517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
1481714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    private List<String> getIncludesFrom(String includer) {
1494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        ensureInitialized();
1504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return mIncludes.get(includer);
1524517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
1534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
1544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
155eaf6870a9e0f1cf2a8cc18f2904dc05c7192ac20Tor Norbye     * Gets the list of all other layouts that are including the given layout.
1564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
1574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param included the file that is included
1584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return the files that are including the given file, or null or empty
1594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
1601714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    public List<Reference> getIncludedBy(IResource included) {
1611714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        ensureInitialized();
1621714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        String mapKey = getMapKey(included);
1631714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        List<String> result = mIncludedBy.get(mapKey);
1641714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        if (result == null) {
1651714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            String name = getResourceName(included);
1661714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (!name.equals(mapKey)) {
1671714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                result = mIncludedBy.get(name);
1681714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            }
1691714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
1701714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
1711714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        if (result != null && result.size() > 0) {
1721714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            List<Reference> references = new ArrayList<Reference>(result.size());
1731714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            for (String s : result) {
1741714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                references.add(new Reference(mProject, s));
1751714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            }
1761714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return references;
1771714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        } else {
1781714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return null;
1791714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
1801714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    }
1811714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
1829bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye    /**
1839bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye     * Returns true if the given resource is included from some other layout in the
1849bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye     * project
1859bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye     *
1869bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye     * @param included the resource to check
1879bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye     * @return true if the file is included by some other layout
1889bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye     */
1899bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye    public boolean isIncluded(IResource included) {
1909bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye        ensureInitialized();
1919bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye        String mapKey = getMapKey(included);
1929bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye        List<String> result = mIncludedBy.get(mapKey);
1939bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye        if (result == null) {
1949bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye            String name = getResourceName(included);
1959bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye            if (!name.equals(mapKey)) {
1969bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye                result = mIncludedBy.get(name);
1979bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye            }
1989bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye        }
1999bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye
2009bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye        return result != null && result.size() > 0;
2019bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye    }
2029bd5e125506d94855fa7f8dff917f20e1b4edb0bTor Norbye
2031714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    @VisibleForTesting
2041714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    /* package */ List<String> getIncludedBy(String included) {
2054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        ensureInitialized();
2064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return mIncludedBy.get(included);
2074517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
2084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Initialize the inclusion data structures, if not already done */
2104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private void ensureInitialized() {
2114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (mIncludes == null) {
2124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            // Initialize
2134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (!readSettings()) {
2144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // Couldn't read settings: probably the first time this code is running
2154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // so there is no known data about includes.
2164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // Yes, these should be multimaps! If we start using Guava replace
2184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // these with multimaps.
2194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                mIncludes = new HashMap<String, List<String>>();
2204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                mIncludedBy = new HashMap<String, List<String>>();
2214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2224517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                scanProject();
2234517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                saveSettings();
2244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
2254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
2264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
2274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2284517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    // ----- Persistence -----
2294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2304517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
2314517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Create a String serialization of the includes map. The map attempts to be compact;
2324517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * it strips out the @layout/ prefix, and eliminates the values for empty string
2334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * values. The map can be restored by calling {@link #decodeMap}. The encoded String
2344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * will have sorted keys.
2354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
2364517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param map the map to be serialized
2374517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return a serialization (never null) of the given map
2384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
2394517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    @VisibleForTesting
2404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    public static String encodeMap(Map<String, List<String>> map) {
2414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        StringBuilder sb = new StringBuilder();
2424517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2434517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (map != null) {
2444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            // Process the keys in sorted order rather than just
2454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            // iterating over the entry set to ensure stable output
2464517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            List<String> keys = new ArrayList<String>(map.keySet());
2474517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            Collections.sort(keys);
2484517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            for (String key : keys) {
2494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                List<String> values = map.get(key);
2504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (sb.length() > 0) {
2524517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    sb.append(',');
2534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
2544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                sb.append(key);
2554517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (values.size() > 0) {
2564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    sb.append('=').append('>');
2574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    sb.append('{');
2584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    boolean first = true;
2594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    for (String value : values) {
2604517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        if (first) {
2614517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            first = false;
2624517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        } else {
2634517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            sb.append(',');
2644517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        }
2654517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        sb.append(value);
2664517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
2674517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    sb.append('}');
2684517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
2694517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
2704517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
2714517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2724517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return sb.toString();
2734517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
2744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2754517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
2764517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Decodes the encoding (produced by {@link #encodeMap}) back into the original map,
2774517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * modulo any key sorting differences.
2784517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
2794517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param encoded an encoding of a map created by {@link #encodeMap}
2804517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return a map corresponding to the encoded values, never null
2814517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
2824517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    @VisibleForTesting
2834517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    public static Map<String, List<String>> decodeMap(String encoded) {
2844517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        HashMap<String, List<String>> map = new HashMap<String, List<String>>();
2854517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2864517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (encoded.length() > 0) {
2874517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            int i = 0;
2884517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            int end = encoded.length();
2894517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2904517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            while (i < end) {
2914517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
2924517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // Find key range
2934517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                int keyBegin = i;
2944517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                int keyEnd = i;
2954517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                while (i < end) {
2964517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    char c = encoded.charAt(i);
2974517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    if (c == ',') {
2984517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        break;
2994517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    } else if (c == '=') {
3004517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        i += 2; // Skip =>
3014517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        break;
3024517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
3034517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    i++;
3044517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    keyEnd = i;
3054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
3064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3074517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                List<String> values = new ArrayList<String>();
3084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // Find values
3094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (i < end && encoded.charAt(i) == '{') {
3104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    i++;
3114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    while (i < end) {
3124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        int valueBegin = i;
3134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        int valueEnd = i;
3144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        char c = 0;
3154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        while (i < end) {
3164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            c = encoded.charAt(i);
3174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            if (c == ',' || c == '}') {
3184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                valueEnd = i;
3194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                break;
3204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            }
3214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            i++;
3224517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        }
3234517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        if (valueEnd > valueBegin) {
3244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            values.add(encoded.substring(valueBegin, valueEnd));
3254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        }
3264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        if (c == '}') {
3281714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                            if (i < end-1 && encoded.charAt(i+1) == ',') {
3291714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                                i++;
3301714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                            }
3314517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            break;
3324517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        }
3334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        assert c == ',';
3344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        i++;
3354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
3364517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
3374517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                String key = encoded.substring(keyBegin, keyEnd);
3394517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                map.put(key, values);
3404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                i++;
3414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
3424517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
3434517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return map;
3454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
3464517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3474517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
3484517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Stores the settings in the persistent project storage.
3494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
3504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private void saveSettings() {
3514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        // Serialize the mIncludes map into a compact String. The mIncludedBy map can be
3524517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        // inferred from it.
3534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        String encoded = encodeMap(mIncludes);
3544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3554517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        try {
3564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (encoded.length() >= 2048) {
3574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // The maximum length of a setting key is 2KB, according to the javadoc
3584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // for the project class. It's unlikely that we'll
3594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // hit this -- even with an average layout root name of 20 characters
3604517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // we can still store over a hundred names. But JUST IN CASE we run
3614517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // into this, we'll clear out the key in this name which means that the
3624517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // information will need to be recomputed in the next IDE session.
3634517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                mProject.setPersistentProperty(CONFIG_INCLUDES, null);
3644517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            } else {
3654517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                String existing = mProject.getPersistentProperty(CONFIG_INCLUDES);
3664517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (!encoded.equals(existing)) {
3674517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    mProject.setPersistentProperty(CONFIG_INCLUDES, encoded);
3684517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
3694517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
3704517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        } catch (CoreException e) {
3714517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            AdtPlugin.log(e, "Can't store include settings");
3724517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
3734517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
3744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3754517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
3764517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Reads previously stored settings from the persistent project storage
3774517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
3784517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return true iff settings were restored from the project
3794517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
3804517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private boolean readSettings() {
3814517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        try {
3824517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            String encoded = mProject.getPersistentProperty(CONFIG_INCLUDES);
3834517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (encoded != null) {
3844517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                mIncludes = decodeMap(encoded);
3854517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3864517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // Set up a reverse map, pointing from included files to the files that
3874517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // included them
3884517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                mIncludedBy = new HashMap<String, List<String>>(2 * mIncludes.size());
3894517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                for (Map.Entry<String, List<String>> entry : mIncludes.entrySet()) {
3904517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    // File containing the <include>
3914517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    String includer = entry.getKey();
3924517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    // Files being <include>'ed by the above file
3934517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    List<String> included = entry.getValue();
3944517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    setIncludedBy(includer, included);
3954517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
3964517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
3974517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                return true;
3984517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
3994517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        } catch (CoreException e) {
4004517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            AdtPlugin.log(e, "Can't read include settings");
4014517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
4024517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4034517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return false;
4044517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
4054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    // ----- File scanning -----
4074517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
4094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Scan the whole project for XML layout resources that are performing includes.
4104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
4114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private void scanProject() {
4124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        ProjectResources resources = ResourceManager.getInstance().getProjectResources(mProject);
4134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (resources != null) {
414da02c18ad5b54d97a1fcfd5f6633062b0c873c22Xavier Ducrohet            Collection<ResourceItem> layouts = resources.getResourceItemsOfType(LAYOUT);
415da02c18ad5b54d97a1fcfd5f6633062b0c873c22Xavier Ducrohet            for (ResourceItem layout : layouts) {
4164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                List<ResourceFile> sources = layout.getSourceFileList();
4174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                for (ResourceFile source : sources) {
4184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    updateFileIncludes(source, false);
4194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
4204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
4214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4224517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            return;
4234517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
4244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
4254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
4274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Scans the given {@link ResourceFile} and if it is a layout resource, updates the
4284517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * includes in it.
4294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
4301714fd536b42f9963c00e171a9d04319832564f2Tor Norbye     * @param resourceFile the {@link ResourceFile} to be scanned for includes (doesn't
4311714fd536b42f9963c00e171a9d04319832564f2Tor Norbye     *            have to be only layout XML files; this method will filter the type)
4324517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param singleUpdate true if this is a single file being updated, false otherwise
4334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *            (e.g. during initial project scanning)
4344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return true if we updated the includes for the resource file
4354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
4364517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private boolean updateFileIncludes(ResourceFile resourceFile, boolean singleUpdate) {
437da02c18ad5b54d97a1fcfd5f6633062b0c873c22Xavier Ducrohet        Collection<ResourceType> resourceTypes = resourceFile.getResourceTypes();
4384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        for (ResourceType type : resourceTypes) {
4394517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (type == ResourceType.LAYOUT) {
4404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                ensureInitialized();
4414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4424517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                List<String> includes = Collections.emptyList();
4434517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (resourceFile.getFile() instanceof IFileWrapper) {
4444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    IFile file = ((IFileWrapper) resourceFile.getFile()).getIFile();
4454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4464517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    // See if we have an existing XML model for this file; if so, we can
4474517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    // just look directly at the parse tree
4484517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    boolean hadXmlModel = false;
4494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    IStructuredModel model = null;
4504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    try {
4514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        IModelManager modelManager = StructuredModelManager.getModelManager();
452ae5f1f7f6a5ef8a0b4ca037524c3cd3209343144Tor Norbye                        model = modelManager.getExistingModelForRead(file);
4534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        if (model instanceof IDOMModel) {
4544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            IDOMModel domModel = (IDOMModel) model;
4554517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            Document document = domModel.getDocument();
4564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            includes = findIncludesInDocument(document);
4574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            hadXmlModel = true;
4584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        }
4594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    } finally {
4604517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        if (model != null) {
4614517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            model.releaseFromRead();
4624517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        }
4634517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
4644517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
465c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                    // If no XML model we have to read the XML contents and (possibly) parse it.
466c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                    // The actual file may not exist anymore (e.g. when deleting a layout file
467c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                    // or when the workspace is out of sync.)
4684517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    if (!hadXmlModel) {
4692a58932d3c4e2642cbdbfc161b4f7b884b3d7ea6Tor Norbye                        String xml = AdtPlugin.readFile(file);
470c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                        if (xml != null) {
471c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                            includes = findIncludes(xml);
472c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                        }
4734517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
4744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                } else {
4752a58932d3c4e2642cbdbfc161b4f7b884b3d7ea6Tor Norbye                    String xml = AdtPlugin.readFile(resourceFile);
476c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                    if (xml != null) {
477c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                        includes = findIncludes(xml);
478c628731aff0ceaef8deee198f8e9956396ff7855Raphael Moll                    }
4794517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
4804517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4811714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                String key = getMapKey(resourceFile);
4821714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                if (includes.equals(getIncludesFrom(key))) {
4834517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    // Common case -- so avoid doing settings flush etc
4844517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    return false;
4854517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
4864517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4874517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                boolean detectCycles = singleUpdate;
4881714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                setIncluded(key, includes, detectCycles);
4894517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4904517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (singleUpdate) {
4914517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    saveSettings();
4924517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
4934517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4944517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                return true;
4954517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
4964517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
4974517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
4984517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return false;
4994517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
5004517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5014517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
5024517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Finds the list of includes in the given XML content. It attempts quickly return
5034517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * empty if the file does not include any include tags; it does this by only parsing
5044517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * if it detects the string &lt;include in the file.
5054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
5064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private List<String> findIncludes(String xml) {
5072f70fafe0bc1cf6d14cb35241ca252ca3cbe5674Tor Norbye        int index = xml.indexOf("<include"); //$NON-NLS-1$
5084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (index != -1) {
5094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            return findIncludesInXml(xml);
5104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
5114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return Collections.emptyList();
5134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
5144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
5164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Parses the given XML content and extracts all the included URLs and returns them
5174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
5184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param xml layout XML content to be parsed for includes
5194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return a list of included urls, or null
5204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
5214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private List<String> findIncludesInXml(String xml) {
5226feca9ac5f8add768fef2bc2dc1382a68c158d42Tor Norbye        Document document = DomUtilities.parseDocument(xml, false /*logParserErrors*/);
5236feca9ac5f8add768fef2bc2dc1382a68c158d42Tor Norbye        if (document != null) {
5244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            return findIncludesInDocument(document);
5254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
5264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return Collections.emptyList();
5284517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
5294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5304517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Searches the given DOM document and returns the list of includes, if any */
5314517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private List<String> findIncludesInDocument(Document document) {
53212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye        NodeList includes = document.getElementsByTagName(VIEW_INCLUDE);
5334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (includes.getLength() > 0) {
5344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            List<String> urls = new ArrayList<String>();
5354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            for (int i = 0; i < includes.getLength(); i++) {
5364517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                Element element = (Element) includes.item(i);
53712d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                String url = element.getAttribute(ATTR_LAYOUT);
5384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (url.length() > 0) {
5394517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    String resourceName = urlToLocalResource(url);
5404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    if (resourceName != null) {
5414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        urls.add(resourceName);
5424517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
5434517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
5444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
5454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5464517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            return urls;
5474517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
5484517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return Collections.emptyList();
5504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
5514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5524517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
5534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Returns the layout URL to a local resource name (provided the URL is a local
5544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * resource, not something in @android etc.) Returns null otherwise.
5554517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
5564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private static String urlToLocalResource(String url) {
5574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (!url.startsWith("@")) { //$NON-NLS-1$
5584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            return null;
5594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
5604517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        int typeEnd = url.indexOf('/', 1);
5614517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (typeEnd == -1) {
5624517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            return null;
5634517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
5644517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        int nameBegin = typeEnd + 1;
5654517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        int typeBegin = 1;
5664517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        int colon = url.lastIndexOf(':', typeEnd);
5674517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (colon != -1) {
5684517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            String packageName = url.substring(typeBegin, colon);
5694517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if ("android".equals(packageName)) { //$NON-NLS-1$
5704517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // Don't want to point to non-local resources
5714517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                return null;
5724517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
5734517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            typeBegin = colon + 1;
5752f70fafe0bc1cf6d14cb35241ca252ca3cbe5674Tor Norbye            assert "layout".equals(url.substring(typeBegin, typeEnd)); //$NON-NLS-1$
5764517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
5774517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5784517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return url.substring(nameBegin);
5794517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
5804517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
5814517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
5824517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Record the list of included layouts from the given layout
5834517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
5844517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param includer the layout including other layouts
5854517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param included the layouts that were included by the including layout
5864517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param detectCycles if true, check for cycles and report them as project errors
5874517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
5884517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    @VisibleForTesting
5894517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /* package */ void setIncluded(String includer, List<String> included, boolean detectCycles) {
5904517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        // Remove previously linked inverse mappings
5914517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        List<String> oldIncludes = mIncludes.get(includer);
5924517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (oldIncludes != null && oldIncludes.size() > 0) {
5934517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            for (String includee : oldIncludes) {
5944517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                List<String> includers = mIncludedBy.get(includee);
5954517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (includers != null) {
5964517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    includers.remove(includer);
5974517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
5984517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
5994517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
6004517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6014517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        mIncludes.put(includer, included);
6024517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        // Reverse mapping: for included items, point back to including file
6034517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        setIncludedBy(includer, included);
6044517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (detectCycles) {
6064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            detectCycles(includer);
6074517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
6084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
6094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Record the list of included layouts from the given layout */
6114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private void setIncludedBy(String includer, List<String> included) {
6124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        for (String target : included) {
6134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            List<String> list = mIncludedBy.get(target);
6144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (list == null) {
6154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                list = new ArrayList<String>(2); // We don't expect many includes
6164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                mIncludedBy.put(target, list);
6174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
6184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (!list.contains(includer)) {
6194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                list.add(includer);
6204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
6214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
6224517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
6234517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Start listening on project resources */
6254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    public static void start() {
6264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        assert sListener == null;
6274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        sListener = new ResourceListener();
6284517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        ResourceManager.getInstance().addListener(sListener);
6294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
6304517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6314517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    public static void stop() {
6324517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        assert sListener != null;
6334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        ResourceManager.getInstance().addListener(sListener);
6344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
6354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6361714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    private static String getMapKey(ResourceFile resourceFile) {
6371714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        IAbstractFile file = resourceFile.getFile();
6381714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        String name = file.getName();
6391714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        String folderName = file.getParentFolder().getName();
6401714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        return getMapKey(folderName, name);
6411714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    }
6421714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
6431714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    private static String getMapKey(IResource resourceFile) {
6441714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        String folderName = resourceFile.getParent().getName();
6451714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        String name = resourceFile.getName();
6461714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        return getMapKey(folderName, name);
6471714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    }
6481714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
6491714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    private static String getResourceName(IResource resourceFile) {
6501714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        String name = resourceFile.getName();
6511714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        int baseEnd = name.length() - EXT_XML.length() - 1; // -1: the dot
6521714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        if (baseEnd > 0) {
6531714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            name = name.substring(0, baseEnd);
6541714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
6551714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
6561714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        return name;
6571714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    }
6581714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
6591714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    private static String getMapKey(String folderName, String name) {
6601714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        int baseEnd = name.length() - EXT_XML.length() - 1; // -1: the dot
6611714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        if (baseEnd > 0) {
6621714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            name = name.substring(0, baseEnd);
6631714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
6641714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
6651714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        // Create a map key for the given resource file
6661714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        // This will map
6671714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        //     /res/layout/foo.xml => "foo"
6681714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        //     /res/layout-land/foo.xml => "-land/foo"
6691714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
670868a7bbe7c5862c02483ef8f71276fc551d40d60Xavier Ducrohet        if (FD_RES_LAYOUT.equals(folderName)) {
6711714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            // Normal case -- keep just the basename
6721714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return name;
6731714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        } else {
6741714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            // Store the relative path from res/ on down, so
6751714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            // /res/layout-land/foo.xml becomes "layout-land/foo"
6761714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            //if (folderName.startsWith(FD_LAYOUT)) {
6771714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            //    folderName = folderName.substring(FD_LAYOUT.length());
6781714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            //}
6791714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
6801714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return folderName + WS_SEP + name;
6811714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
6821714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    }
6831714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
6844517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Listener of resource file saves, used to update layout inclusion data structures */
6854517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private static class ResourceListener implements IResourceListener {
686ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye        @Override
6874517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        public void fileChanged(IProject project, ResourceFile file, int eventType) {
6884517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (sRefreshing) {
6894517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                return;
6904517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
6914517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6924517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if ((eventType & (CHANGED | ADDED | REMOVED | CONTENT)) == 0) {
6934517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                return;
6944517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
6954517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
6964517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            IncludeFinder finder = get(project);
6974517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (finder != null) {
6984517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (finder.updateFileIncludes(file, true)) {
6994517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    finder.saveSettings();
7004517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
7014517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
7024517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
7034517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
704ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye        @Override
7054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        public void folderChanged(IProject project, ResourceFolder folder, int eventType) {
7064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            // We only care about layout resource files
7074517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
7084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
7094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    // ----- Cycle detection -----
7114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private void detectCycles(String from) {
7134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        // Perform DFS on the include graph and look for a cycle; if we find one, produce
7144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        // a chain of includes on the way back to show to the user
7154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (mIncludes.size() > 0) {
716d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye            Set<String> visiting = new HashSet<String>(mIncludes.size());
717d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye            String chain = dfs(from, visiting);
7184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (chain != null) {
7194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                addError(from, chain);
7204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            } else {
7214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // Is there an existing error for us to clean up?
7224517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                removeErrors(from);
7234517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
7244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
7254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
7264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Format to chain include cycles in: a=>b=>c=>d etc */
7282f70fafe0bc1cf6d14cb35241ca252ca3cbe5674Tor Norbye    private final String CHAIN_FORMAT = "%1$s=>%2$s"; //$NON-NLS-1$
7294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
730d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye    private String dfs(String from, Set<String> visiting) {
731d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye        visiting.add(from);
7324517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        List<String> includes = mIncludes.get(from);
7344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (includes != null && includes.size() > 0) {
7354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            for (String include : includes) {
736d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye                if (visiting.contains(include)) {
7374517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    return String.format(CHAIN_FORMAT, from, include);
7384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
739d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye                String chain = dfs(include, visiting);
7404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                if (chain != null) {
7414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    return String.format(CHAIN_FORMAT, from, chain);
7424517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
7434517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
7444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
7454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
746d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye        visiting.remove(from);
747d99f34325d9d2ace6968d6f46b57f5e5bf773e31Tor Norbye
7484517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return null;
7494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
7504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private void removeErrors(String from) {
7524517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        final IResource resource = findResource(from);
7534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (resource != null) {
7544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            try {
7554517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                final String markerId = IMarker.PROBLEM;
7564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                IMarker[] markers = resource.findMarkers(markerId, true, IResource.DEPTH_ZERO);
7584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                for (final IMarker marker : markers) {
7604517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null);
7614517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    if (tmpMsg == null || tmpMsg.startsWith(MESSAGE)) {
7624517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        // Remove
7634517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        runLater(new Runnable() {
764ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye                            @Override
7654517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            public void run() {
7664517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                try {
7674517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                    sRefreshing = true;
7684517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                    marker.delete();
7694517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                } catch (CoreException e) {
7704517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                    AdtPlugin.log(e, "Can't delete problem marker");
7714517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                } finally {
7724517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                    sRefreshing = false;
7734517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                }
7744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            }
7754517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        });
7764517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
7774517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
7784517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            } catch (CoreException e) {
7794517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // if we couldn't get the markers, then we just mark the file again
7804517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // (since markerAlreadyExists is initialized to false, we do nothing)
7814517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
7824517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
7834517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
7844517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7854517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /** Error message for cycles */
7864517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private static final String MESSAGE = "Found cyclical <include> chain";
7874517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7884517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private void addError(String from, String chain) {
7894517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        final IResource resource = findResource(from);
7904517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (resource != null) {
7914517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            final String markerId = IMarker.PROBLEM;
7924517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            final String message = String.format("%1$s: %2$s", MESSAGE, chain);
7934517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            final int lineNumber = 1;
7944517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            final int severity = IMarker.SEVERITY_ERROR;
7954517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
7964517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            // check if there's a similar marker already, since aapt is launched twice
7974517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            boolean markerAlreadyExists = false;
7984517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            try {
7994517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                IMarker[] markers = resource.findMarkers(markerId, true, IResource.DEPTH_ZERO);
8004517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8014517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                for (IMarker marker : markers) {
8024517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
8034517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    if (tmpLine != lineNumber) {
8044517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        break;
8054517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
8064517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8074517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    int tmpSeverity = marker.getAttribute(IMarker.SEVERITY, -1);
8084517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    if (tmpSeverity != severity) {
8094517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        break;
8104517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
8114517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8124517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null);
8134517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    if (tmpMsg == null || tmpMsg.equals(message) == false) {
8144517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        break;
8154517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
8164517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8174517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    // if we're here, all the marker attributes are equals, we found it
8184517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    // and exit
8194517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    markerAlreadyExists = true;
8204517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    break;
8214517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                }
8224517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8234517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            } catch (CoreException e) {
8244517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // if we couldn't get the markers, then we just mark the file again
8254517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                // (since markerAlreadyExists is initialized to false, we do nothing)
8264517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
8274517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8284517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            if (!markerAlreadyExists) {
8294517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                runLater(new Runnable() {
830ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye                    @Override
8314517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    public void run() {
8324517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        try {
8334517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            sRefreshing = true;
8344517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8354517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            // Adding a resource will force a refresh on the file;
8364517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            // ignore these updates
8374517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            BaseProjectHelper.markResource(resource, markerId, message, lineNumber,
8384517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                                    severity);
8394517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        } finally {
8404517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                            sRefreshing = false;
8414517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                        }
8424517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                    }
8434517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye                });
8444517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            }
8454517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
8464517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
8474517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8484517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    // FIXME: Find more standard Eclipse way to do this.
8494517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    // We need to run marker registration/deletion "later", because when the include
8504517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    // scanning is running it's in the middle of resource notification, so the IDE
8514517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    // throws an exception
8524517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private static void runLater(Runnable runnable) {
8534517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        Display display = Display.findDisplay(Thread.currentThread());
8544517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        if (display != null) {
8554517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            display.asyncExec(runnable);
8564517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        } else {
8574517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye            AdtPlugin.log(IStatus.WARNING, "Could not find display");
8584517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        }
8594517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
8604517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8614517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
8624517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Finds the project resource for the given layout path
8634517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     *
8644517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @param from the resource name
8654517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * @return the {@link IResource}, or null if not found
8664517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
8674517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    private IResource findResource(String from) {
8684517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        final IResource resource = mProject.findMember(WS_LAYOUTS + WS_SEP + from + '.' + EXT_XML);
8694517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return resource;
8704517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
8714517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye
8724517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /**
8734517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * Creates a blank, project-less {@link IncludeFinder} <b>for use by unit tests
8744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     * only</b>
8754517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye     */
8764517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    @VisibleForTesting
8774517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    /* package */ static IncludeFinder create() {
8784517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        IncludeFinder finder = new IncludeFinder(null);
8794517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        finder.mIncludes = new HashMap<String, List<String>>();
8804517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        finder.mIncludedBy = new HashMap<String, List<String>>();
8814517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye        return finder;
8824517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye    }
8831714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
8841714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    /** A reference to a particular file in the project */
8851714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    public static class Reference {
8861714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /** The unique id referencing the file, such as (for res/layout-land/main.xml)
8871714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * "layout-land/main") */
8881714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        private final String mId;
8891714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
8901714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /** The project containing the file */
8911714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        private final IProject mProject;
8921714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
8931714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /** The resource name of the file, such as (for res/layout/main.xml) "main" */
8941714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        private String mName;
8951714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
8961714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /** Creates a new include reference */
8971714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        private Reference(IProject project, String id) {
8981714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            super();
8991714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            mProject = project;
9001714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            mId = id;
9011714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9021714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9031714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /**
9041714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * Returns the id identifying the given file within the project
9051714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         *
9061714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * @return the id identifying the given file within the project
9071714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         */
9081714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public String getId() {
9091714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return mId;
9101714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9111714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9121714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /**
9131714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * Returns the {@link IFile} in the project for the given file. May return null if
9141714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * there is an error in locating the file or if the file no longer exists.
9151714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         *
9161714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * @return the project file, or null
9171714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         */
9181714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public IFile getFile() {
9191714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            String reference = mId;
9201714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (!reference.contains(WS_SEP)) {
921868a7bbe7c5862c02483ef8f71276fc551d40d60Xavier Ducrohet                reference = FD_RES_LAYOUT + WS_SEP + reference;
9221714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            }
9231714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
92412d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye            String projectPath = FD_RESOURCES + WS_SEP + reference + '.' + EXT_XML;
9251714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            IResource member = mProject.findMember(projectPath);
9261714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (member instanceof IFile) {
9271714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                return (IFile) member;
9281714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            }
9291714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9301714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return null;
9311714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9321714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9331714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /**
9341714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * Returns a description of this reference, suitable to be shown to the user
9351714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         *
9361714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * @return a display name for the reference
9371714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         */
9381714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public String getDisplayName() {
9391714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            // The ID is deliberately kept in a pretty user-readable format but we could
9401714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            // consider prepending layout/ on ids that don't have it (to make the display
9411714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            // more uniform) or ripping out all layout[-constraint] prefixes out and
9421714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            // instead prepending @ etc.
9431714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return mId;
9441714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9451714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9461714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /**
9471714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * Returns the name of the reference, suitable for resource lookup. For example,
9481714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * for "res/layout/main.xml", as well as for "res/layout-land/main.xml", this
9491714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * would be "main".
9501714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         *
9511714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * @return the resource name of the reference
9521714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         */
9531714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public String getName() {
9541714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (mName == null) {
9551714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                mName = mId;
9561714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                int index = mName.lastIndexOf(WS_SEP);
9571714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                if (index != -1) {
9581714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                    mName = mName.substring(index + 1);
9591714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                }
9601714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            }
9611714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9621714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return mName;
9631714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9641714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9651714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        @Override
9661714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public int hashCode() {
9671714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            final int prime = 31;
9681714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            int result = 1;
9691714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            result = prime * result + ((mId == null) ? 0 : mId.hashCode());
9701714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return result;
9711714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9721714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9731714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        @Override
9741714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public boolean equals(Object obj) {
9751714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (this == obj)
9761714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                return true;
9771714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (obj == null)
9781714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                return false;
9791714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (getClass() != obj.getClass())
9801714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                return false;
9811714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            Reference other = (Reference) obj;
9821714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            if (mId == null) {
9831714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                if (other.mId != null)
9841714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                    return false;
9851714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            } else if (!mId.equals(other.mId))
9861714fd536b42f9963c00e171a9d04319832564f2Tor Norbye                return false;
9871714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return true;
9881714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9891714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9901714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        @Override
9911714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public String toString() {
9922f70fafe0bc1cf6d14cb35241ca252ca3cbe5674Tor Norbye            return "Reference [getId()=" + getId() //$NON-NLS-1$
9932f70fafe0bc1cf6d14cb35241ca252ca3cbe5674Tor Norbye                    + ", getDisplayName()=" + getDisplayName() //$NON-NLS-1$
9942f70fafe0bc1cf6d14cb35241ca252ca3cbe5674Tor Norbye                    + ", getName()=" + getName() //$NON-NLS-1$
9952f70fafe0bc1cf6d14cb35241ca252ca3cbe5674Tor Norbye                    + ", getFile()=" + getFile() + "]"; //$NON-NLS-1$
9961714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
9971714fd536b42f9963c00e171a9d04319832564f2Tor Norbye
9981714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        /**
9991714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * Creates a reference to the given file
10001714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         *
10011714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * @param file the file to create a reference for
10021714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         * @return a reference to the given file
10031714fd536b42f9963c00e171a9d04319832564f2Tor Norbye         */
10041714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        public static Reference create(IFile file) {
10051714fd536b42f9963c00e171a9d04319832564f2Tor Norbye            return new Reference(file.getProject(), getMapKey(file));
10061714fd536b42f9963c00e171a9d04319832564f2Tor Norbye        }
1007adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
1008adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        /**
1009adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye         * Returns the resource name of this layout, such as {@code @layout/foo}.
1010adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye         *
1011adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye         * @return the resource name
1012adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye         */
1013adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        public String getResourceName() {
1014adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            return '@' + FD_RES_LAYOUT + '/' + getName();
1015adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        }
1016adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye    }
1017adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
1018adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye    /**
1019adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     * Returns a collection of layouts (expressed as resource names, such as
1020adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     * {@code @layout/foo} which would be invalid includes in the given layout
1021adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     * (because it would introduce a cycle)
1022adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     *
1023adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     * @param layout the layout file to check for cyclic dependencies from
1024adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     * @return a collection of layout resources which cannot be included from
1025adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     *         the given layout, never null
1026adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye     */
1027adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye    public Collection<String> getInvalidIncludes(IFile layout) {
1028adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        IProject project = layout.getProject();
1029adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        Reference self = Reference.create(layout);
1030adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
1031adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        // Add anyone who transitively can reach this file via includes.
1032adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        LinkedList<Reference> queue = new LinkedList<Reference>();
1033adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        List<Reference> invalid = new ArrayList<Reference>();
1034adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        queue.add(self);
1035adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        invalid.add(self);
1036adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        Set<String> seen = new HashSet<String>();
1037adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        seen.add(self.getId());
1038adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        while (!queue.isEmpty()) {
1039adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            Reference reference = queue.removeFirst();
1040adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            String refId = reference.getId();
1041adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
1042adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            // Look up both configuration specific includes as well as includes in the
1043adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            // base versions
1044adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            List<String> included = getIncludedBy(refId);
1045adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            if (refId.indexOf('/') != -1) {
1046adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                List<String> baseIncluded = getIncludedBy(reference.getName());
1047adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                if (included == null) {
1048adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                    included = baseIncluded;
1049adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                } else if (baseIncluded != null) {
1050adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                    included = new ArrayList<String>(included);
1051adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                    included.addAll(baseIncluded);
1052adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                }
1053adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            }
1054adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
1055adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            if (included != null && included.size() > 0) {
1056adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                for (String id : included) {
1057adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                    if (!seen.contains(id)) {
1058adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                        seen.add(id);
1059adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                        Reference ref = new Reference(project, id);
1060adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                        invalid.add(ref);
1061adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                        queue.addLast(ref);
1062adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                    }
1063adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye                }
1064adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            }
1065adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        }
1066adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
1067adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        List<String> result = new ArrayList<String>();
1068adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        for (Reference reference : invalid) {
1069adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye            result.add(reference.getResourceName());
1070adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        }
1071adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye
1072adee9788a5ac646a39b516abe4cdd1022911a3f5Tor Norbye        return result;
10731714fd536b42f9963c00e171a9d04319832564f2Tor Norbye    }
10744517a1f5f4f9fd21b6a611d8a40ac8b81a7bb9c5Tor Norbye}
1075