blob: ad01160c07a82391db87049ec21b4ded125210fb [file] [log] [blame]
/*
* 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.util;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.SdkConstants;
import com.intellij.ide.actions.CreateElementActionBase;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModulePackageIndex;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
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 org.jetbrains.android.AndroidFileTemplateProvider;
import org.jetbrains.android.dom.manifest.Manifest;
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.sdk.AndroidPlatform;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidResourceUtil {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.util.AndroidResourceUtil");
public static final String NEW_ID_PREFIX = "@+id/";
public static final Set<ResourceType> VALUE_RESOURCE_TYPES = EnumSet.of(ResourceType.DRAWABLE, ResourceType.COLOR, ResourceType.DIMEN,
ResourceType.STRING, ResourceType.STYLE, ResourceType.ARRAY,
ResourceType.PLURALS, ResourceType.ID, ResourceType.BOOL,
ResourceType.INTEGER);
public static final Set<ResourceType> ALL_VALUE_RESOURCE_TYPES = EnumSet.noneOf(ResourceType.class);
public static final Set<ResourceType> REFERRABLE_RESOURCE_TYPES = EnumSet.noneOf(ResourceType.class);
public static final Set<ResourceType> XML_FILE_RESOURCE_TYPES = EnumSet.of(ResourceType.ANIM, ResourceType.ANIMATOR,
ResourceType.INTERPOLATOR, ResourceType.LAYOUT,
ResourceType.MENU, ResourceType.XML, ResourceType.COLOR,
ResourceType.DRAWABLE);
static final String ROOT_TAG_PROPERTY = "ROOT_TAG";
static final String LAYOUT_WIDTH_PROPERTY = "LAYOUT_WIDTH";
static final String LAYOUT_HEIGHT_PROPERTY = "LAYOUT_HEIGHT";
private AndroidResourceUtil() {
}
@NotNull
public static String normalizeXmlResourceValue(@NotNull String value) {
return value.replace("'", "\\'").replace("\"", "\\\"");
}
static {
REFERRABLE_RESOURCE_TYPES.addAll(Arrays.asList(ResourceType.values()));
REFERRABLE_RESOURCE_TYPES.remove(ResourceType.ATTR);
REFERRABLE_RESOURCE_TYPES.remove(ResourceType.STYLEABLE);
ALL_VALUE_RESOURCE_TYPES.addAll(VALUE_RESOURCE_TYPES);
ALL_VALUE_RESOURCE_TYPES.add(ResourceType.ATTR);
ALL_VALUE_RESOURCE_TYPES.add(ResourceType.STYLEABLE);
}
@NotNull
public static PsiField[] findResourceFields(@NotNull AndroidFacet facet,
@NotNull String resClassName,
@NotNull String resourceName,
boolean onlyInOwnPackages) {
resourceName = getRJavaFieldName(resourceName);
final List<PsiJavaFile> rClassFiles = findRJavaFiles(facet, onlyInOwnPackages);
final List<PsiField> result = new ArrayList<PsiField>();
for (PsiJavaFile rClassFile : rClassFiles) {
if (rClassFile == null) {
continue;
}
final PsiClass rClass = findClass(rClassFile.getClasses(), AndroidUtils.R_CLASS_NAME);
if (rClass != null) {
final PsiClass resourceTypeClass = findClass(rClass.getInnerClasses(), resClassName);
if (resourceTypeClass != null) {
final PsiField field = resourceTypeClass.findFieldByName(resourceName, false);
if (field != null) {
result.add(field);
}
}
}
}
return result.toArray(new PsiField[result.size()]);
}
@NotNull
private static List<PsiJavaFile> findRJavaFiles(@NotNull AndroidFacet facet, boolean onlyInOwnPackages) {
final Module module = facet.getModule();
final Project project = module.getProject();
final Manifest manifest = facet.getManifest();
if (manifest == null) {
return Collections.emptyList();
}
final Set<PsiDirectory> dirs = new HashSet<PsiDirectory>();
collectDirsForPackage(module, project, null, dirs, new HashSet<Module>(), onlyInOwnPackages);
final List<PsiJavaFile> rJavaFiles = new ArrayList<PsiJavaFile>();
for (PsiDirectory dir : dirs) {
final VirtualFile file = dir.getVirtualFile().findChild(AndroidCommonUtils.R_JAVA_FILENAME);
if (file != null) {
final PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
if (psiFile instanceof PsiJavaFile) {
rJavaFiles.add((PsiJavaFile)psiFile);
}
}
}
return rJavaFiles;
}
private static void collectDirsForPackage(Module module,
final Project project,
@Nullable String packageName,
final Set<PsiDirectory> dirs,
Set<Module> visitedModules,
boolean onlyInOwnPackages) {
if (!visitedModules.add(module)) {
return;
}
if (packageName != null) {
ModulePackageIndex.getInstance(module).getDirsByPackageName(packageName, false).forEach(new Processor<VirtualFile>() {
public boolean process(final VirtualFile directory) {
final PsiDirectory psiDir = PsiManager.getInstance(project).findDirectory(directory);
if (psiDir != null) {
dirs.add(psiDir);
}
return true;
}
});
}
final AndroidFacet ownFacet = AndroidFacet.getInstance(module);
String ownPackageName = null;
if (ownFacet != null) {
final Manifest ownManifest = ownFacet.getManifest();
ownPackageName = ownManifest != null ? ownManifest.getPackage().getValue() : null;
if (ownPackageName != null && !ownPackageName.equals(packageName)) {
ModulePackageIndex.getInstance(module).getDirsByPackageName(ownPackageName, false).forEach(new Processor<VirtualFile>() {
public boolean process(final VirtualFile directory) {
final PsiDirectory psiDir = PsiManager.getInstance(project).findDirectory(directory);
if (psiDir != null) {
dirs.add(psiDir);
}
return true;
}
});
}
}
for (Module otherModule : ModuleManager.getInstance(project).getModules()) {
if (ModuleRootManager.getInstance(otherModule).isDependsOn(module)) {
collectDirsForPackage(otherModule, project, packageName != null || onlyInOwnPackages ? packageName : ownPackageName, dirs,
visitedModules, onlyInOwnPackages);
}
}
}
@Nullable
private static PsiClass findClass(@NotNull PsiClass[] classes, @NotNull String name) {
for (PsiClass c : classes) {
if (name.equals(c.getName())) {
return c;
}
}
return null;
}
@NotNull
public static PsiField[] findResourceFieldsForFileResource(@NotNull PsiFile file, boolean onlyInOwnPackages) {
final AndroidFacet facet = AndroidFacet.getInstance(file);
if (facet == null) {
return PsiField.EMPTY_ARRAY;
}
final String resourceType = facet.getLocalResourceManager().getFileResourceType(file);
if (resourceType == null) {
return PsiField.EMPTY_ARRAY;
}
final String resourceName = AndroidCommonUtils.getResourceName(resourceType, file.getName());
return findResourceFields(facet, resourceType, resourceName, onlyInOwnPackages);
}
@NotNull
public static PsiField[] findResourceFieldsForValueResource(XmlTag tag, boolean onlyInOwnPackages) {
final AndroidFacet facet = AndroidFacet.getInstance(tag);
if (facet == null) {
return PsiField.EMPTY_ARRAY;
}
String fileResType = facet.getLocalResourceManager().getFileResourceType(tag.getContainingFile());
final String resourceType = "values".equals(fileResType)
? getResourceTypeByValueResourceTag(tag)
: null;
if (resourceType == null) {
return PsiField.EMPTY_ARRAY;
}
final String name = tag.getAttributeValue("name");
if (name == null) {
return PsiField.EMPTY_ARRAY;
}
return findResourceFields(facet, resourceType, name, onlyInOwnPackages);
}
@NotNull
public static String getRJavaFieldName(@NotNull String resourceName) {
final String[] identifiers = resourceName.split("\\.");
final StringBuilder result = new StringBuilder();
for (int i = 0, n = identifiers.length; i < n; i++) {
result.append(identifiers[i]);
if (i < n - 1) {
result.append('_');
}
}
return result.toString();
}
public static boolean isCorrectAndroidResourceName(@NotNull String resourceName) {
if (resourceName.length() == 0) {
return false;
}
if (resourceName.startsWith(".") || resourceName.endsWith(".")) {
return false;
}
final String[] identifiers = resourceName.split("\\.");
for (String identifier : identifiers) {
if (!StringUtil.isJavaIdentifier(identifier)) {
return false;
}
}
return true;
}
@Nullable
public static String getResourceTypeByValueResourceTag(@NotNull XmlTag tag) {
String resClassName = tag.getName();
resClassName = resClassName.equals("item")
? tag.getAttributeValue("type", null)
: AndroidCommonUtils.getResourceTypeByTagName(resClassName);
if (resClassName != null) {
final String resourceName = tag.getAttributeValue("name");
return resourceName != null ? resClassName : null;
}
return null;
}
@Nullable
public static String getResourceClassName(@NotNull PsiField field) {
final PsiClass resourceClass = field.getContainingClass();
if (resourceClass != null) {
final PsiClass parentClass = resourceClass.getContainingClass();
if (parentClass != null &&
AndroidUtils.R_CLASS_NAME.equals(parentClass.getName()) &&
parentClass.getContainingClass() == null) {
return resourceClass.getName();
}
}
return null;
}
// result contains XmlAttributeValue or PsiFile
@NotNull
public static List<PsiElement> findResourcesByField(@NotNull PsiField field) {
final AndroidFacet facet = AndroidFacet.getInstance(field);
return facet != null
? facet.getLocalResourceManager().findResourcesByField(field)
: Collections.<PsiElement>emptyList();
}
public static boolean isResourceField(@NotNull PsiField field) {
PsiClass c = field.getContainingClass();
if (c == null) return false;
c = c.getContainingClass();
if (c != null && AndroidUtils.R_CLASS_NAME.equals(c.getName())) {
AndroidFacet facet = AndroidFacet.getInstance(field);
if (facet != null) {
PsiFile file = c.getContainingFile();
if (file != null && isRJavaFile(facet, file)) {
return true;
}
}
}
return false;
}
@NotNull
public static PsiField[] findIdFields(@NotNull XmlAttributeValue value) {
if (value.getParent() instanceof XmlAttribute) {
return findIdFields((XmlAttribute)value.getParent());
}
return PsiField.EMPTY_ARRAY;
}
public static boolean isIdDeclaration(@Nullable String attrValue) {
return attrValue != null && attrValue.startsWith(NEW_ID_PREFIX);
}
public static boolean isIdReference(@Nullable String attrValue) {
return attrValue != null && attrValue.startsWith("@id/");
}
public static boolean isIdDeclaration(@NotNull XmlAttributeValue value) {
return isIdDeclaration(value.getValue());
}
@NotNull
public static PsiField[] findIdFields(@NotNull XmlAttribute attribute) {
final XmlAttributeValue value = attribute.getValueElement();
if (value != null && isIdDeclaration(value)) {
final String id = getResourceNameByReferenceText(attribute.getValue());
if (id != null) {
final AndroidFacet facet = AndroidFacet.getInstance(attribute);
if (facet != null) {
return findResourceFields(facet, ResourceType.ID.getName(), id, false);
}
}
}
return PsiField.EMPTY_ARRAY;
}
@Nullable
public static String getResourceNameByReferenceText(@NotNull String text) {
int i = text.indexOf('/');
if (i < text.length() - 1) {
return text.substring(i + 1, text.length());
}
return null;
}
@NotNull
public static ResourceElement addValueResource(@NotNull final ResourceType resType, @NotNull final Resources resources) {
switch (resType) {
case STRING:
return resources.addString();
case PLURALS:
return resources.addPlurals();
case DIMEN:
return resources.addDimen();
case COLOR:
return resources.addColor();
case DRAWABLE:
return resources.addDrawable();
case STYLE:
return resources.addStyle();
case ARRAY:
// todo: choose among string-array, integer-array and array
return resources.addStringArray();
case INTEGER:
return resources.addInteger();
case BOOL:
return resources.addBool();
case ID:
final Item item = resources.addItem();
item.getType().setValue(ResourceType.ID.getName());
return item;
case ATTR:
return resources.addAttr();
case STYLEABLE:
return resources.addDeclareStyleable();
default:
throw new IllegalArgumentException("Incorrect resource type");
}
}
@NotNull
public static List<VirtualFile> getResourceSubdirs(@Nullable String resourceType, @NotNull VirtualFile[] resourceDirs) {
if (resourceType != null && ResourceFolderType.getTypeByName(resourceType) == null) {
return Collections.emptyList();
}
final List<VirtualFile> dirs = new ArrayList<VirtualFile>();
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 = AndroidCommonUtils.getResourceTypeByDirName(child.getName());
if (resourceType.equals(type)) dirs.add(child);
}
}
}
return dirs;
}
@Nullable
public static String getDefaultResourceFileName(@NotNull ResourceType type) {
if (ResourceType.PLURALS == type) {
return "strings.xml";
}
if (VALUE_RESOURCE_TYPES.contains(type)) {
return type.getName() + "s.xml";
}
if (ResourceType.ATTR == type ||
ResourceType.STYLEABLE == type) {
return "attrs.xml";
}
return null;
}
@NotNull
public static List<ResourceElement> getValueResourcesFromElement(@NotNull String resourceType, @NotNull Resources resources) {
final List<ResourceElement> result = new ArrayList<ResourceElement>();
if (resourceType.equals(ResourceType.STRING.getName())) {
result.addAll(resources.getStrings());
}
else if (resourceType.equals(ResourceType.PLURALS.getName())) {
result.addAll(resources.getPluralss());
}
else if (resourceType.equals(ResourceType.DRAWABLE.getName())) {
result.addAll(resources.getDrawables());
}
else if (resourceType.equals(ResourceType.COLOR.getName())) {
result.addAll(resources.getColors());
}
else if (resourceType.equals(ResourceType.DIMEN.getName())) {
result.addAll(resources.getDimens());
}
else if (resourceType.equals(ResourceType.STYLE.getName())) {
result.addAll(resources.getStyles());
}
else if (resourceType.equals(ResourceType.ARRAY.getName())) {
result.addAll(resources.getStringArrays());
result.addAll(resources.getIntegerArrays());
result.addAll(resources.getArrays());
}
else if (resourceType.equals(ResourceType.INTEGER.getName())) {
result.addAll(resources.getIntegers());
}
else if (resourceType.equals(ResourceType.BOOL.getName())) {
result.addAll(resources.getBools());
}
for (Item item : resources.getItems()) {
String type = item.getType().getValue();
if (resourceType.equals(type)) {
result.add(item);
}
}
return result;
}
public static boolean isInResourceSubdirectory(@NotNull PsiFile file, @Nullable String resourceType) {
file = file.getOriginalFile();
PsiDirectory dir = file.getContainingDirectory();
if (dir == null) return false;
return isResourceSubdirectory(dir, resourceType);
}
public static boolean isResourceSubdirectory(@NotNull PsiDirectory directory, @Nullable String resourceType) {
PsiDirectory dir = directory;
final String dirName = dir.getName();
if (resourceType != null && !dirName.equals(resourceType) && !dirName.startsWith(resourceType + '-')) {
return false;
}
dir = dir.getParent();
if (dir == null) {
return false;
}
if ("default".equals(dir.getName())) {
dir = dir.getParentDirectory();
}
return dir != null && isResourceDirectory(dir);
}
public static boolean isLocalResourceDirectory(@NotNull VirtualFile dir, @NotNull Project project) {
final Module module = ModuleUtil.findModuleForFile(dir, project);
if (module != null) {
final AndroidFacet facet = AndroidFacet.getInstance(module);
return facet != null && facet.getLocalResourceManager().isResourceDir(dir);
}
return false;
}
public static boolean isResourceDirectory(@NotNull PsiDirectory directory) {
PsiDirectory dir = directory;
// check facet settings
VirtualFile vf = dir.getVirtualFile();
if (isLocalResourceDirectory(vf, dir.getProject())) {
return true;
}
// method can be invoked for system resource dir, so we should check it
if (!SdkConstants.FD_RES.equals(dir.getName())) return false;
dir = dir.getParent();
if (dir != null) {
if (dir.findFile(SdkConstants.FN_ANDROID_MANIFEST_XML) != null) {
return true;
}
dir = dir.getParent();
if (dir != null) {
if (containsAndroidJar(dir)) return true;
dir = dir.getParent();
if (dir != null) {
return containsAndroidJar(dir);
}
}
}
return false;
}
private static boolean containsAndroidJar(@NotNull PsiDirectory psiDirectory) {
return psiDirectory.findFile(SdkConstants.FN_FRAMEWORK_LIBRARY) != null;
}
public static boolean isRJavaFile(@NotNull AndroidFacet facet, @NotNull PsiFile file) {
if (file.getName().equals(AndroidCommonUtils.R_JAVA_FILENAME) && file instanceof PsiJavaFile) {
final PsiJavaFile javaFile = (PsiJavaFile)file;
final Manifest manifest = facet.getManifest();
if (manifest == null) {
return false;
}
final String manifestPackage = manifest.getPackage().getValue();
if (manifestPackage != null && javaFile.getPackageName().equals(manifestPackage)) {
return true;
}
for (String aPackage : AndroidUtils.getDepLibsPackages(facet.getModule())) {
if (javaFile.getPackageName().equals(aPackage)) {
return true;
}
}
}
return false;
}
public static boolean isManifestJavaFile(@NotNull AndroidFacet facet, @NotNull PsiFile file) {
if (file.getName().equals(AndroidCommonUtils.MANIFEST_JAVA_FILE_NAME) && file instanceof PsiJavaFile) {
final Manifest manifest = facet.getManifest();
final PsiJavaFile javaFile = (PsiJavaFile)file;
return manifest != null && javaFile.getPackageName().equals(manifest.getPackage().getValue());
}
return false;
}
public static List<String> getNames(@NotNull Collection<ResourceType> resourceTypes) {
if (resourceTypes.size() == 0) {
return Collections.emptyList();
}
final List<String> result = new ArrayList<String>();
for (ResourceType type : resourceTypes) {
result.add(type.getName());
}
return result;
}
@NotNull
public static String[] getNamesArray(@NotNull Collection<ResourceType> resourceTypes) {
final List<String> names = getNames(resourceTypes);
return ArrayUtil.toStringArray(names);
}
public static boolean createValueResource(@NotNull Module module,
@NotNull String resourceName,
@NotNull final ResourceType resourceType,
@NotNull String fileName,
@NotNull List<String> dirNames,
@NotNull Processor<ResourceElement> afterAddedProcessor) {
final Project project = module.getProject();
final AndroidFacet facet = AndroidFacet.getInstance(module);
assert facet != null;
try {
return addValueResource(facet, resourceName, resourceType, fileName, dirNames, afterAddedProcessor);
}
catch (Exception e) {
final String message = CreateElementActionBase.filterMessage(e.getMessage());
if (message == null || message.length() == 0) {
LOG.error(e);
}
else {
LOG.info(e);
AndroidUtils.reportError(project, message);
}
return false;
}
}
public static boolean createValueResource(@NotNull Module module,
@NotNull String resourceName,
@NotNull final ResourceType resourceType,
@NotNull String fileName,
@NotNull List<String> dirNames,
@NotNull final String value) {
return createValueResource(module, resourceName, resourceType, fileName, dirNames, new Processor<ResourceElement>() {
@Override
public boolean process(ResourceElement element) {
if (value.length() > 0) {
final String s = resourceType == ResourceType.STRING
? normalizeXmlResourceValue(value)
: value;
element.setStringValue(s);
}
else if (resourceType == ResourceType.STYLEABLE ||
resourceType == ResourceType.STYLE) {
element.setStringValue("value");
element.getXmlTag().getValue().setText("");
}
return true;
}
});
}
private static boolean addValueResource(@NotNull AndroidFacet facet,
@NotNull final String resourceName,
@NotNull final ResourceType resourceType,
@NotNull String fileName,
@NotNull List<String> dirNames,
@NotNull final Processor<ResourceElement> afterAddedProcessor) throws Exception {
if (dirNames.size() == 0) {
return false;
}
final VirtualFile[] resFiles = new VirtualFile[dirNames.size()];
for (int i = 0, n = dirNames.size(); i < n; i++) {
final VirtualFile resFile = findOrCreateResourceFile(facet, fileName, dirNames.get(i));
if (resFile == null) {
return false;
}
resFiles[i] = resFile;
}
if (!ReadonlyStatusHandler.ensureFilesWritable(facet.getModule().getProject(), resFiles)) {
return false;
}
final Resources[] resourcesElements = new Resources[resFiles.length];
for (int i = 0; i < resFiles.length; i++) {
final Resources resources = AndroidUtils.loadDomElement(facet.getModule(), resFiles[i], Resources.class);
if (resources == null) {
AndroidUtils.reportError(facet.getModule().getProject(), AndroidBundle.message("not.resource.file.error", fileName));
return false;
}
resourcesElements[i] = resources;
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
for (Resources resources : resourcesElements) {
final ResourceElement element = addValueResource(resourceType, resources);
element.getName().setValue(resourceName);
afterAddedProcessor.process(element);
}
}
});
return true;
}
@Nullable
private static VirtualFile findOrCreateResourceFile(@NotNull AndroidFacet facet,
@NotNull final String fileName,
@NotNull String dirName) throws Exception {
final Module module = facet.getModule();
final Project project = module.getProject();
final VirtualFile resDir = facet.getLocalResourceManager().getResourceDir();
if (resDir == null) {
AndroidUtils.reportError(project, AndroidBundle.message("check.resource.dir.error", module.getName()));
return null;
}
final VirtualFile dir = AndroidUtils.createChildDirectoryIfNotExist(project, resDir, dirName);
final String dirPath = FileUtil.toSystemDependentName(resDir.getPath() + '/' + dirName);
if (dir == null) {
AndroidUtils.reportError(project, AndroidBundle.message("android.cannot.create.dir.error", dirPath));
return null;
}
final VirtualFile file = dir.findChild(fileName);
if (file != null) {
return file;
}
AndroidFileTemplateProvider
.createFromTemplate(project, dir, AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE, fileName);
final VirtualFile result = dir.findChild(fileName);
if (result == null) {
AndroidUtils.reportError(project, AndroidBundle.message("android.cannot.create.file.error", dirPath + File.separatorChar + fileName));
}
return result;
}
@Nullable
public static Pair<String, String> getReferredResourceField(@NotNull AndroidFacet facet, @NotNull PsiReferenceExpression exp) {
final String resFieldName = exp.getReferenceName();
if (resFieldName == null || resFieldName.length() == 0) {
return null;
}
PsiExpression qExp = exp.getQualifierExpression();
if (!(qExp instanceof PsiReferenceExpression)) {
return null;
}
final PsiReferenceExpression resClassReference = (PsiReferenceExpression)qExp;
final String resClassName = resClassReference.getReferenceName();
if (resClassName == null || resClassName.length() == 0) {
return null;
}
qExp = resClassReference.getQualifierExpression();
if (!(qExp instanceof PsiReferenceExpression)) {
return null;
}
final PsiElement resolvedElement = ((PsiReferenceExpression)qExp).resolve();
if (!(resolvedElement instanceof PsiClass) ||
!AndroidUtils.R_CLASS_NAME.equals(((PsiClass)resolvedElement).getName())) {
return null;
}
final PsiFile containingFile = resolvedElement.getContainingFile();
if (containingFile == null || !isRJavaFile(facet, containingFile)) {
return null;
}
return new Pair<String, String>(resClassName, resFieldName);
}
@NotNull
public static XmlFile createFileResource(@NotNull String fileName,
@NotNull PsiDirectory resSubdir,
@NotNull String rootTagName,
@NotNull String resourceType,
boolean valuesResourceFile) throws Exception {
FileTemplateManager manager = FileTemplateManager.getInstance();
String templateName = getTemplateName(resourceType, valuesResourceFile, rootTagName);
FileTemplate template = manager.getJ2eeTemplate(templateName);
Properties properties = new Properties();
if (!valuesResourceFile) {
properties.setProperty(ROOT_TAG_PROPERTY, rootTagName);
}
if (ResourceType.LAYOUT.getName().equals(resourceType)) {
final Module module = ModuleUtilCore.findModuleForPsiElement(resSubdir);
final AndroidPlatform platform = module != null ? AndroidPlatform.getInstance(module) : null;
final int apiLevel = platform != null ? platform.getApiLevel() : -1;
final String value = apiLevel == -1 || apiLevel >= 8
? "match_parent" : "fill_parent";
properties.setProperty(LAYOUT_WIDTH_PROPERTY, value);
properties.setProperty(LAYOUT_HEIGHT_PROPERTY, value);
}
PsiElement createdElement = FileTemplateUtil.createFromTemplate(template, fileName, properties, resSubdir);
assert createdElement instanceof XmlFile;
return (XmlFile)createdElement;
}
private static String getTemplateName(String resourceType, boolean valuesResourceFile, String rootTagName) {
if (valuesResourceFile) {
return AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE;
}
if (ResourceType.LAYOUT.getName().equals(resourceType)) {
return AndroidUtils.TAG_LINEAR_LAYOUT.equals(rootTagName)
? AndroidFileTemplateProvider.LAYOUT_RESOURCE_VERTICAL_FILE_TEMPLATE
: AndroidFileTemplateProvider.LAYOUT_RESOURCE_FILE_TEMPLATE;
}
return AndroidFileTemplateProvider.RESOURCE_FILE_TEMPLATE;
}
@NotNull
public static String getFieldNameByResourceName(@NotNull String fieldName) {
return fieldName.replace('.', '_').replace('-', '_').replace(':', '_');
}
@Nullable
public static String getInvalidResourceFileNameMessage(@NotNull String fileName) {
for (int i = 0, n = fileName.length(); i < n; i++) {
char c = fileName.charAt(i);
if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '.')) {
return AndroidBundle.message("invalid.file.resource.name.error");
}
}
if (!StringUtil.isJavaIdentifier(getFieldNameByResourceName(fileName))) {
return AndroidBundle.message("invalid.file.resource.name.error1");
}
return null;
}
}