diff --git a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java index a0c9497b5c38..021fd3e43888 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java +++ b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java @@ -24,6 +24,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; @@ -174,8 +175,9 @@ public class AndroidResourcesLineMarkerProvider implements LineMarkerProvider { private static Map> buildLocalResourceMap(@NotNull Project project, @NotNull final LocalResourceManager resManager) { final Map> result = new HashMap>(); - Collection resourceFiles = resManager.getResourceElements(); - for (Resources res : resourceFiles) { + List> resourceFiles = resManager.getResourceElements(); + for (Pair pair : resourceFiles) { + final Resources res = pair.getFirst(); for (ResourceType valueResourceType : AndroidResourceUtil.VALUE_RESOURCE_TYPES) { for (ResourceElement valueResource : AndroidResourceUtil.getValueResourcesFromElement(valueResourceType.getName(), res)) { addResource(valueResourceType.getName(), valueResource, result); diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index fec52eded011..8a469cede0f7 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -729,8 +729,9 @@ public class AndroidCompileUtil { } } - for (final Resources resources : manager.getResourceElements()) { - waitForSmartMode(project); + for (final Pair pair : manager.getResourceElements()) { + final Resources resources = pair.getFirst(); + waitForSmartMode(project); ApplicationManager.getApplication().runReadAction(new Runnable() { @Override diff --git a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java index 6947ffd6e52b..0b1e8f17b196 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java +++ b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java @@ -1,29 +1,44 @@ package org.jetbrains.android.compiler; +import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; import com.intellij.openapi.compiler.ValidityState; import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.ContentIterator; +import com.intellij.openapi.roots.impl.FileIndexImplUtil; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.containers.HashSet; +import com.intellij.openapi.vfs.VirtualFileFilter; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.HashMap; +import org.jetbrains.android.dom.resources.ResourceElement; +import org.jetbrains.android.dom.resources.Resources; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.resourceManagers.LocalResourceManager; +import org.jetbrains.android.resourceManagers.ResourceManager; import org.jetbrains.android.sdk.AndroidPlatform; +import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.util.Set; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * @author Eugene.Kudelevsky */ public class ResourceNamesValidityState implements ValidityState { - private final Set myResourcesSet = new HashSet(); + private final Map myResources = new HashMap(); + private final String myAndroidTargetHashString; private final long myManifestTimestamp; - + public ResourceNamesValidityState(@NotNull Module module) { final AndroidFacet facet = AndroidFacet.getInstance(module); assert facet != null; @@ -35,19 +50,83 @@ public class ResourceNamesValidityState implements ValidityState { final VirtualFile manifestFile = AndroidRootUtil.getManifestFile(facet); myManifestTimestamp = manifestFile != null ? manifestFile.getModificationStamp() : -1; - AndroidCompileUtil.collectAllResources(facet, myResourcesSet); + final LocalResourceManager manager = facet.getLocalResourceManager(); + + for (final Pair pair : manager.getResourceElements()) { + final Resources resources = pair.getFirst(); + final VirtualFile file = pair.getSecond(); + + for (final ResourceType resType : AndroidResourceUtil.VALUE_RESOURCE_TYPES) { + addValueResources(file, resType, AndroidResourceUtil.getValueResourcesFromElement(resType.getName(), resources), myResources); + } + addValueResources(file, ResourceType.ATTR, resources.getAttrs(), myResources); + addValueResources(file, ResourceType.DECLARE_STYLEABLE, resources.getDeclareStyleables(), myResources); + } + + for (final VirtualFile subdir : manager.getResourceSubdirs(null)) { + final String subdirName = subdir.getName(); + final int index = subdirName.indexOf('-'); + final String typeName = index >= 0 ? subdirName.substring(0, index) : subdirName; + final ResourceType type = ResourceType.getEnum(typeName); + final boolean idProvidingResource = type != null && ArrayUtil.find(ResourceManager.ID_PROVIDING_RESOURCE_TYPES, type) >= 0; + + FileIndexImplUtil.iterateRecursively(subdir, VirtualFileFilter.ALL, new ContentIterator() { + @Override + public boolean processFile(VirtualFile fileOrDir) { + if (!fileOrDir.isDirectory()) { + MyResourceFileData data = myResources.get(fileOrDir.getPath()); + if (data == null) { + data = new MyResourceFileData(); + myResources.put(fileOrDir.getPath(), data); + } + + if (idProvidingResource) { + data.setTimestamp(fileOrDir.getTimeStamp()); + } + } + return true; + } + }); + } } - + + private static void addValueResources(VirtualFile file, + ResourceType resType, + Collection resourceElements, + Map result) { + for (ResourceElement element : resourceElements) { + final String name = element.getName().getValue(); + + if (name != null) { + MyResourceFileData data = result.get(file.getPath()); + if (data == null) { + data = new MyResourceFileData(); + result.put(file.getPath(), data); + } + data.addValueResource(new ResourceEntry(resType.getName(), name)); + } + } + } + public ResourceNamesValidityState(@NotNull DataInput in) throws IOException { myAndroidTargetHashString = in.readUTF(); myManifestTimestamp = in.readLong(); - + final int resourcesCount = in.readInt(); - + for (int i = 0; i < resourcesCount; i++) { - final String type = in.readUTF(); - final String name = in.readUTF(); - myResourcesSet.add(new ResourceEntry(type, name)); + final String filePath = in.readUTF(); + + final int valueResourcesCount = in.readInt(); + final List valueResources = new ArrayList(valueResourcesCount); + + for (int j = 0; j < valueResourcesCount; j++) { + final String resType = in.readUTF(); + final String resName = in.readUTF(); + valueResources.add(new ResourceEntry(resType, resName)); + } + final long fileTimestamp = in.readLong(); + myResources.put(filePath, new MyResourceFileData(valueResources, fileTimestamp)); } } @@ -56,24 +135,86 @@ public class ResourceNamesValidityState implements ValidityState { if (!(otherState instanceof ResourceNamesValidityState)) { return false; } - + final ResourceNamesValidityState other = (ResourceNamesValidityState)otherState; return other.myAndroidTargetHashString.equals(myAndroidTargetHashString) && other.myManifestTimestamp == myManifestTimestamp && - other.myResourcesSet.equals(myResourcesSet); + other.myResources.equals(myResources); } @Override public void save(DataOutput out) throws IOException { out.writeUTF(myAndroidTargetHashString); out.writeLong(myManifestTimestamp); - - out.writeInt(myResourcesSet.size()); - - for (ResourceEntry resourceEntry : myResourcesSet) { - out.writeUTF(resourceEntry.getType()); - out.writeUTF(resourceEntry.getName()); + + out.writeInt(myResources.size()); + + for (Map.Entry entry : myResources.entrySet()) { + out.writeUTF(entry.getKey()); + + final MyResourceFileData data = entry.getValue(); + final List valueResources = data.getValueResources(); + out.writeInt(valueResources.size()); + + for (ResourceEntry resource : valueResources) { + out.writeUTF(resource.getType()); + out.writeUTF(resource.getName()); + } + out.writeLong(data.getTimestamp()); + } + } + + private static class MyResourceFileData { + // order matters because of id assigning in R.java + private final List myValueResources; + + private long myTimestamp; + + MyResourceFileData() { + this(new ArrayList(), 0); + } + + private MyResourceFileData(@NotNull List valueResources, long timestamp) { + myValueResources = valueResources; + myTimestamp = timestamp; + } + + @NotNull + List getValueResources() { + return myValueResources; + } + + long getTimestamp() { + return myTimestamp; + } + + public void setTimestamp(long timestamp) { + myTimestamp = timestamp; + } + + public void addValueResource(@NotNull ResourceEntry entry) { + myValueResources.add(entry); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MyResourceFileData data = (MyResourceFileData)o; + + if (myTimestamp != data.myTimestamp) return false; + if (!myValueResources.equals(data.myValueResources)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myValueResources.hashCode(); + result = 31 * result + (int)(myTimestamp ^ (myTimestamp >>> 32)); + return result; } } } diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java index 8ee3f3974bf7..16cd938f0d43 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java @@ -24,6 +24,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -96,7 +97,7 @@ public class LocalResourceManager extends ResourceManager { return AndroidRootUtil.getResourceDir(getFacet()); } - public List getResourceElements() { + public List> getResourceElements() { return getResourceElements(null); } @@ -200,7 +201,8 @@ public class LocalResourceManager extends ResourceManager { @NotNull public List findAttrs(@NotNull String name) { List list = new ArrayList(); - for (Resources res : getResourceElements()) { + for (Pair pair : getResourceElements()) { + final Resources res = pair.getFirst(); for (Attr attr : res.getAttrs()) { if (name.equals(attr.getName().getValue())) { list.add(attr); @@ -219,7 +221,8 @@ public class LocalResourceManager extends ResourceManager { public List findStyleables(@NotNull String name) { List list = new ArrayList(); - for (Resources res : getResourceElements()) { + for (Pair pair : getResourceElements()) { + final Resources res = pair.getFirst(); for (DeclareStyleable styleable : res.getDeclareStyleables()) { if (name.equals(styleable.getName().getValue())) { list.add(styleable); diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java index 0220bfeed1c7..dbdd4a6b5d9f 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java @@ -17,11 +17,11 @@ package org.jetbrains.android.resourceManagers; import com.android.resources.ResourceType; 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.util.Computable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -53,7 +53,9 @@ 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 ResourceType[] ID_PROVIDING_RESOURCE_TYPES = new ResourceType[] { + ResourceType.LAYOUT, ResourceType.MENU + }; protected final Module myModule; protected final AndroidFacet myFacet; @@ -130,17 +132,19 @@ public abstract class ResourceManager { return findResourceFiles(resType, null, true); } - protected List getResourceElements(@Nullable Set files) { + protected List> getResourceElements(@Nullable Set files) { return getRootDomElements(Resources.class, files); } - private List getRootDomElements(@NotNull Class elementType, - @Nullable Set files) { - final List result = new ArrayList(); + private List> getRootDomElements(@NotNull Class elementType, + @Nullable Set files) { + final List> result = new ArrayList>(); 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); + final T element = AndroidUtils.loadDomElement(myModule, file, elementType); + if (element != null) { + result.add(new Pair(element, file)); + } } } return result; @@ -162,8 +166,9 @@ public abstract class ResourceManager { protected List getValueResources(@NotNull final String resourceType, @Nullable Set files) { final List result = new ArrayList(); - Collection resourceFiles = getResourceElements(files); - for (final Resources resources : resourceFiles) { + List> resourceFiles = getResourceElements(files); + for (final Pair pair : resourceFiles) { + final Resources resources = pair.getFirst(); ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { @@ -337,10 +342,11 @@ public abstract class ResourceManager { } @NotNull - private List getResourceSubdirsToSearchIds() { + public List getResourceSubdirsToSearchIds() { final List resSubdirs = new ArrayList(); - resSubdirs.addAll(getResourceSubdirs(ResourceType.LAYOUT.getName())); - resSubdirs.addAll(getResourceSubdirs(ResourceType.MENU.getName())); + for (ResourceType type : ID_PROVIDING_RESOURCE_TYPES) { + resSubdirs.addAll(getResourceSubdirs(type.getName())); + } return resSubdirs; } diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java index 0ad958e47c7c..3fc28e1d7f66 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java @@ -31,6 +31,7 @@ import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; import com.intellij.codeInspection.htmlInspections.RequiredAttributesInspection; import com.intellij.lang.ASTNode; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; @@ -170,6 +171,12 @@ public class XmlTagInsertHandler implements InsertHandler { if (myAttrValueMarker == null) { return; } + + final UndoManager manager = UndoManager.getInstance(project); + if (manager.isUndoInProgress() || manager.isRedoInProgress()) { + return; + } + if (chooseAttributeName) { final int startOffset = myAttrValueMarker.getStartOffset(); final int endOffset = myAttrValueMarker.getEndOffset();