do not suggest value local value resources
[idea/community.git] / plugins / android / src / org / jetbrains / android / resourceManagers / SystemResourceManager.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.android.resourceManagers;
17
18 import com.android.resources.ResourceType;
19 import com.android.sdklib.IAndroidTarget;
20 import com.android.sdklib.SdkConstants;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.module.Module;
23 import com.intellij.openapi.vfs.LocalFileSystem;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.psi.PsiElement;
26 import com.intellij.psi.PsiFile;
27 import com.intellij.psi.SmartPointerManager;
28 import com.intellij.psi.SmartPsiElementPointer;
29 import com.intellij.psi.search.GlobalSearchScope;
30 import com.intellij.psi.xml.*;
31 import com.intellij.util.containers.HashMap;
32 import com.intellij.util.containers.HashSet;
33 import com.intellij.util.indexing.FileBasedIndex;
34 import com.intellij.util.xml.ConvertContext;
35 import org.jetbrains.android.AndroidIdIndex;
36 import org.jetbrains.android.AndroidValueResourcesIndex;
37 import org.jetbrains.android.dom.attrs.AttributeDefinitions;
38 import org.jetbrains.android.dom.resources.ResourceElement;
39 import org.jetbrains.android.facet.AndroidFacet;
40 import org.jetbrains.android.sdk.AndroidPlatform;
41 import org.jetbrains.android.sdk.AndroidTargetData;
42 import org.jetbrains.android.util.AndroidResourceUtil;
43 import org.jetbrains.android.util.ResourceEntry;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46
47 import java.util.*;
48
49 /**
50  * @author coyote
51  */
52 public class SystemResourceManager extends ResourceManager {
53   private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.resourceManagers.SystemResourceManager");
54   
55   private volatile Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> myIdMap;
56   
57   private final AndroidPlatform myPlatform;
58
59   public SystemResourceManager(@NotNull Module module, @NotNull AndroidPlatform androidPlatform) {
60     super(module);
61     myPlatform = androidPlatform;
62   }
63
64   @NotNull
65   public VirtualFile[] getAllResourceDirs() {
66     VirtualFile resDir = getResourceDir();
67     return resDir != null ? new VirtualFile[]{resDir} : VirtualFile.EMPTY_ARRAY;
68   }
69
70   @Nullable
71   public VirtualFile getResourceDir() {
72     String resPath = myPlatform.getTarget().getPath(IAndroidTarget.RESOURCES);
73     return LocalFileSystem.getInstance().findFileByPath(resPath);
74   }
75
76   @NotNull
77   @Override
78   public Collection<String> getValueResourceNames(@NotNull String resourceType) {
79     final ResourceType type = ResourceType.getEnum(resourceType);
80     
81     if (type == null) {
82       LOG.error("Unknown resource type " + resourceType);
83       return Collections.emptyList();
84     }
85
86     final FileBasedIndex index = FileBasedIndex.getInstance();
87     final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerEntry(resourceType);
88     final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject());
89     
90     final Map<VirtualFile, Set<ResourceEntry>> file2resourceSet = new HashMap<VirtualFile, Set<ResourceEntry>>();
91
92     for (Set<ResourceEntry> entrySet : index.getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, scope)) {
93       for (ResourceEntry entry : entrySet) {
94         final Collection<VirtualFile> files = index.getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, entry, scope);
95
96         for (VirtualFile file : files) {
97           Set<ResourceEntry> resourcesInFile = file2resourceSet.get(file);
98           
99           if (resourcesInFile == null) {
100             resourcesInFile = new HashSet<ResourceEntry>();
101             file2resourceSet.put(file, resourcesInFile);
102           }
103           resourcesInFile.add(entry);
104         }
105       }
106     }
107     final Set<String> result = new HashSet<String>();
108     final Set<VirtualFile> resourceFiles = getAllResourceFiles();
109
110     for (Map.Entry<VirtualFile, Set<ResourceEntry>> entry : file2resourceSet.entrySet()) {
111       if (resourceFiles.contains(entry.getKey())) {
112         for (ResourceEntry resourceEntry : entry.getValue()) {
113           result.add(resourceEntry.getName());
114         }
115       }
116     }
117     return result;
118   }
119
120   @Nullable
121   public static SystemResourceManager getInstance(@NotNull ConvertContext context) {
122     AndroidFacet facet = AndroidFacet.getInstance(context);
123     return facet != null ? facet.getSystemResourceManager() : null;
124   }
125
126   @Nullable
127   public synchronized AttributeDefinitions getAttributeDefinitions() {
128     final AndroidTargetData targetData = myPlatform.getSdk().getTargetData(myPlatform.getTarget());
129     return targetData != null ? targetData.getAttrDefs(myModule.getProject()) : null;
130   }
131
132   @Nullable
133   public List<PsiElement> findIdDeclarations(@NotNull String id) {
134     if (myIdMap == null) {
135       myIdMap = createIdMap();
136       return doFindIdDeclarations(id, false);
137     }
138     return doFindIdDeclarations(id, true);
139   }
140
141   private List<PsiElement> doFindIdDeclarations(@NotNull String id, boolean recreateMapIfCannotResolve) {
142     final List<SmartPsiElementPointer<? extends PsiElement>> pointers = myIdMap.get(id);
143
144     if (pointers == null || pointers.size() == 0) {
145       return Collections.emptyList();
146     }
147     final List<PsiElement> result = new ArrayList<PsiElement>();
148
149     for (SmartPsiElementPointer<? extends PsiElement> pointer : pointers) {
150       final PsiElement element = pointer.getElement();
151       
152       if (element != null) {
153         result.add(element);
154       }
155       else if (recreateMapIfCannotResolve) {
156         myIdMap = createIdMap();
157         return doFindIdDeclarations(id, false);
158       }
159     }
160     return result;
161   }
162
163   @NotNull
164   public Collection<String> getIds() {
165     if (myIdMap == null) {
166       myIdMap = createIdMap();
167     }
168     return myIdMap.keySet();
169   }
170
171   @NotNull
172   @Override
173   public List<ResourceElement> findValueResources(@NotNull String resourceType,
174                                                   @NotNull String resourceName,
175                                                   boolean distinguishDelimetersInName) {
176     final ResourceType type = ResourceType.getEnum(resourceType);
177     if (type == null) {
178       LOG.error("Unknown resource type " + resourceType);
179       return Collections.emptyList();
180     }
181
182     final Collection<VirtualFile> files = FileBasedIndex.getInstance()
183       .getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, new ResourceEntry(resourceType, resourceName),
184                           GlobalSearchScope.allScope(myModule.getProject()));
185
186     if (files.size() == 0) {
187       return Collections.emptyList();
188     }
189     final Set<VirtualFile> fileSet = new HashSet<VirtualFile>(files);
190     final List<ResourceElement> result = new ArrayList<ResourceElement>();
191     
192     for (ResourceElement element : getValueResources(resourceType, fileSet)) {
193       final String name = element.getName().getValue();
194
195       if (equal(resourceName, name, distinguishDelimetersInName)) {
196         result.add(element);
197       }
198     }
199     return result;
200   }
201
202   @NotNull
203   public Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> createIdMap() {
204     Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> result = new HashMap<String, List<SmartPsiElementPointer<? extends PsiElement>>>();
205     fillIdMap(result);
206     return result;
207   }
208
209   protected void fillIdMap(@NotNull Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> result) {
210     for (String resType : AndroidIdIndex.RES_TYPES_CONTAINING_ID_DECLARATIONS) {
211       List<PsiFile> resFiles = findResourceFiles(resType);
212       for (PsiFile resFile : resFiles) {
213         collectIdDeclarations(resFile, result);
214       }
215     }
216   }
217
218   protected static void collectIdDeclarations(PsiFile psiFile, Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> result) {
219     if (psiFile instanceof XmlFile) {
220       XmlDocument document = ((XmlFile)psiFile).getDocument();
221       if (document != null) {
222         XmlTag rootTag = document.getRootTag();
223         if (rootTag != null) {
224           fillMapRecursively(rootTag, result);
225         }
226       }
227     }
228   }
229
230   private static void fillMapRecursively(@NotNull XmlTag tag, @NotNull Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> result) {
231     XmlAttribute idAttr = tag.getAttribute("id", SdkConstants.NS_RESOURCES);
232     if (idAttr != null) {
233       XmlAttributeValue idAttrValue = idAttr.getValueElement();
234       if (idAttrValue != null) {
235         if (AndroidResourceUtil.isIdDeclaration(idAttrValue)) {
236           String id = AndroidResourceUtil.getResourceNameByReferenceText(idAttrValue.getValue());
237           if (id != null) {
238             List<SmartPsiElementPointer<? extends PsiElement>> list = result.get(id);
239             
240             if (list == null) {
241               list = new ArrayList<SmartPsiElementPointer<? extends PsiElement>>();
242               result.put(id, list);
243             }
244             final SmartPointerManager manager = SmartPointerManager.getInstance(tag.getProject());
245             list.add(manager.createSmartPsiElementPointer(idAttr));
246           }
247         }
248       }
249     }
250     for (XmlTag subtag : tag.getSubTags()) {
251       fillMapRecursively(subtag, result);
252     }
253   }
254 }