/* * Copyright 2000-2010 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.android; import com.android.SdkConstants; import com.android.resources.ResourceType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.psi.xml.XmlFile; import com.intellij.xml.XmlSchemaProvider; import gnu.trove.THashMap; import org.jetbrains.android.dom.AndroidDomExtender; import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.dom.manifest.ManifestDomFileDescription; import org.jetbrains.android.dom.xml.XmlResourceDomFileDescription; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.net.URL; import java.util.HashSet; import java.util.Map; import java.util.Set; import static com.android.SdkConstants.*; /** * Created by IntelliJ IDEA. * User: Eugene.Kudelevsky * Date: Aug 12, 2009 * Time: 6:49:18 PM * To change this template use File | Settings | File Templates. */ public class AndroidXmlSchemaProvider extends XmlSchemaProvider { private static final Key<Map<String, CachedValue<XmlFile>>> DESCRIPTORS_MAP_IN_MODULE = Key.create("ANDROID_DESCRIPTORS_MAP_IN_MODULE"); @Override public XmlFile getSchema(@NotNull @NonNls String url, @Nullable final Module module, @NotNull PsiFile baseFile) { if (module == null) return null; assert AndroidFacet.getInstance(module) != null; Map<String, CachedValue<XmlFile>> descriptors = module.getUserData(DESCRIPTORS_MAP_IN_MODULE); if (descriptors == null) { descriptors = new THashMap<String, CachedValue<XmlFile>>(); module.putUserData(DESCRIPTORS_MAP_IN_MODULE, descriptors); } CachedValue<XmlFile> reference = descriptors.get(url); if (reference != null) { return reference.getValue(); } CachedValuesManager manager = CachedValuesManager.getManager(module.getProject()); reference = manager.createCachedValue(new CachedValueProvider<XmlFile>() { @Override public Result<XmlFile> compute() { final URL resource = AndroidXmlSchemaProvider.class.getResource("android.xsd"); final VirtualFile fileByURL = VfsUtil.findFileByURL(resource); XmlFile result = (XmlFile)PsiManager.getInstance(module.getProject()).findFile(fileByURL).copy(); return new Result<XmlFile>(result, PsiModificationTracker.MODIFICATION_COUNT); } }, false); descriptors.put(url, reference); return reference.getValue(); } @Override public boolean isAvailable(@NotNull final XmlFile file) { final PsiFile f = file.getOriginalFile(); if (!(f instanceof XmlFile)) { return false; } final XmlFile originalFile = (XmlFile)f; return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { @Override public Boolean compute() { if (isXmlResourceFile(originalFile) || ManifestDomFileDescription.isManifestFile(originalFile)) { return AndroidFacet.getInstance(originalFile) != null; } return false; } }); } private static boolean isXmlResourceFile(XmlFile file) { if (!AndroidResourceUtil.isInResourceSubdirectory(file, null)) { return false; } final PsiDirectory parent = file.getParent(); if (parent == null) { return false; } final String resType = AndroidCommonUtils.getResourceTypeByDirName(parent.getName()); if (resType == null) { return false; } if (resType.equals(ResourceType.XML.getName())) { return XmlResourceDomFileDescription.isXmlResourceFile(file); } return !resType.equals("raw"); } @NotNull @Override public Set<String> getAvailableNamespaces(@NotNull XmlFile file, @Nullable String tagName) { Set<String> result = new HashSet<String>(); AndroidFacet facet = AndroidFacet.getInstance(file); if (facet != null) { result.add(SdkConstants.NS_RESOURCES); String localNs = getLocalXmlNamespace(facet); if (localNs != null) { result.add(localNs); } result.add(SdkConstants.TOOLS_URI); } return result; } @Override public String getDefaultPrefix(@NotNull @NonNls String namespace, @NotNull XmlFile context) { if (ANDROID_URI.equals(namespace)) { return ANDROID_NS_NAME; } else if (namespace.equals(TOOLS_URI)) { return TOOLS_PREFIX; } else if (namespace.equals(AUTO_URI) || namespace.startsWith(URI_PREFIX)) { return APP_PREFIX; } return null; } @Nullable public static String getLocalXmlNamespace(@NotNull AndroidFacet facet) { if (facet.isLibraryProject() || facet.isGradleProject()) { return SdkConstants.AUTO_URI; } final Manifest manifest = facet.getManifest(); if (manifest != null) { String aPackage = ApplicationManager.getApplication().runReadAction(new Computable<String>() { @Override @Nullable public String compute() { return manifest.getPackage().getValue(); } }); if (aPackage != null && aPackage.length() != 0) { return SdkConstants.URI_PREFIX + aPackage; } } return null; } }