From 05158f48e244a627c93026c4544beb5c14f9d83c Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 30 Jan 2012 22:10:39 +0400 Subject: [PATCH 1/6] android performance improvement: indexing of sdk resources --- plugins/android/src/META-INF/plugin.xml | 1 + .../org/jetbrains/android/AndroidIdIndex.java | 3 +- .../AndroidResourcesLineMarkerProvider.java | 4 +- .../android/AndroidValueResourcesIndex.java | 198 ++++++++++++++++++ .../android/AndroidXmlSchemaProvider.java | 3 +- .../actions/CreateXmlResourceDialog.java | 4 +- .../compiler/ResourceNamesValidityState.java | 1 + .../jetbrains/android/facet/AndroidFacet.java | 1 + .../facet/AndroidResourceFilesListener.java | 1 + .../LocalResourceManager.java | 19 +- .../resourceManagers/ResourceManager.java | 64 +++--- .../SystemResourceManager.java | 111 +++------- .../android/sdk/AndroidSdkUtils.java | 10 + .../android/util/AndroidResourceUtil.java | 68 ++++-- .../{compiler => util}/ResourceEntry.java | 4 +- .../testData/dom/layout/layoutAttrs3.xml | 3 + .../jetbrains/android/AndroidTestCase.java | 4 + .../android/dom/AndroidLayoutDomTest.java | 4 + 18 files changed, 345 insertions(+), 158 deletions(-) create mode 100644 plugins/android/src/org/jetbrains/android/AndroidValueResourcesIndex.java rename plugins/android/src/org/jetbrains/android/{compiler => util}/ResourceEntry.java (88%) create mode 100644 plugins/android/testData/dom/layout/layoutAttrs3.xml diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index bcbaf0fd30ca..e6e625de60e8 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -173,6 +173,7 @@ serviceImplementation="org.jetbrains.android.maven.AndroidExternalApklibDependenciesManager"/> + diff --git a/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java b/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java index 0344676ae229..96e3738d55a3 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java +++ b/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java @@ -30,7 +30,6 @@ import com.intellij.util.containers.HashMap; import com.intellij.util.indexing.*; import com.intellij.util.io.EnumeratorStringDescriptor; import com.intellij.util.io.KeyDescriptor; -import org.jetbrains.android.resourceManagers.ResourceManager; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.annotations.NotNull; @@ -53,7 +52,7 @@ public class AndroidIdIndex extends ScalarIndexExtension { if (parent == null || !parent.isDirectory()) { return false; } - final String resourceType = ResourceManager.getResourceTypeByDirName(parent.getName()); + final String resourceType = AndroidResourceUtil.getResourceTypeByDirName(parent.getName()); if (resourceType == null || !canContainIdDeclaration(resourceType)) { return false; } diff --git a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java index 2f2db3646235..0a29088483b3 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java +++ b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java @@ -171,7 +171,7 @@ public class AndroidResourcesLineMarkerProvider implements LineMarkerProvider { Collection resourceFiles = resManager.getResourceElements(); for (Resources res : resourceFiles) { for (String valueResourceType : ResourceManager.VALUE_RESOURCE_TYPES) { - for (ResourceElement valueResource : ResourceManager.getValueResources(valueResourceType, res)) { + for (ResourceElement valueResource : ResourceManager.getValueResourcesFromElement(valueResourceType, res)) { addResource(valueResourceType, valueResource, result); } } @@ -197,7 +197,7 @@ public class AndroidResourcesLineMarkerProvider implements LineMarkerProvider { public void run() { List resourceSubdirs = resManager.getResourceSubdirs(null); for (VirtualFile dir : resourceSubdirs) { - String resType = ResourceManager.getResourceTypeByDirName(dir.getName()); + String resType = AndroidResourceUtil.getResourceTypeByDirName(dir.getName()); if (resType != null) { for (VirtualFile resourceFile : dir.getChildren()) { if (!resourceFile.isDirectory()) { diff --git a/plugins/android/src/org/jetbrains/android/AndroidValueResourcesIndex.java b/plugins/android/src/org/jetbrains/android/AndroidValueResourcesIndex.java new file mode 100644 index 000000000000..8bc32348f9c5 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/AndroidValueResourcesIndex.java @@ -0,0 +1,198 @@ +package org.jetbrains.android; + +import com.android.resources.ResourceType; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.ex.temp.TempFileSystem; +import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.HashSet; +import com.intellij.util.indexing.*; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.xml.NanoXmlUtil; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.android.util.ResourceEntry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidValueResourcesIndex extends FileBasedIndexExtension> { + public static final ID> INDEX_ID = ID.create("android.value.resources.index"); + + private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() { + public boolean acceptInput(final VirtualFile file) { + return (file.getFileSystem() == LocalFileSystem.getInstance() || file.getFileSystem() instanceof TempFileSystem) && + file.getFileType() == StdFileTypes.XML; + } + }; + + private final DataIndexer, FileContent> myIndexer = + new DataIndexer, FileContent>() { + @NotNull + public Map> map(FileContent inputData) { + final Map> result = new HashMap>(); + + NanoXmlUtil.parse(new ByteArrayInputStream(inputData.getContent()), new NanoXmlUtil.IXMLBuilderAdapter() { + private boolean mySeenResources = false; + private String myLastTypeAttr = null; + private String myLastNameAttr = null; + + @Override + public void startElement(String name, String nsPrefix, String nsURI, String systemID, int lineNr) throws Exception { + super.startElement(name, nsPrefix, nsURI, systemID, lineNr); + + if (!mySeenResources) { + if ("resources".equals(name)) { + mySeenResources = true; + } + else { + throw new NanoXmlUtil.ParserStoppedException(); + } + } + myLastNameAttr = null; + myLastTypeAttr = null; + } + + @Override + public void addAttribute(String key, String nsPrefix, String nsURI, String value, String type) throws Exception { + super.addAttribute(key, nsPrefix, nsURI, value, type); + + if ("name".equals(key)) { + myLastNameAttr = value; + } + else if ("type".equals(key)) { + myLastTypeAttr = value; + } + } + + @Override + public void elementAttributesProcessed(String name, String nsPrefix, String nsURI) throws Exception { + super.elementAttributesProcessed(name, nsPrefix, nsURI); + + if (myLastNameAttr != null && name != null) { + final String resType = "item".equals(name) + ? myLastTypeAttr + : AndroidResourceUtil.getResourceTypeByTagName(name); + if (resType != null && ResourceType.getEnum(resType) != null) { + final ResourceEntry entry = new ResourceEntry(resType, myLastNameAttr); + result.put(entry, Collections.emptySet()); + + final ResourceEntry typeMarkerEntry = createTypeMarkerEntry(resType); + Set set = result.get(typeMarkerEntry); + + if (set == null) { + set = new HashSet(); + result.put(typeMarkerEntry, set); + } + set.add(entry); + } + } + } + }); + + return result; + } + }; + + public static ResourceEntry createTypeMarkerEntry(String type) { + return new ResourceEntry(type, "TYPE_MARKER_RESOURCE"); + } + + private final KeyDescriptor myKeyDescriptor = new KeyDescriptor() { + @Override + public void save(DataOutput out, ResourceEntry value) throws IOException { + out.writeUTF(value.getType()); + out.writeUTF(value.getName()); + } + + @Override + public ResourceEntry read(DataInput in) throws IOException { + final String resType = in.readUTF(); + final String resName = in.readUTF(); + return new ResourceEntry(resType, resName); + } + + @Override + public int getHashCode(ResourceEntry value) { + return value.hashCode(); + } + + @Override + public boolean isEqual(ResourceEntry val1, ResourceEntry val2) { + return val1.equals(val2); + } + }; + + private final DataExternalizer> myValueExternalizer = new DataExternalizer>() { + @Override + public void save(DataOutput out, Set value) throws IOException { + out.writeInt(value.size()); + + for (ResourceEntry entry : value) { + myKeyDescriptor.save(out, entry); + } + } + + @Nullable + @Override + public Set read(DataInput in) throws IOException { + final int size = in.readInt(); + + if (size == 0) { + return Collections.emptySet(); + } + final Set result = new HashSet(size); + + for (int i = 0; i < size; i++) { + result.add(myKeyDescriptor.read(in)); + } + return result; + } + }; + + @Override + public ID> getName() { + return INDEX_ID; + } + + @Override + public DataIndexer, FileContent> getIndexer() { + return myIndexer; + } + + @Override + public KeyDescriptor getKeyDescriptor() { + return myKeyDescriptor; + } + + @Override + public DataExternalizer> getValueExternalizer() { + return myValueExternalizer; + } + + @Override + public FileBasedIndex.InputFilter getInputFilter() { + return myInputFilter; + } + + @Override + public boolean dependsOnFileContent() { + return true; + } + + @Override + public int getVersion() { + return 0; + } +} diff --git a/plugins/android/src/org/jetbrains/android/AndroidXmlSchemaProvider.java b/plugins/android/src/org/jetbrains/android/AndroidXmlSchemaProvider.java index 9916c9cffec8..5652e9862fdb 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidXmlSchemaProvider.java +++ b/plugins/android/src/org/jetbrains/android/AndroidXmlSchemaProvider.java @@ -36,6 +36,7 @@ import gnu.trove.THashMap; import org.jetbrains.android.dom.manifest.ManifestDomFileDescription; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.resourceManagers.ResourceManager; +import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -107,7 +108,7 @@ public class AndroidXmlSchemaProvider extends XmlSchemaProvider { return false; } - final String resType = ResourceManager.getResourceTypeByDirName(parent.getName()); + final String resType = AndroidResourceUtil.getResourceTypeByDirName(parent.getName()); if (resType == null) { return false; } diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java index dae344b92ced..8bc1055a1fd6 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java @@ -141,7 +141,7 @@ public class CreateXmlResourceDialog extends DialogWrapper { return new ValidationInfo("specify module", myModuleCombo); } else if (!ResourceFolderType.VALUES.getName().equals( - ResourceManager.getResourceTypeByDirName(directoryName))) { + AndroidResourceUtil.getResourceTypeByDirName(directoryName))) { return new ValidationInfo("directory name is not appropriate for value resources"); } @@ -196,7 +196,7 @@ public class CreateXmlResourceDialog extends DialogWrapper { return new ValidationInfo(AndroidBundle.message("not.resource.file.error", FileUtil.toSystemDependentName(resFile.getPath()))); } - for (ResourceElement element : ResourceManager.getValueResources(resourceType.getName(), resources)) { + for (ResourceElement element : ResourceManager.getValueResourcesFromElement(resourceType.getName(), resources)) { if (resourceName.equals(element.getName().getValue())) { return new ValidationInfo("resource '" + resourceName + "' already exists in " + FileUtil.toSystemDependentName( resFile.getPath())); diff --git a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java index e1aefdc3aa16..c1150a0226c6 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java +++ b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java @@ -8,6 +8,7 @@ import com.intellij.util.containers.HashSet; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; import org.jetbrains.android.sdk.AndroidPlatform; +import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import java.io.DataInput; diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java index 3e3b8a51ac3b..371077fede8e 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java @@ -80,6 +80,7 @@ import org.jetbrains.android.resourceManagers.SystemResourceManager; import org.jetbrains.android.sdk.*; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java index 04208af7976a..27d7809494f8 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java @@ -32,6 +32,7 @@ import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.fileTypes.AndroidIdlFileType; import org.jetbrains.android.fileTypes.AndroidRenderscriptFileType; import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java index 5369c0d85a21..91ae8a93420c 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java @@ -20,7 +20,6 @@ import com.android.AndroidConstants; import com.intellij.CommonBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -101,21 +100,7 @@ public class LocalResourceManager extends ResourceManager { } public List getResourceElements() { - return getResourceElements(getAllResourceFiles()); - } - - @NotNull - private Set getAllResourceFiles() { - final Set files = new HashSet(); - - for (VirtualFile valueResourceDir : getResourceSubdirs("values")) { - for (VirtualFile valueResourceFile : valueResourceDir.getChildren()) { - if (!valueResourceFile.isDirectory() && valueResourceFile.getFileType().equals(StdFileTypes.XML)) { - files.add(valueResourceFile); - } - } - } - return files; + return getResourceElements(null); } @NotNull @@ -141,7 +126,7 @@ public class LocalResourceManager extends ResourceManager { @NotNull public List getValueResources(@NotNull final String resourceType) { - return getValueResources(resourceType, getAllResourceFiles()); + return getValueResources(resourceType, null); } private static void collectResourceDirs(Module module, Set result, Set visited) { diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java index d0bee9a74065..b720cce15aa4 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java @@ -17,6 +17,7 @@ package org.jetbrains.android.resourceManagers; import com.android.sdklib.SdkConstants; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; @@ -29,13 +30,14 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashSet; import com.intellij.util.xml.DomElement; import org.jetbrains.android.dom.attrs.AttributeDefinitions; import org.jetbrains.android.dom.resources.Item; import org.jetbrains.android.dom.resources.ResourceElement; import org.jetbrains.android.dom.resources.Resources; import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -93,33 +95,9 @@ public abstract class ResourceManager { return VirtualFile.EMPTY_ARRAY; } - @Nullable - public static String getResourceTypeByDirName(@NotNull String name) { - int index = name.indexOf('-'); - String type = index >= 0 ? name.substring(0, index) : name; - return ArrayUtil.find(FILE_RESOURCE_TYPES, type) >= 0 ? type : null; - } - @NotNull public List getResourceSubdirs(@Nullable String resourceType) { - List dirs = new ArrayList(); - if (ArrayUtil.find(FILE_RESOURCE_TYPES, resourceType) < 0 && resourceType != null) { - return dirs; - } - VirtualFile[] resourcesDirs = getAllResourceDirs(); - for (VirtualFile resourcesDir : resourcesDirs) { - if (resourcesDir == null) return dirs; - if (resourceType == null) { - ContainerUtil.addAll(dirs, resourcesDir.getChildren()); - } - else { - for (VirtualFile child : resourcesDir.getChildren()) { - String type = getResourceTypeByDirName(child.getName()); - if (resourceType.equals(type)) dirs.add(child); - } - } - } - return dirs; + return AndroidResourceUtil.getResourceSubdirs(resourceType, getAllResourceDirs()); } @NotNull @@ -193,21 +171,37 @@ public abstract class ResourceManager { return findResourceFiles(resType, null, true); } - protected List getResourceElements(@NotNull Set files) { + protected List getResourceElements(@Nullable Set files) { return getRootDomElements(Resources.class, files); } private List getRootDomElements(@NotNull Class elementType, - @NotNull Collection files) { + @Nullable Set files) { final List result = new ArrayList(); - for (VirtualFile file : files) { - T element = AndroidUtils.loadDomElement(myModule, file, elementType); - if (element != null) result.add(element); + for (VirtualFile file : getAllResourceFiles()) { + if ((files == null || files.contains(file)) && file.isValid()) { + T element = AndroidUtils.loadDomElement(myModule, file, elementType); + if (element != null) result.add(element); + } } return result; } - protected List getValueResources(final String resourceType, Set files) { + @NotNull + private Set getAllResourceFiles() { + final Set files = new HashSet(); + + for (VirtualFile valueResourceDir : getResourceSubdirs("values")) { + for (VirtualFile valueResourceFile : valueResourceDir.getChildren()) { + if (!valueResourceFile.isDirectory() && valueResourceFile.getFileType().equals(StdFileTypes.XML)) { + files.add(valueResourceFile); + } + } + } + return files; + } + + protected List getValueResources(@NotNull final String resourceType, @Nullable Set files) { final List result = new ArrayList(); Collection resourceFiles = getResourceElements(files); for (final Resources resources : resourceFiles) { @@ -217,7 +211,7 @@ public abstract class ResourceManager { if (!resources.isValid() || myModule.isDisposed() || myModule.getProject().isDisposed()) { return; } - result.addAll(getValueResources(resourceType, resources)); + result.addAll(getValueResourcesFromElement(resourceType, resources)); } }); } @@ -245,7 +239,7 @@ public abstract class ResourceManager { if (possibleResDir == null || !isResourceDir(possibleResDir.getVirtualFile())) { return null; } - String type = getResourceTypeByDirName(dir.getName()); + String type = AndroidResourceUtil.getResourceTypeByDirName(dir.getName()); if (type == null) return null; return isCorrectFileName(type, file.getName()) ? type : null; } @@ -270,7 +264,7 @@ public abstract class ResourceManager { public abstract Collection getValueResourceNames(@NotNull final String resourceType); @NotNull - public static List getValueResources(@NotNull String resourceType, Resources resources) { + public static List getValueResourcesFromElement(@NotNull String resourceType, Resources resources) { List result = new ArrayList(); if (resourceType.equals("string")) { result.addAll(resources.getStrings()); diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java index 83604208d580..43147141ebf5 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java @@ -15,31 +15,32 @@ */ package org.jetbrains.android.resourceManagers; -import com.android.resources.ResourceFolderType; import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; -import com.intellij.lang.injection.InjectedLanguageManager; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.xml.*; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; +import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.xml.ConvertContext; import org.jetbrains.android.AndroidIdIndex; +import org.jetbrains.android.AndroidValueResourcesIndex; import org.jetbrains.android.dom.attrs.AttributeDefinitions; import org.jetbrains.android.dom.resources.ResourceElement; -import org.jetbrains.android.dom.resources.Resources; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.sdk.AndroidTargetData; import org.jetbrains.android.util.AndroidResourceUtil; -import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,8 +54,6 @@ public class SystemResourceManager extends ResourceManager { private volatile Map>> myIdMap; - private volatile Map>> myValueResourcesMap; - private final AndroidPlatform myPlatform; public SystemResourceManager(@NotNull Module module, @NotNull AndroidPlatform androidPlatform) { @@ -83,8 +82,17 @@ public class SystemResourceManager extends ResourceManager { LOG.error("Unknown resource type " + resourceType); return Collections.emptyList(); } - final Map> map = getValueResourcesMap().get(type); - return map != null ? map.keySet() : Collections.emptyList(); + final Set result = new HashSet(); + final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerEntry(resourceType); + final List> values = FileBasedIndex.getInstance() + .getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, GlobalSearchScope.moduleWithLibrariesScope(myModule)); + + for (Set entrySet : values) { + for (ResourceEntry entry : entrySet) { + result.add(entry.getName()); + } + } + return result; } @Nullable @@ -96,7 +104,7 @@ public class SystemResourceManager extends ResourceManager { @Nullable public synchronized AttributeDefinitions getAttributeDefinitions() { final AndroidTargetData targetData = myPlatform.getSdk().getTargetData(myPlatform.getTarget()); - return targetData.getAttrDefs(myModule.getProject()); + return targetData != null ? targetData.getAttrDefs(myModule.getProject()) : null; } @Nullable @@ -108,68 +116,6 @@ public class SystemResourceManager extends ResourceManager { return doFindIdDeclarations(id, true); } - @NotNull - private synchronized Map>> getValueResourcesMap() { - if (myValueResourcesMap == null) { - myValueResourcesMap = new HashMap>>(); - - for (VirtualFile valueResourceDir : getResourceSubdirs(ResourceFolderType.VALUES.getName())) { - for (final VirtualFile valueResourceFile : valueResourceDir.getChildren()) { - if (!valueResourceFile.isDirectory() && valueResourceFile.getFileType().equals(StdFileTypes.XML)) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - fillValueResourcesMap(valueResourceFile); - } - }); - } - } - } - } - return myValueResourcesMap; - } - - private void fillValueResourcesMap(@NotNull VirtualFile valueResourceFile) { - final Resources roots = AndroidUtils.loadDomElement(myModule, valueResourceFile, Resources.class); - if (roots == null) { - return; - } - - for (ResourceType resType : ResourceType.values()) { - Map> map = myValueResourcesMap.get(resType); - - if (map == null) { - map = new HashMap>(); - myValueResourcesMap.put(resType, map); - } - - for (ResourceElement element : ResourceManager.getValueResources(resType.getName(), roots)) { - final String name = element.getName().getValue(); - - if (name != null) { - Set fileSet = map.get(name); - - if (fileSet == null) { - fileSet = new HashSet(); - map.put(name, fileSet); - } - fileSet.add(valueResourceFile); - } - } - } - - PsiManager.getInstance(myModule.getProject()).dropResolveCaches(); - final XmlElement element = roots.getXmlElement(); - - if (element != null) { - final PsiFile file = element.getContainingFile(); - - if (file != null) { - InjectedLanguageManager.getInstance(myModule.getProject()).dropFileCaches(file); - } - } - } - private List doFindIdDeclarations(@NotNull String id, boolean recreateMapIfCannotResolve) { final List> pointers = myIdMap.get(id); @@ -210,17 +156,16 @@ public class SystemResourceManager extends ResourceManager { LOG.error("Unknown resource type " + resourceType); return Collections.emptyList(); } - - final Map> map = getValueResourcesMap().get(type); - if (map == null) { + + final Collection files = FileBasedIndex.getInstance() + .getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, new ResourceEntry(resourceType, resourceName), + GlobalSearchScope.moduleWithLibrariesScope( + myModule)); + + if (files.size() == 0) { return Collections.emptyList(); } - - final Set fileSet = map.get(resourceName); - if (fileSet == null) { - return Collections.emptyList(); - } - + final Set fileSet = new HashSet(files); final List result = new ArrayList(); for (ResourceElement element : getValueResources(resourceType, fileSet)) { diff --git a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java index 7f422a5134b1..766e05545306 100644 --- a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java +++ b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java @@ -98,6 +98,16 @@ public class AndroidSdkUtils { if (sdkDir != null) { addJavaDocAndSources(result, sdkDir); } + + final String resFolderPath = target.getPath(IAndroidTarget.RESOURCES); + + if (resFolderPath != null) { + final VirtualFile resFolder = LocalFileSystem.getInstance().findFileByPath(resFolderPath); + + if (resFolder != null) { + result.add(new OrderRoot(resFolder, OrderRootType.CLASSES)); + } + } return result; } diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java index 4d95bb5dc5b1..8fe65fbb034b 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java @@ -30,7 +30,9 @@ import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.android.AndroidIdIndex; @@ -217,24 +219,34 @@ public class AndroidResourceUtil { LocalResourceManager manager = facet.getLocalResourceManager(); String fileResType = manager.getFileResourceType(tag.getContainingFile()); if ("values".equals(fileResType)) { - String resClassName = tag.getName(); - if (resClassName.equals("item")) { - resClassName = tag.getAttributeValue("type", null); - } - else if (resClassName.equals("declare-styleable")) { - resClassName = "styleable"; - } - else if (resClassName.endsWith("-array")) { - resClassName = "array"; - } - if (resClassName != null) { - String resourceName = tag.getAttributeValue("name"); - return resourceName != null ? resClassName : null; - } + return getResourceTypeByValueResourceTag(tag); } return null; } + @Nullable + public static String getResourceTypeByValueResourceTag(XmlTag tag) { + String resClassName = tag.getName(); + resClassName = resClassName.equals("item") + ? tag.getAttributeValue("type", null) + : getResourceTypeByTagName(resClassName); + if (resClassName != null) { + final String resourceName = tag.getAttributeValue("name"); + return resourceName != null ? resClassName : null; + } + return null; + } + + public static String getResourceTypeByTagName(@NotNull String tagName) { + if (tagName.equals("declare-styleable")) { + tagName = "styleable"; + } + else if (tagName.endsWith("-array")) { + tagName = "array"; + } + return tagName; + } + @Nullable public static String getResourceClassName(@NotNull PsiField field) { PsiClass resourceClass = field.getContainingClass(); @@ -435,4 +447,32 @@ public class AndroidResourceUtil { } throw new IllegalArgumentException("Incorrect resource type"); } + + @Nullable + public static String getResourceTypeByDirName(@NotNull String name) { + int index = name.indexOf('-'); + String type = index >= 0 ? name.substring(0, index) : name; + return ArrayUtil.find(ResourceManager.FILE_RESOURCE_TYPES, type) >= 0 ? type : null; + } + + @NotNull + public static List getResourceSubdirs(@Nullable String resourceType, @NotNull VirtualFile[] resourceDirs) { + List dirs = new ArrayList(); + if (ArrayUtil.find(ResourceManager.FILE_RESOURCE_TYPES, resourceType) < 0 && resourceType != null) { + return dirs; + } + for (VirtualFile resourcesDir : resourceDirs) { + if (resourcesDir == null) return dirs; + if (resourceType == null) { + ContainerUtil.addAll(dirs, resourcesDir.getChildren()); + } + else { + for (VirtualFile child : resourcesDir.getChildren()) { + String type = getResourceTypeByDirName(child.getName()); + if (resourceType.equals(type)) dirs.add(child); + } + } + } + return dirs; + } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/ResourceEntry.java b/plugins/android/src/org/jetbrains/android/util/ResourceEntry.java similarity index 88% rename from plugins/android/src/org/jetbrains/android/compiler/ResourceEntry.java rename to plugins/android/src/org/jetbrains/android/util/ResourceEntry.java index 868e523eb3f2..c9fbe72541fa 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/ResourceEntry.java +++ b/plugins/android/src/org/jetbrains/android/util/ResourceEntry.java @@ -1,4 +1,4 @@ -package org.jetbrains.android.compiler; +package org.jetbrains.android.util; import org.jetbrains.annotations.NotNull; @@ -9,7 +9,7 @@ public class ResourceEntry { private final String myType; private final String myName; - ResourceEntry(@NotNull String type, @NotNull String name) { + public ResourceEntry(@NotNull String type, @NotNull String name) { myType = type; myName = name; } diff --git a/plugins/android/testData/dom/layout/layoutAttrs3.xml b/plugins/android/testData/dom/layout/layoutAttrs3.xml new file mode 100644 index 000000000000..d3f552d4ac25 --- /dev/null +++ b/plugins/android/testData/dom/layout/layoutAttrs3.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/AndroidTestCase.java b/plugins/android/testSrc/org/jetbrains/android/AndroidTestCase.java index 27721ed74b60..966daab2e5cd 100644 --- a/plugins/android/testSrc/org/jetbrains/android/AndroidTestCase.java +++ b/plugins/android/testSrc/org/jetbrains/android/AndroidTestCase.java @@ -29,6 +29,7 @@ import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; @@ -151,6 +152,9 @@ public abstract class AndroidTestCase extends JavaCodeInsightFixtureTestCase { VirtualFile androidJar = JarFileSystem.getInstance().findFileByPath(androidJarPath); sdkModificator.addRoot(androidJar, OrderRootType.CLASSES); + VirtualFile resFolder = LocalFileSystem.getInstance().findFileByPath(sdkPath + "/platforms/android-1.5/data/res"); + sdkModificator.addRoot(resFolder, OrderRootType.CLASSES); + AndroidSdkAdditionalData data = new AndroidSdkAdditionalData(sdk); AndroidSdk sdkObject = AndroidSdk.parse(sdkPath, new EmptySdkLog()); data.setBuildTarget(sdkObject.findTargetByName("Android 1.5")); diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java index bc2c6d48a911..ebf4b0d9ac74 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java @@ -83,6 +83,10 @@ public class AndroidLayoutDomTest extends AndroidDomTest { doTestHighlighting("layoutAttrs2.xml"); } + public void testCheckLayoutAttrs3() throws Throwable { + doTestHighlighting("layoutAttrs3.xml"); + } + public void testUnknownAttribute() throws Throwable { doTestHighlighting("hl1.xml"); } From 9a255f41434550125b4d89fb60240cfea6ac8c3b Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 30 Jan 2012 22:35:14 +0400 Subject: [PATCH 2/6] speed up: migrate to nano xml --- .../org/jetbrains/android/AndroidIdIndex.java | 68 ++++++++----------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java b/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java index 96e3738d55a3..11ef3f940486 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java +++ b/plugins/android/src/org/jetbrains/android/AndroidIdIndex.java @@ -21,18 +21,16 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.ex.temp.TempFileSystem; -import com.intellij.psi.PsiFile; -import com.intellij.psi.XmlRecursiveElementVisitor; -import com.intellij.psi.xml.XmlAttributeValue; -import com.intellij.psi.xml.XmlFile; -import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.indexing.*; import com.intellij.util.io.EnumeratorStringDescriptor; import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.text.CharArrayUtil; +import com.intellij.util.xml.NanoXmlUtil; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.annotations.NotNull; +import java.io.ByteArrayInputStream; import java.util.Collections; import java.util.Map; @@ -46,53 +44,41 @@ public class AndroidIdIndex extends ScalarIndexExtension { private static final FileBasedIndex.InputFilter INPUT_FILTER = new FileBasedIndex.InputFilter() { public boolean acceptInput(final VirtualFile file) { - if ((file.getFileSystem() == LocalFileSystem.getInstance() || file.getFileSystem() instanceof TempFileSystem) && - file.getFileType() == StdFileTypes.XML) { - VirtualFile parent = file.getParent(); - if (parent == null || !parent.isDirectory()) { - return false; - } - final String resourceType = AndroidResourceUtil.getResourceTypeByDirName(parent.getName()); - if (resourceType == null || !canContainIdDeclaration(resourceType)) { - return false; - } - parent = parent.getParent(); - return parent != null && SdkConstants.FD_RES.equals(parent.getName()); - } - return false; + return (file.getFileSystem() == LocalFileSystem.getInstance() || file.getFileSystem() instanceof TempFileSystem) && + file.getFileType() == StdFileTypes.XML; } }; private static final DataIndexer INDEXER = new DataIndexer() { @NotNull public Map map(FileContent inputData) { - PsiFile file = inputData.getPsiFile(); - if (file instanceof XmlFile) { - final HashMap ids = new HashMap(); - file.accept(new XmlRecursiveElementVisitor() { - @Override - public void visitXmlAttributeValue(XmlAttributeValue attributeValue) { - if (AndroidResourceUtil.isIdDeclaration(attributeValue)) { - String id = AndroidResourceUtil.getResourceNameByReferenceText(attributeValue.getValue()); - if (id != null) { - if (ids.isEmpty()) { - ids.put(MARKER, null); - } - ids.put(id, null); + final CharSequence content = inputData.getContentAsText(); + + if (content == null || CharArrayUtil.indexOf(content, SdkConstants.NS_RESOURCES, 0) == -1) { + return Collections.emptyMap(); + } + final HashMap ids = new HashMap(); + + NanoXmlUtil.parse(new ByteArrayInputStream(inputData.getContent()), new NanoXmlUtil.IXMLBuilderAdapter() { + @Override + public void addAttribute(String key, String nsPrefix, String nsURI, String value, String type) throws Exception { + super.addAttribute(key, nsPrefix, nsURI, value, type); + + if (AndroidResourceUtil.isIdDeclaration(value)) { + String id = AndroidResourceUtil.getResourceNameByReferenceText(value); + if (id != null) { + if (ids.isEmpty()) { + ids.put(MARKER, null); } + ids.put(id, null); } } - }); - return ids; - } - return Collections.emptyMap(); + } + }); + return ids; } }; - private static boolean canContainIdDeclaration(@NotNull String resType) { - return ArrayUtil.find(RES_TYPES_CONTAINING_ID_DECLARATIONS, resType) >= 0; - } - @Override public ID getName() { return INDEX_ID; @@ -120,6 +106,6 @@ public class AndroidIdIndex extends ScalarIndexExtension { @Override public int getVersion() { - return 1; + return 2; } } From b18673254f97fdb02db7337f8f34e08a43ef4246 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 30 Jan 2012 23:09:40 +0400 Subject: [PATCH 3/6] do not suggest value local value resources --- .../resourceManagers/ResourceManager.java | 2 +- .../SystemResourceManager.java | 37 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java index b720cce15aa4..1d65b487c475 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java @@ -188,7 +188,7 @@ public abstract class ResourceManager { } @NotNull - private Set getAllResourceFiles() { + protected Set getAllResourceFiles() { final Set files = new HashSet(); for (VirtualFile valueResourceDir : getResourceSubdirs("values")) { diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java index 43147141ebf5..a0aa3d098be8 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java @@ -82,14 +82,36 @@ public class SystemResourceManager extends ResourceManager { LOG.error("Unknown resource type " + resourceType); return Collections.emptyList(); } - final Set result = new HashSet(); - final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerEntry(resourceType); - final List> values = FileBasedIndex.getInstance() - .getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, GlobalSearchScope.moduleWithLibrariesScope(myModule)); - for (Set entrySet : values) { + final FileBasedIndex index = FileBasedIndex.getInstance(); + final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerEntry(resourceType); + final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject()); + + final Map> file2resourceSet = new HashMap>(); + + for (Set entrySet : index.getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, scope)) { for (ResourceEntry entry : entrySet) { - result.add(entry.getName()); + final Collection files = index.getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, entry, scope); + + for (VirtualFile file : files) { + Set resourcesInFile = file2resourceSet.get(file); + + if (resourcesInFile == null) { + resourcesInFile = new HashSet(); + file2resourceSet.put(file, resourcesInFile); + } + resourcesInFile.add(entry); + } + } + } + final Set result = new HashSet(); + final Set resourceFiles = getAllResourceFiles(); + + for (Map.Entry> entry : file2resourceSet.entrySet()) { + if (resourceFiles.contains(entry.getKey())) { + for (ResourceEntry resourceEntry : entry.getValue()) { + result.add(resourceEntry.getName()); + } } } return result; @@ -159,8 +181,7 @@ public class SystemResourceManager extends ResourceManager { final Collection files = FileBasedIndex.getInstance() .getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, new ResourceEntry(resourceType, resourceName), - GlobalSearchScope.moduleWithLibrariesScope( - myModule)); + GlobalSearchScope.allScope(myModule.getProject())); if (files.size() == 0) { return Collections.emptyList(); From 26b215f78670916fa0e6d63fe4f67918d1f7286c Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 31 Jan 2012 16:19:16 +0400 Subject: [PATCH 4/6] android: use index also to find local value resources and ids --- .../AndroidResourcesLineMarkerProvider.java | 2 +- .../LocalResourceManager.java | 57 ------ .../resourceManagers/ResourceManager.java | 176 ++++++++++++++-- .../SystemResourceManager.java | 190 ------------------ .../android/util/AndroidResourceUtil.java | 28 +-- 5 files changed, 166 insertions(+), 287 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java index 0a29088483b3..f2df753a4392 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java +++ b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java @@ -264,7 +264,7 @@ public class AndroidResourcesLineMarkerProvider implements LineMarkerProvider { if (resourceMap != null) { targets = new ArrayList(); if (resType.equals("id")) { - AndroidResourceUtil.collectIdDeclarations(fieldName, manager.getModule(), targets); + manager.collectIdDeclarations(fieldName, targets); } List resources = resourceMap.get(new MyResourceEntry(fieldName, resType)); if (resources != null) { diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java index 91ae8a93420c..5a7a1f8d265f 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java @@ -27,14 +27,10 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.xml.XmlFile; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashSet; -import com.intellij.util.indexing.FileBasedIndex; -import com.intellij.util.xml.GenericAttributeValue; import org.jetbrains.android.AndroidFileTemplateProvider; -import org.jetbrains.android.AndroidIdIndex; import org.jetbrains.android.actions.CreateResourceFileAction; import org.jetbrains.android.dom.attrs.AttributeDefinitions; import org.jetbrains.android.dom.resources.Attr; @@ -51,7 +47,6 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Set; @@ -109,21 +104,6 @@ public class LocalResourceManager extends ResourceManager { return AndroidRootUtil.getResourceOverlayDirs(getModule()); } - @NotNull - @Override - public Collection getValueResourceNames(@NotNull String resourceType) { - final List result = new ArrayList(); - - for (ResourceElement element : getValueResources(resourceType)) { - final String name = element.getName().getValue(); - - if (name != null) { - result.add(name); - } - } - return result; - } - @NotNull public List getValueResources(@NotNull final String resourceType) { return getValueResources(resourceType, null); @@ -178,43 +158,6 @@ public class LocalResourceManager extends ResourceManager { myAttrDefs = null; } - @Nullable - public List findIdDeclarations(@NotNull String id) { - List declarations = new ArrayList(); - AndroidResourceUtil.collectIdDeclarations(id, myModule, declarations); - return declarations; - } - - @NotNull - public Collection getIds() { - List result = new ArrayList(); - Project project = myModule.getProject(); - GlobalSearchScope scope = GlobalSearchScope.projectScope(myModule.getProject()); - for (String key : FileBasedIndex.getInstance().getAllKeys(AndroidIdIndex.INDEX_ID, project)) { - if (!AndroidIdIndex.MARKER.equals(key)) { - if (FileBasedIndex.getInstance().getValues(AndroidIdIndex.INDEX_ID, key, scope).size() > 0) { - result.add(key); - } - } - } - return result; - } - - @NotNull - @Override - public List findValueResources(@NotNull String resourceType, - @NotNull String resourceName, - boolean distinguishDelimetersInName) { - List elements = new ArrayList(); - for (ResourceElement element : getValueResources(resourceType)) { - GenericAttributeValue name = element.getName(); - if (name != null && equal(resourceName, name.getValue(), distinguishDelimetersInName)) { - elements.add(element); - } - } - return elements; - } - @NotNull public List findAttrs(@NotNull String name) { List list = new ArrayList(); diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java index 1d65b487c475..0c69c063e21c 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java @@ -15,8 +15,10 @@ */ package org.jetbrains.android.resourceManagers; +import com.android.resources.ResourceType; import com.android.sdklib.SdkConstants; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; @@ -24,14 +26,18 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; +import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.xml.DomElement; +import org.jetbrains.android.AndroidIdIndex; +import org.jetbrains.android.AndroidValueResourcesIndex; import org.jetbrains.android.dom.attrs.AttributeDefinitions; import org.jetbrains.android.dom.resources.Item; import org.jetbrains.android.dom.resources.ResourceElement; @@ -39,6 +45,7 @@ import org.jetbrains.android.dom.resources.Resources; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,6 +57,8 @@ import static java.util.Collections.addAll; * @author coyote */ public abstract class ResourceManager { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.resourceManagers.LocalResourceManager"); + public static final Set REFERABLE_RESOURCE_TYPES = new HashSet(); public static final String[] FILE_RESOURCE_TYPES = new String[]{"drawable", "anim", "layout", "values", "menu", "xml", "raw", "color"}; public static final String[] VALUE_RESOURCE_TYPES = @@ -178,7 +187,7 @@ public abstract class ResourceManager { private List getRootDomElements(@NotNull Class elementType, @Nullable Set files) { final List result = new ArrayList(); - for (VirtualFile file : getAllResourceFiles()) { + for (VirtualFile file : getAllValueResourceFiles()) { if ((files == null || files.contains(file)) && file.isValid()) { T element = AndroidUtils.loadDomElement(myModule, file, elementType); if (element != null) result.add(element); @@ -188,7 +197,7 @@ public abstract class ResourceManager { } @NotNull - protected Set getAllResourceFiles() { + protected Set getAllValueResourceFiles() { final Set files = new HashSet(); for (VirtualFile valueResourceDir : getResourceSubdirs("values")) { @@ -261,7 +270,48 @@ public abstract class ResourceManager { } @NotNull - public abstract Collection getValueResourceNames(@NotNull final String resourceType); + public Collection getValueResourceNames(@NotNull final String resourceType) { + final ResourceType type = ResourceType.getEnum(resourceType); + + if (type == null) { + LOG.error("Unknown resource type " + resourceType); + return Collections.emptyList(); + } + + final FileBasedIndex index = FileBasedIndex.getInstance(); + final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerEntry(resourceType); + final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject()); + + final Map> file2resourceSet = new HashMap>(); + + for (Set entrySet : index.getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, scope)) { + for (ResourceEntry entry : entrySet) { + final Collection files = index.getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, entry, scope); + + for (VirtualFile file : files) { + Set resourcesInFile = file2resourceSet.get(file); + + if (resourcesInFile == null) { + resourcesInFile = new HashSet(); + file2resourceSet.put(file, resourcesInFile); + } + resourcesInFile.add(entry); + } + } + } + final Set result = new HashSet(); + + for (VirtualFile file : getAllValueResourceFiles()) { + final Set entries = file2resourceSet.get(file); + + if (entries != null) { + for (ResourceEntry entry : entries) { + result.add(entry.getName()); + } + } + } + return result; + } @NotNull public static List getValueResourcesFromElement(@NotNull String resourceType, Resources resources) { @@ -306,19 +356,91 @@ public abstract class ResourceManager { // searches only declarations such as "@+id/..." @Nullable - public abstract List findIdDeclarations(@NotNull String id); + public List findIdDeclarations(@NotNull String id) { + List declarations = new ArrayList(); + collectIdDeclarations(id, declarations); + return declarations; + } @NotNull - public abstract Collection getIds(); + public Collection getIds() { + final Project project = myModule.getProject(); + final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject()); + + final FileBasedIndex index = FileBasedIndex.getInstance(); + final Map> file2ids = new HashMap>(); + + for (String key : index.getAllKeys(AndroidIdIndex.INDEX_ID, project)) { + if (!AndroidIdIndex.MARKER.equals(key)) { + if (index.getValues(AndroidIdIndex.INDEX_ID, key, scope).size() > 0) { + + for (VirtualFile file : index.getContainingFiles(AndroidIdIndex.INDEX_ID, key, scope)) { + Set ids = file2ids.get(file); + + if (ids == null) { + ids = new HashSet(); + file2ids.put(file, ids); + } + ids.add(key); + } + } + } + } + final Set result = new HashSet(); + + for (VirtualFile resSubdir : getResourceSubdirsToSearchIds()) { + for (VirtualFile resFile : resSubdir.getChildren()) { + final Set ids = file2ids.get(resFile); + + if (ids != null) { + result.addAll(ids); + } + } + } + return result; + } + + @NotNull + protected List getResourceSubdirsToSearchIds() { + final List resSubdirs = new ArrayList(); + resSubdirs.addAll(getResourceSubdirs(ResourceType.LAYOUT.getName())); + resSubdirs.addAll(getResourceSubdirs(ResourceType.MENU.getName())); + return resSubdirs; + } public List findValueResources(@NotNull String resType, @NotNull String resName) { return findValueResources(resType, resName, true); } @NotNull - public abstract List findValueResources(@NotNull String resourceType, - @NotNull String resourceName, - boolean distinguishDelimetersInName); + public List findValueResources(@NotNull String resourceType, + @NotNull String resourceName, + boolean distinguishDelimetersInName) { + final ResourceType type = ResourceType.getEnum(resourceType); + if (type == null) { + LOG.error("Unknown resource type " + resourceType); + return Collections.emptyList(); + } + + final Collection files = FileBasedIndex.getInstance() + .getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, new ResourceEntry(resourceType, resourceName), + GlobalSearchScope.allScope(myModule.getProject())); + + if (files.size() == 0) { + return Collections.emptyList(); + } + final Set fileSet = new HashSet(files); + final List result = new ArrayList(); + + for (ResourceElement element : getValueResources(resourceType, fileSet)) { + final String name = element.getName().getValue(); + + if (equal(resourceName, name, distinguishDelimetersInName)) { + result.add(element); + } + } + return result; + } public static boolean isInResourceSubdirectory(@NotNull PsiFile file, @Nullable String resourceType) { file = file.getOriginalFile(); @@ -376,4 +498,34 @@ public abstract class ResourceManager { private static boolean containsAndroidJar(@NotNull PsiDirectory psiDirectory) { return psiDirectory.findFile(SdkConstants.FN_FRAMEWORK_LIBRARY) != null; } + + public void collectIdDeclarations(@NotNull final String id, final List targets) { + final Collection files = + FileBasedIndex.getInstance().getContainingFiles(AndroidIdIndex.INDEX_ID, id, GlobalSearchScope.allScope(myModule.getProject())); + final Set fileSet = new HashSet(files); + final PsiManager psiManager = PsiManager.getInstance(myModule.getProject()); + + for (VirtualFile subdir : getResourceSubdirsToSearchIds()) { + for (VirtualFile file : subdir.getChildren()) { + if (fileSet.contains(file)) { + final PsiFile psiFile = psiManager.findFile(file); + + if (psiFile instanceof XmlFile) { + psiFile.accept(new XmlRecursiveElementVisitor() { + @Override + public void visitXmlAttributeValue(XmlAttributeValue attributeValue) { + if (AndroidResourceUtil.isIdDeclaration(attributeValue)) { + final String idInAttr = AndroidResourceUtil.getResourceNameByReferenceText(attributeValue.getValue()); + + if (id.equals(idInAttr)) { + targets.add(attributeValue); + } + } + } + }); + } + } + } + } + } } diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java index a0aa3d098be8..ac61347c3c8d 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java @@ -15,45 +15,22 @@ */ package org.jetbrains.android.resourceManagers; -import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.SdkConstants; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.SmartPointerManager; -import com.intellij.psi.SmartPsiElementPointer; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.xml.*; -import com.intellij.util.containers.HashMap; -import com.intellij.util.containers.HashSet; -import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.xml.ConvertContext; -import org.jetbrains.android.AndroidIdIndex; -import org.jetbrains.android.AndroidValueResourcesIndex; import org.jetbrains.android.dom.attrs.AttributeDefinitions; -import org.jetbrains.android.dom.resources.ResourceElement; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.sdk.AndroidTargetData; -import org.jetbrains.android.util.AndroidResourceUtil; -import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; - /** * @author coyote */ public class SystemResourceManager extends ResourceManager { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.resourceManagers.SystemResourceManager"); - - private volatile Map>> myIdMap; - private final AndroidPlatform myPlatform; public SystemResourceManager(@NotNull Module module, @NotNull AndroidPlatform androidPlatform) { @@ -73,50 +50,6 @@ public class SystemResourceManager extends ResourceManager { return LocalFileSystem.getInstance().findFileByPath(resPath); } - @NotNull - @Override - public Collection getValueResourceNames(@NotNull String resourceType) { - final ResourceType type = ResourceType.getEnum(resourceType); - - if (type == null) { - LOG.error("Unknown resource type " + resourceType); - return Collections.emptyList(); - } - - final FileBasedIndex index = FileBasedIndex.getInstance(); - final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerEntry(resourceType); - final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject()); - - final Map> file2resourceSet = new HashMap>(); - - for (Set entrySet : index.getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, scope)) { - for (ResourceEntry entry : entrySet) { - final Collection files = index.getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, entry, scope); - - for (VirtualFile file : files) { - Set resourcesInFile = file2resourceSet.get(file); - - if (resourcesInFile == null) { - resourcesInFile = new HashSet(); - file2resourceSet.put(file, resourcesInFile); - } - resourcesInFile.add(entry); - } - } - } - final Set result = new HashSet(); - final Set resourceFiles = getAllResourceFiles(); - - for (Map.Entry> entry : file2resourceSet.entrySet()) { - if (resourceFiles.contains(entry.getKey())) { - for (ResourceEntry resourceEntry : entry.getValue()) { - result.add(resourceEntry.getName()); - } - } - } - return result; - } - @Nullable public static SystemResourceManager getInstance(@NotNull ConvertContext context) { AndroidFacet facet = AndroidFacet.getInstance(context); @@ -128,127 +61,4 @@ public class SystemResourceManager extends ResourceManager { final AndroidTargetData targetData = myPlatform.getSdk().getTargetData(myPlatform.getTarget()); return targetData != null ? targetData.getAttrDefs(myModule.getProject()) : null; } - - @Nullable - public List findIdDeclarations(@NotNull String id) { - if (myIdMap == null) { - myIdMap = createIdMap(); - return doFindIdDeclarations(id, false); - } - return doFindIdDeclarations(id, true); - } - - private List doFindIdDeclarations(@NotNull String id, boolean recreateMapIfCannotResolve) { - final List> pointers = myIdMap.get(id); - - if (pointers == null || pointers.size() == 0) { - return Collections.emptyList(); - } - final List result = new ArrayList(); - - for (SmartPsiElementPointer pointer : pointers) { - final PsiElement element = pointer.getElement(); - - if (element != null) { - result.add(element); - } - else if (recreateMapIfCannotResolve) { - myIdMap = createIdMap(); - return doFindIdDeclarations(id, false); - } - } - return result; - } - - @NotNull - public Collection getIds() { - if (myIdMap == null) { - myIdMap = createIdMap(); - } - return myIdMap.keySet(); - } - - @NotNull - @Override - public List findValueResources(@NotNull String resourceType, - @NotNull String resourceName, - boolean distinguishDelimetersInName) { - final ResourceType type = ResourceType.getEnum(resourceType); - if (type == null) { - LOG.error("Unknown resource type " + resourceType); - return Collections.emptyList(); - } - - final Collection files = FileBasedIndex.getInstance() - .getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, new ResourceEntry(resourceType, resourceName), - GlobalSearchScope.allScope(myModule.getProject())); - - if (files.size() == 0) { - return Collections.emptyList(); - } - final Set fileSet = new HashSet(files); - final List result = new ArrayList(); - - for (ResourceElement element : getValueResources(resourceType, fileSet)) { - final String name = element.getName().getValue(); - - if (equal(resourceName, name, distinguishDelimetersInName)) { - result.add(element); - } - } - return result; - } - - @NotNull - public Map>> createIdMap() { - Map>> result = new HashMap>>(); - fillIdMap(result); - return result; - } - - protected void fillIdMap(@NotNull Map>> result) { - for (String resType : AndroidIdIndex.RES_TYPES_CONTAINING_ID_DECLARATIONS) { - List resFiles = findResourceFiles(resType); - for (PsiFile resFile : resFiles) { - collectIdDeclarations(resFile, result); - } - } - } - - protected static void collectIdDeclarations(PsiFile psiFile, Map>> result) { - if (psiFile instanceof XmlFile) { - XmlDocument document = ((XmlFile)psiFile).getDocument(); - if (document != null) { - XmlTag rootTag = document.getRootTag(); - if (rootTag != null) { - fillMapRecursively(rootTag, result); - } - } - } - } - - private static void fillMapRecursively(@NotNull XmlTag tag, @NotNull Map>> result) { - XmlAttribute idAttr = tag.getAttribute("id", SdkConstants.NS_RESOURCES); - if (idAttr != null) { - XmlAttributeValue idAttrValue = idAttr.getValueElement(); - if (idAttrValue != null) { - if (AndroidResourceUtil.isIdDeclaration(idAttrValue)) { - String id = AndroidResourceUtil.getResourceNameByReferenceText(idAttrValue.getValue()); - if (id != null) { - List> list = result.get(id); - - if (list == null) { - list = new ArrayList>(); - result.put(id, list); - } - final SmartPointerManager manager = SmartPointerManager.getInstance(tag.getProject()); - list.add(manager.createSmartPsiElementPointer(idAttr)); - } - } - } - } - for (XmlTag subtag : tag.getSubTags()) { - fillMapRecursively(subtag, result); - } - } } diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java index 8fe65fbb034b..553fb41e9c4a 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java @@ -24,18 +24,14 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtilBase; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; -import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; -import com.intellij.util.indexing.FileBasedIndex; -import org.jetbrains.android.AndroidIdIndex; import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.dom.resources.*; import org.jetbrains.android.facet.AndroidFacet; @@ -280,7 +276,7 @@ public class AndroidResourceUtil { if (type != null) { String name = field.getName(); if (type.equals("id")) { - collectIdDeclarations(name, manager.getModule(), targets); + manager.collectIdDeclarations(name, targets); } for (PsiFile file : manager.findResourceFiles(type, name, false)) { targets.add(file); @@ -368,28 +364,6 @@ public class AndroidResourceUtil { return null; } - public static void collectIdDeclarations(@NotNull final String id, Module module, final List targets) { - Collection files = - FileBasedIndex.getInstance().getContainingFiles(AndroidIdIndex.INDEX_ID, id, GlobalSearchScope.projectScope(module.getProject())); - PsiManager psiManager = PsiManager.getInstance(module.getProject()); - for (VirtualFile file : files) { - PsiFile psiFile = psiManager.findFile(file); - if (psiFile instanceof XmlFile) { - psiFile.accept(new XmlRecursiveElementVisitor() { - @Override - public void visitXmlAttributeValue(XmlAttributeValue attributeValue) { - if (isIdDeclaration(attributeValue)) { - String idInAttr = getResourceNameByReferenceText(attributeValue.getValue()); - if (id.equals(idInAttr)) { - targets.add(attributeValue); - } - } - } - }); - } - } - } - public static boolean isRJavaField(@NotNull PsiFile file, @NotNull PsiField field) { PsiClass aClass = field.getContainingClass(); if (aClass != null) { From ba6b93f15f34c2dcc208d65d7e7bcd71855b5167 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 31 Jan 2012 16:56:07 +0400 Subject: [PATCH 5/6] android id index: search for "> { public static final ID> INDEX_ID = ID.create("android.value.resources.index"); - + private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() { public boolean acceptInput(final VirtualFile file) { return (file.getFileSystem() == LocalFileSystem.getInstance() || file.getFileSystem() instanceof TempFileSystem) && @@ -41,6 +42,10 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension, FileContent>() { @NotNull public Map> map(FileContent inputData) { + + if (CharArrayUtil.indexOf(inputData.getContentAsText(), "> result = new HashMap>(); NanoXmlUtil.parse(new ByteArrayInputStream(inputData.getContent()), new NanoXmlUtil.IXMLBuilderAdapter() { From 126535522abbc17b17d7c62679c1853a70b6ead6 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 31 Jan 2012 17:22:12 +0400 Subject: [PATCH 6/6] fix compilation --- .../src/org/jetbrains/android/compiler/AndroidCompileUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index d5cb5c5d389e..858500668f22 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -58,6 +58,7 @@ import org.jetbrains.android.fileTypes.AndroidRenderscriptFileType; import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable;