2 * Copyright 2000-2010 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.jetbrains.android.resourceManagers;
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;
52 public class SystemResourceManager extends ResourceManager {
53 private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.resourceManagers.SystemResourceManager");
55 private volatile Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> myIdMap;
57 private final AndroidPlatform myPlatform;
59 public SystemResourceManager(@NotNull Module module, @NotNull AndroidPlatform androidPlatform) {
61 myPlatform = androidPlatform;
65 public VirtualFile[] getAllResourceDirs() {
66 VirtualFile resDir = getResourceDir();
67 return resDir != null ? new VirtualFile[]{resDir} : VirtualFile.EMPTY_ARRAY;
71 public VirtualFile getResourceDir() {
72 String resPath = myPlatform.getTarget().getPath(IAndroidTarget.RESOURCES);
73 return LocalFileSystem.getInstance().findFileByPath(resPath);
78 public Collection<String> getValueResourceNames(@NotNull String resourceType) {
79 final ResourceType type = ResourceType.getEnum(resourceType);
82 LOG.error("Unknown resource type " + resourceType);
83 return Collections.emptyList();
86 final FileBasedIndex index = FileBasedIndex.getInstance();
87 final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerEntry(resourceType);
88 final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject());
90 final Map<VirtualFile, Set<ResourceEntry>> file2resourceSet = new HashMap<VirtualFile, Set<ResourceEntry>>();
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);
96 for (VirtualFile file : files) {
97 Set<ResourceEntry> resourcesInFile = file2resourceSet.get(file);
99 if (resourcesInFile == null) {
100 resourcesInFile = new HashSet<ResourceEntry>();
101 file2resourceSet.put(file, resourcesInFile);
103 resourcesInFile.add(entry);
107 final Set<String> result = new HashSet<String>();
108 final Set<VirtualFile> resourceFiles = getAllResourceFiles();
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());
121 public static SystemResourceManager getInstance(@NotNull ConvertContext context) {
122 AndroidFacet facet = AndroidFacet.getInstance(context);
123 return facet != null ? facet.getSystemResourceManager() : null;
127 public synchronized AttributeDefinitions getAttributeDefinitions() {
128 final AndroidTargetData targetData = myPlatform.getSdk().getTargetData(myPlatform.getTarget());
129 return targetData != null ? targetData.getAttrDefs(myModule.getProject()) : null;
133 public List<PsiElement> findIdDeclarations(@NotNull String id) {
134 if (myIdMap == null) {
135 myIdMap = createIdMap();
136 return doFindIdDeclarations(id, false);
138 return doFindIdDeclarations(id, true);
141 private List<PsiElement> doFindIdDeclarations(@NotNull String id, boolean recreateMapIfCannotResolve) {
142 final List<SmartPsiElementPointer<? extends PsiElement>> pointers = myIdMap.get(id);
144 if (pointers == null || pointers.size() == 0) {
145 return Collections.emptyList();
147 final List<PsiElement> result = new ArrayList<PsiElement>();
149 for (SmartPsiElementPointer<? extends PsiElement> pointer : pointers) {
150 final PsiElement element = pointer.getElement();
152 if (element != null) {
155 else if (recreateMapIfCannotResolve) {
156 myIdMap = createIdMap();
157 return doFindIdDeclarations(id, false);
164 public Collection<String> getIds() {
165 if (myIdMap == null) {
166 myIdMap = createIdMap();
168 return myIdMap.keySet();
173 public List<ResourceElement> findValueResources(@NotNull String resourceType,
174 @NotNull String resourceName,
175 boolean distinguishDelimetersInName) {
176 final ResourceType type = ResourceType.getEnum(resourceType);
178 LOG.error("Unknown resource type " + resourceType);
179 return Collections.emptyList();
182 final Collection<VirtualFile> files = FileBasedIndex.getInstance()
183 .getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, new ResourceEntry(resourceType, resourceName),
184 GlobalSearchScope.allScope(myModule.getProject()));
186 if (files.size() == 0) {
187 return Collections.emptyList();
189 final Set<VirtualFile> fileSet = new HashSet<VirtualFile>(files);
190 final List<ResourceElement> result = new ArrayList<ResourceElement>();
192 for (ResourceElement element : getValueResources(resourceType, fileSet)) {
193 final String name = element.getName().getValue();
195 if (equal(resourceName, name, distinguishDelimetersInName)) {
203 public Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> createIdMap() {
204 Map<String, List<SmartPsiElementPointer<? extends PsiElement>>> result = new HashMap<String, List<SmartPsiElementPointer<? extends PsiElement>>>();
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);
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);
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());
238 List<SmartPsiElementPointer<? extends PsiElement>> list = result.get(id);
241 list = new ArrayList<SmartPsiElementPointer<? extends PsiElement>>();
242 result.put(id, list);
244 final SmartPointerManager manager = SmartPointerManager.getInstance(tag.getProject());
245 list.add(manager.createSmartPsiElementPointer(idAttr));
250 for (XmlTag subtag : tag.getSubTags()) {
251 fillMapRecursively(subtag, result);