From 7c7ba0359e0d2137e7540e72f2242f3fd0debec5 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 18 Jun 2012 14:56:47 +0400 Subject: [PATCH] augment of R and Manifest classes, do not generate the constants anymore --- plugins/android/src/META-INF/plugin.xml | 1 + .../android/augment/AndroidLightClass.java | 338 ++++++++++++++++++ .../android/augment/AndroidLightField.java | 28 ++ .../augment/AndroidPsiAugmentProvider.java | 61 ++++ .../android/augment/ManifestInnerClass.java | 64 ++++ .../android/augment/PermissionClass.java | 41 +++ .../android/augment/PermissionGroupClass.java | 41 +++ .../android/augment/ResourceTypeClass.java | 57 +++ .../compiler/AndroidAutogenerator.java | 83 +++-- .../android/compiler/AndroidCompileUtil.java | 113 +----- .../ResourceReferenceConverter.java | 20 +- .../jetbrains/android/facet/AndroidFacet.java | 8 - .../android/facet/AndroidFacetEditorTab.java | 13 +- .../facet/AndroidResourceFilesListener.java | 32 +- .../resourceManagers/ResourceManager.java | 11 + .../android/util/AndroidResourceUtil.java | 14 + .../jetbrains/android/util/AndroidUtils.java | 1 + plugins/android/testData/dom/R.java | 8 - .../testData/dom/layout/JavaCompletion1.java | 7 + .../dom/layout/JavaCompletion1_after.java | 7 + .../testData/dom/layout/JavaCompletion2.java | 7 + .../dom/layout/JavaCompletion2_after.java | 7 + .../testData/dom/layout/JavaCompletion3.java | 7 + .../dom/layout/JavaCompletion3_after.java | 7 + .../dom/layout/JavaHighlighting1.java | 7 + .../dom/layout/JavaHighlighting2.java | 7 + .../dom/layout/JavaHighlighting3.java | 7 + .../dom/layout/JavaHighlighting4.java | 7 + .../dom/layout/JavaHighlighting5.java | 7 + .../testData/dom/layout/JavaIdCompletion.java | 7 + .../dom/layout/JavaIdCompletion_after.java | 7 + plugins/android/testData/dom/layout/main.xml | 3 + .../dom/manifest/JavaHighlighting.java | 17 + .../testData/dom/manifest/Manifest.java | 4 + .../dom/manifest/PermissionsManifest.xml | 10 + .../dom/resources/JavaCompletion1.java | 7 + .../dom/resources/JavaCompletion1_after.java | 7 + .../dom/resources/JavaCompletion2.java | 7 + .../dom/resources/JavaCompletion2_after.java | 7 + .../dom/resources/JavaCompletion3.java | 7 + .../dom/resources/JavaCompletion3_after.java | 7 + .../dom/resources/JavaCompletion4.java | 7 + .../dom/resources/JavaCompletion4_after.java | 7 + .../dom/resources/JavaHighlighting1.java | 7 + .../dom/resources/JavaHighlighting2.java | 7 + .../dom/resources/value_resources.xml | 4 + .../jetbrains/android/dom/AndroidDomTest.java | 15 + .../android/dom/AndroidLayoutDomTest.java | 45 +++ .../android/dom/AndroidManifestDomTest.java | 7 + .../dom/AndroidValueResourcesTest.java | 30 ++ 50 files changed, 1017 insertions(+), 213 deletions(-) create mode 100644 plugins/android/src/org/jetbrains/android/augment/AndroidLightClass.java create mode 100644 plugins/android/src/org/jetbrains/android/augment/AndroidLightField.java create mode 100644 plugins/android/src/org/jetbrains/android/augment/AndroidPsiAugmentProvider.java create mode 100644 plugins/android/src/org/jetbrains/android/augment/ManifestInnerClass.java create mode 100644 plugins/android/src/org/jetbrains/android/augment/PermissionClass.java create mode 100644 plugins/android/src/org/jetbrains/android/augment/PermissionGroupClass.java create mode 100644 plugins/android/src/org/jetbrains/android/augment/ResourceTypeClass.java create mode 100644 plugins/android/testData/dom/layout/JavaCompletion1.java create mode 100644 plugins/android/testData/dom/layout/JavaCompletion1_after.java create mode 100644 plugins/android/testData/dom/layout/JavaCompletion2.java create mode 100644 plugins/android/testData/dom/layout/JavaCompletion2_after.java create mode 100644 plugins/android/testData/dom/layout/JavaCompletion3.java create mode 100644 plugins/android/testData/dom/layout/JavaCompletion3_after.java create mode 100644 plugins/android/testData/dom/layout/JavaHighlighting1.java create mode 100644 plugins/android/testData/dom/layout/JavaHighlighting2.java create mode 100644 plugins/android/testData/dom/layout/JavaHighlighting3.java create mode 100644 plugins/android/testData/dom/layout/JavaHighlighting4.java create mode 100644 plugins/android/testData/dom/layout/JavaHighlighting5.java create mode 100644 plugins/android/testData/dom/layout/JavaIdCompletion.java create mode 100644 plugins/android/testData/dom/layout/JavaIdCompletion_after.java create mode 100644 plugins/android/testData/dom/layout/main.xml create mode 100644 plugins/android/testData/dom/manifest/JavaHighlighting.java create mode 100644 plugins/android/testData/dom/manifest/Manifest.java create mode 100644 plugins/android/testData/dom/manifest/PermissionsManifest.xml create mode 100644 plugins/android/testData/dom/resources/JavaCompletion1.java create mode 100644 plugins/android/testData/dom/resources/JavaCompletion1_after.java create mode 100644 plugins/android/testData/dom/resources/JavaCompletion2.java create mode 100644 plugins/android/testData/dom/resources/JavaCompletion2_after.java create mode 100644 plugins/android/testData/dom/resources/JavaCompletion3.java create mode 100644 plugins/android/testData/dom/resources/JavaCompletion3_after.java create mode 100644 plugins/android/testData/dom/resources/JavaCompletion4.java create mode 100644 plugins/android/testData/dom/resources/JavaCompletion4_after.java create mode 100644 plugins/android/testData/dom/resources/JavaHighlighting1.java create mode 100644 plugins/android/testData/dom/resources/JavaHighlighting2.java create mode 100644 plugins/android/testData/dom/resources/value_resources.xml diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 4700bd2e5eec..3e202563f1a4 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -220,6 +220,7 @@ + diff --git a/plugins/android/src/org/jetbrains/android/augment/AndroidLightClass.java b/plugins/android/src/org/jetbrains/android/augment/AndroidLightClass.java new file mode 100644 index 000000000000..51f02ddcd4d5 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/augment/AndroidLightClass.java @@ -0,0 +1,338 @@ +package org.jetbrains.android.augment; + +import com.intellij.lang.java.JavaLanguage; +import com.intellij.navigation.ItemPresentation; +import com.intellij.navigation.ItemPresentationProviders; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.impl.PsiClassImplUtil; +import com.intellij.psi.impl.PsiImplUtil; +import com.intellij.psi.impl.light.LightElement; +import com.intellij.psi.impl.light.LightEmptyImplementsList; +import com.intellij.psi.impl.light.LightModifierList; +import com.intellij.psi.impl.light.LightTypeParameterListBuilder; +import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.search.SearchScope; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +public abstract class AndroidLightClass extends LightElement implements PsiClass, SyntheticElement { + private final PsiClass myContainingClass; + protected final String myName; + + protected AndroidLightClass(@NotNull PsiClass context, @NotNull String name) { + super(context.getManager(), JavaLanguage.INSTANCE); + myContainingClass = context; + myName = name; + } + + @Override + public String toString() { + return "AndroidRClass"; + } + + @Override + public String getQualifiedName() { + return myContainingClass.getQualifiedName() + '.' + myName; + } + + @Override + public boolean isInterface() { + return false; + } + + @Override + public boolean isAnnotationType() { + return false; + } + + @Override + public boolean isEnum() { + return false; + } + + @Override + public PsiReferenceList getExtendsList() { + return new LightEmptyImplementsList(myManager); + } + + @Override + public PsiReferenceList getImplementsList() { + return new LightEmptyImplementsList(myManager); + } + + @NotNull + @Override + public PsiClassType[] getExtendsListTypes() { + return PsiClassType.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClassType[] getImplementsListTypes() { + return PsiClassType.EMPTY_ARRAY; + } + + @Override + public PsiClass getSuperClass() { + return null; + } + + @Override + public PsiClass[] getInterfaces() { + return PsiClass.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClass[] getSupers() { + return PsiClass.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClassType[] getSuperTypes() { + return PsiClassType.EMPTY_ARRAY; + } + + @NotNull + @Override + public abstract PsiField[] getFields(); + + @NotNull + @Override + public PsiMethod[] getMethods() { + return PsiMethod.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiMethod[] getConstructors() { + return PsiMethod.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClass[] getInnerClasses() { + return PsiClass.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClassInitializer[] getInitializers() { + return PsiClassInitializer.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiField[] getAllFields() { + return getFields(); + } + + @NotNull + @Override + public PsiMethod[] getAllMethods() { + return PsiMethod.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiClass[] getAllInnerClasses() { + return PsiClass.EMPTY_ARRAY; + } + + @Override + public PsiField findFieldByName(@NonNls String name, boolean checkBases) { + final PsiField[] fields = getFields(); + for (final PsiField field : fields) { + if (name.equals(field.getName())) return field; + } + return null; + } + + @Override + public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) { + return null; + } + + @NotNull + @Override + public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) { + return PsiMethod.EMPTY_ARRAY; + } + + @NotNull + @Override + public PsiMethod[] findMethodsByName(@NonNls String name, boolean checkBases) { + return PsiMethod.EMPTY_ARRAY; + } + + @NotNull + @Override + public List> findMethodsAndTheirSubstitutorsByName(@NonNls String name, boolean checkBases) { + return Collections.emptyList(); + } + + @NotNull + @Override + public List> getAllMethodsAndTheirSubstitutors() { + return Collections.emptyList(); + } + + @Override + public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) { + return null; + } + + @Override + public PsiElement getLBrace() { + return null; + } + + @Override + public PsiElement getRBrace() { + return null; + } + + @Override + public PsiIdentifier getNameIdentifier() { + return null; + } + + @NotNull + @Override + public PsiElement getNavigationElement() { + return myContainingClass; + } + + @Nullable + @Override + public PsiElement getScope() { + return null; + } + + @Override + public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { + return false; + } + + @Override + public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) { + return false; + } + + @Override + public PsiClass getContainingClass() { + return myContainingClass; + } + + @NotNull + @Override + public Collection getVisibleSignatures() { + return Collections.emptyList(); + } + + @Override + public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { + throw new IncorrectOperationException("Cannot change the name of " + getQualifiedName() + " class"); + } + + @Override + public PsiDocComment getDocComment() { + return null; + } + + @Override + public boolean isDeprecated() { + return false; + } + + @Override + public boolean hasTypeParameters() { + return false; + } + + @Override + public PsiTypeParameterList getTypeParameterList() { + return new LightTypeParameterListBuilder(myManager, getLanguage()); + } + + @NotNull + @Override + public PsiTypeParameter[] getTypeParameters() { + return PsiTypeParameter.EMPTY_ARRAY; + } + + @Override + public PsiModifierList getModifierList() { + return new LightModifierList(myManager, getLanguage(), PsiModifier.PUBLIC, PsiModifier.STATIC, PsiModifier.FINAL); + } + + @Override + public boolean hasModifierProperty(@PsiModifier.ModifierConstant @NonNls @NotNull String name) { + final PsiModifierList list = getModifierList(); + return list != null && list.hasModifierProperty(name); + } + + @Override + public String getName() { + return myName; + } + + @Override + protected boolean isVisibilitySupported() { + return true; + } + + @Override + protected Icon getElementIcon(@IconFlags int flags) { + return PsiClassImplUtil.getClassIcon(flags, this); + } + + @Override + public boolean isEquivalentTo(PsiElement another) { + return PsiClassImplUtil.isClassEquivalentTo(this, another); + } + + @NotNull + @Override + public SearchScope getUseScope() { + return PsiImplUtil.getMemberUseScope(this); + } + + @Override + public ItemPresentation getPresentation() { + return ItemPresentationProviders.getItemPresentation(this); + } + + @Nullable + @Override + public PsiFile getContainingFile() { + final PsiClass containingClass = getContainingClass(); + return containingClass == null ? null : containingClass.getContainingFile(); + } + + @Override + public PsiElement getParent() { + return myContainingClass; + } + + @Override + public boolean processDeclarations(@NotNull final PsiScopeProcessor processor, + @NotNull final ResolveState state, + final PsiElement lastParent, + @NotNull final PsiElement place) { + return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place, false); + } +} diff --git a/plugins/android/src/org/jetbrains/android/augment/AndroidLightField.java b/plugins/android/src/org/jetbrains/android/augment/AndroidLightField.java new file mode 100644 index 000000000000..cf25fefa55d1 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/augment/AndroidLightField.java @@ -0,0 +1,28 @@ +package org.jetbrains.android.augment; + +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiType; +import com.intellij.psi.impl.light.LightFieldBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** +* @author Eugene.Kudelevsky +*/ +class AndroidLightField extends LightFieldBuilder { + private final AndroidLightClass myContext; + + public AndroidLightField(@NotNull String name, + @NotNull AndroidLightClass context, + @NotNull PsiType type) { + super(name, type, context); + myContext = context; + setContainingClass(context); + } + + @Nullable + @Override + public PsiFile getContainingFile() { + return myContext.getContainingFile(); + } +} diff --git a/plugins/android/src/org/jetbrains/android/augment/AndroidPsiAugmentProvider.java b/plugins/android/src/org/jetbrains/android/augment/AndroidPsiAugmentProvider.java new file mode 100644 index 000000000000..d7a9a153bc98 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/augment/AndroidPsiAugmentProvider.java @@ -0,0 +1,61 @@ +package org.jetbrains.android.augment; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.augment.PsiAugmentProvider; +import org.jetbrains.android.dom.converters.ResourceReferenceConverter; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidPsiAugmentProvider extends PsiAugmentProvider { + @SuppressWarnings("unchecked") + @NotNull + @Override + public List getAugments(@NotNull PsiElement element, @NotNull Class type) { + if (type != PsiClass.class || + !(element instanceof PsiClass)) { + return Collections.emptyList(); + } + final PsiClass aClass = (PsiClass)element; + final String className = aClass.getName(); + + if (!AndroidUtils.R_CLASS_NAME.equals(className) && + !AndroidUtils.MANIFEST_CLASS_NAME.equals(className)) { + return Collections.emptyList(); + } + + final AndroidFacet facet = AndroidFacet.getInstance(element); + if (facet == null) { + return Collections.emptyList(); + } + + final PsiFile containingFile = element.getContainingFile(); + if (containingFile == null) { + return Collections.emptyList(); + } + + if (AndroidResourceUtil.isRJavaFile(facet, containingFile)) { + final Set types = ResourceReferenceConverter.getResourceTypesInCurrentModule(facet); + final List result = new ArrayList(types.size()); + + for (String resType : types) { + final AndroidLightClass resClass = new ResourceTypeClass(facet, resType, aClass); + result.add((Psi)resClass); + } + return result; + } + else if (AndroidResourceUtil.isManifestJavaFile(facet, containingFile)) { + return Arrays.asList((Psi)new PermissionClass(facet, aClass), + (Psi)new PermissionGroupClass(facet, aClass)); + } + return Collections.emptyList(); + } +} diff --git a/plugins/android/src/org/jetbrains/android/augment/ManifestInnerClass.java b/plugins/android/src/org/jetbrains/android/augment/ManifestInnerClass.java new file mode 100644 index 000000000000..fff6efd99239 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/augment/ManifestInnerClass.java @@ -0,0 +1,64 @@ +package org.jetbrains.android.augment; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +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.XmlElement; +import org.jetbrains.android.dom.manifest.Manifest; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** +* @author Eugene.Kudelevsky +*/ +abstract class ManifestInnerClass extends AndroidLightClass { + private CachedValue myFieldsCache; + private final AndroidFacet myFacet; + + ManifestInnerClass(@NotNull AndroidFacet facet, @NotNull String name, @NotNull PsiClass context) { + super(context, name); + myFacet = facet; + } + + @NotNull + @Override + public PsiField[] getFields() { + if (myFieldsCache == null) { + myFieldsCache = CachedValuesManager.getManager(getProject()).createCachedValue(new CachedValueProvider() { + @Override + public Result compute() { + final Manifest manifest = myFacet.getManifest(); + if (manifest == null) { + return Result.create(PsiField.EMPTY_ARRAY, PsiModificationTracker.MODIFICATION_COUNT); + } + final List> pairs = doGetFields(manifest); + + final PsiField[] result = new PsiField[pairs.size()]; + final PsiClassType stringType = PsiType.getJavaLangString(myManager, GlobalSearchScope.allScope(getProject())); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(getProject()); + int i = 0; + for (Pair pair : pairs) { + final AndroidLightField field = new AndroidLightField(pair.getFirst(), ManifestInnerClass.this, stringType); + field.setModifiers(PsiModifier.PUBLIC, PsiModifier.STATIC); + field.setInitializer(factory.createExpressionFromText("\"" + pair.getSecond() + "\"", field)); + result[i++] = field; + } + + final XmlElement xmlElement = manifest.getXmlElement(); + final PsiFile psiManifestFile = xmlElement != null ? xmlElement.getContainingFile() : null; + return Result.create(result, psiManifestFile != null ? psiManifestFile : PsiModificationTracker.MODIFICATION_COUNT); + } + }); + } + return myFieldsCache.getValue(); + } + + @NotNull + protected abstract List> doGetFields(@NotNull Manifest manifest); +} diff --git a/plugins/android/src/org/jetbrains/android/augment/PermissionClass.java b/plugins/android/src/org/jetbrains/android/augment/PermissionClass.java new file mode 100644 index 000000000000..4338f7a701cf --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/augment/PermissionClass.java @@ -0,0 +1,41 @@ +package org.jetbrains.android.augment; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiClass; +import org.jetbrains.android.dom.manifest.Manifest; +import org.jetbrains.android.dom.manifest.Permission; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** +* @author Eugene.Kudelevsky +*/ +class PermissionClass extends ManifestInnerClass { + PermissionClass(@NotNull AndroidFacet facet, @NotNull PsiClass context) { + super(facet, "permission", context); + } + + @NotNull + @Override + protected List> doGetFields(@NotNull Manifest manifest) { + final List> result = new ArrayList>(); + + for (Permission permission : manifest.getPermissions()) { + final String name = permission.getName().getValue(); + + if (name != null && name.length() > 0) { + final int lastDotIndex = name.lastIndexOf('.'); + final String lastId = name.substring(lastDotIndex + 1); + + if (lastId.length() > 0) { + result.add(Pair.create(AndroidResourceUtil.getFieldNameByResourceName(lastId), name)); + } + } + } + return result; + } +} diff --git a/plugins/android/src/org/jetbrains/android/augment/PermissionGroupClass.java b/plugins/android/src/org/jetbrains/android/augment/PermissionGroupClass.java new file mode 100644 index 000000000000..ff90a1899e08 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/augment/PermissionGroupClass.java @@ -0,0 +1,41 @@ +package org.jetbrains.android.augment; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiClass; +import org.jetbrains.android.dom.manifest.Manifest; +import org.jetbrains.android.dom.manifest.PermissionGroup; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +public class PermissionGroupClass extends ManifestInnerClass { + PermissionGroupClass(@NotNull AndroidFacet facet, @NotNull PsiClass context) { + super(facet, "permission_group", context); + } + + @NotNull + @Override + protected List> doGetFields(@NotNull Manifest manifest) { + final List> result = new ArrayList>(); + + for (PermissionGroup permissionGroup : manifest.getPermissionGroups()) { + final String name = permissionGroup.getName().getValue(); + + if (name != null && name.length() > 0) { + final int lastDotIndex = name.lastIndexOf('.'); + final String lastId = name.substring(lastDotIndex + 1); + + if (lastId.length() > 0) { + result.add(Pair.create(AndroidResourceUtil.getFieldNameByResourceName(lastId), name)); + } + } + } + return result; + } +} diff --git a/plugins/android/src/org/jetbrains/android/augment/ResourceTypeClass.java b/plugins/android/src/org/jetbrains/android/augment/ResourceTypeClass.java new file mode 100644 index 000000000000..c6cee4030a2b --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/augment/ResourceTypeClass.java @@ -0,0 +1,57 @@ +package org.jetbrains.android.augment; + +import com.intellij.psi.*; +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 org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** +* @author Eugene.Kudelevsky +*/ +class ResourceTypeClass extends AndroidLightClass { + private CachedValue myFieldsCache; + private final AndroidFacet myFacet; + + public ResourceTypeClass(@NotNull AndroidFacet facet, @NotNull String name, @NotNull PsiClass context) { + super(context, name); + myFacet = facet; + } + + @NotNull + @Override + public PsiField[] getFields() { + if (myFieldsCache == null) { + myFieldsCache = CachedValuesManager.getManager(getProject()).createCachedValue(new CachedValueProvider() { + @Override + public Result compute() { + final PsiField[] fields = buildResourceFields(myFacet, myName, ResourceTypeClass.this); + return Result.create(fields, PsiModificationTracker.MODIFICATION_COUNT); + } + }); + } + return myFieldsCache.getValue(); + } + + @NotNull + private static PsiField[] buildResourceFields(@NotNull AndroidFacet facet, + @NotNull String resType, + @NotNull final AndroidLightClass context) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(facet.getModule().getProject()); + final Collection resNames = facet.getLocalResourceManager().getResourceNames(resType); + final PsiField[] result = new PsiField[resNames.size()]; + int i = 0; + for (String resName : resNames) { + final AndroidLightField field = new AndroidLightField(AndroidResourceUtil.getFieldNameByResourceName(resName), context, PsiType.INT); + field.setModifiers(PsiModifier.PUBLIC, PsiModifier.STATIC); + field.setInitializer(factory.createExpressionFromText("0", field)); + result[i++] = field; + } + return result; + } +} diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java index 504790bdc735..3f24d7eaf7a4 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java @@ -19,10 +19,8 @@ import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.FilenameIndex; -import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; -import org.jetbrains.android.compiler.tools.AndroidApt; import org.jetbrains.android.compiler.tools.AndroidIdl; import org.jetbrains.android.compiler.tools.AndroidRenderscript; import org.jetbrains.android.dom.manifest.Manifest; @@ -33,12 +31,13 @@ import org.jetbrains.android.fileTypes.AndroidRenderscriptFileType; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidCommonUtils; -import org.jetbrains.android.util.AndroidCompilerMessageKind; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.util.*; @@ -283,36 +282,33 @@ public class AndroidAutogenerator { // Aapt generation can be very long, so we generate it in temp directory first tempOutDir = FileUtil.createTempDirectory("android_apt_autogeneration", "tmp"); - final Map> messages = - AndroidApt.compile(item.myTarget, item.myPlatformToolsRevision, item.myManifestFileOsPath, item.myPackage, - tempOutDir.getPath(), item.myResDirOsPaths, ArrayUtil.EMPTY_STRING_ARRAY, item.myNonConstantFields); + generateStubClasses(item.myPackage, tempOutDir, + AndroidUtils.R_CLASS_NAME, + AndroidUtils.MANIFEST_CLASS_NAME); - if (messages.get(AndroidCompilerMessageKind.ERROR).size() == 0) { - for (String genFileRelPath : item.myGenFileRelPath2package.keySet()) { - final File srcFile = new File(tempOutDir.getPath() + '/' + genFileRelPath); + for (String genFileRelPath : item.myGenFileRelPath2package.keySet()) { + final File srcFile = new File(tempOutDir.getPath() + '/' + genFileRelPath); - if (srcFile.isFile()) { - final File dstFile = new File(item.myOutputDirOsPath + '/' + genFileRelPath); + if (srcFile.isFile()) { + final File dstFile = new File(item.myOutputDirOsPath + '/' + genFileRelPath); - if (dstFile.exists()) { - if (!FileUtil.delete(dstFile)) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - if (module.isDisposed() || module.getProject().isDisposed()) { - return; - } - context.addMessage(CompilerMessageCategory.ERROR, - "Cannot delete " + FileUtil.toSystemDependentName(dstFile.getPath()), null, -1, -1); + if (dstFile.exists()) { + if (!FileUtil.delete(dstFile)) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + if (module.isDisposed() || module.getProject().isDisposed()) { + return; } - }); - } + context.addMessage(CompilerMessageCategory.ERROR, + "Cannot delete " + FileUtil.toSystemDependentName(dstFile.getPath()), null, -1, -1); + } + }); } - FileUtil.rename(srcFile, dstFile); } + FileUtil.rename(srcFile, dstFile); } } - AndroidCompileUtil.addMessages(context, AndroidCompileUtil.toCompilerMessageCategoryKeys(messages), module); for (Map.Entry entry : item.myGenFileRelPath2package.entrySet()) { final String path = item.myOutputDirOsPath + '/' + entry.getKey(); @@ -328,16 +324,13 @@ public class AndroidAutogenerator { if (genSourceRoot != null) { genSourceRoot.refresh(false, true); } + facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.AAPT); - if (messages.get(AndroidCompilerMessageKind.ERROR).size() == 0) { - facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.AAPT); + for (String relPath : item.myGenFileRelPath2package.keySet()) { + final VirtualFile genFile = LocalFileSystem.getInstance().findFileByPath(item.myOutputDirOsPath + '/' + relPath); - for (String relPath : item.myGenFileRelPath2package.keySet()) { - final VirtualFile genFile = LocalFileSystem.getInstance().findFileByPath(item.myOutputDirOsPath + '/' + relPath); - - if (genFile != null && genFile.exists()) { - facet.markFileAutogenerated(AndroidAutogeneratorMode.AAPT, genFile); - } + if (genFile != null && genFile.exists()) { + facet.markFileAutogenerated(AndroidAutogeneratorMode.AAPT, genFile); } } } @@ -357,6 +350,30 @@ public class AndroidAutogenerator { } } + private static void generateStubClasses(@NotNull String aPackage, @NotNull File outputDir, @NotNull String... classNames) + throws IOException { + assert aPackage.length() > 0; + + for (String className : classNames) { + final File packageDir = new File(outputDir.getPath() + '/' + aPackage.replace('.', '/')); + if (!packageDir.exists() && !packageDir.mkdirs()) { + throw new IOException("Cannot create directory " + FileUtil.toSystemDependentName(packageDir.getPath())); + } + final BufferedWriter writer = new BufferedWriter(new FileWriter(new File(packageDir, className + ".java"))); + try { + writer.write( + "package " + aPackage + ";\n\n" + + "/* This stub is for using by IDE only. It is NOT the " + className + " class actually packed into APK */\n" + + "public final class " + className + " {\n" + + "}" + ); + } + finally { + writer.close(); + } + } + } + private static void removeAllFilesWithSameName(@NotNull final Module module, @NotNull File file, @NotNull String directoryPath) { final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); final VirtualFile genDir = LocalFileSystem.getInstance().findFileByPath(directoryPath); diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index 37869ef757d1..b31a35ad3cf7 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -15,7 +15,6 @@ */ package org.jetbrains.android.compiler; -import com.android.resources.ResourceType; import com.intellij.CommonBundle; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.CompilerConfigurationImpl; @@ -35,7 +34,6 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.ui.Messages; @@ -57,18 +55,16 @@ import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import org.jetbrains.android.compiler.artifact.*; import org.jetbrains.android.dom.manifest.Manifest; -import org.jetbrains.android.dom.resources.Attr; -import org.jetbrains.android.dom.resources.DeclareStyleable; -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.AndroidFacetConfiguration; import org.jetbrains.android.facet.AndroidRootUtil; import org.jetbrains.android.fileTypes.AndroidIdlFileType; import org.jetbrains.android.fileTypes.AndroidRenderscriptFileType; -import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.sdk.AndroidPlatform; -import org.jetbrains.android.util.*; +import org.jetbrains.android.util.AndroidCommonUtils; +import org.jetbrains.android.util.AndroidCompilerMessageKind; +import org.jetbrains.android.util.AndroidExecutionUtil; +import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -140,18 +136,6 @@ public class AndroidCompileUtil { addMessages(context, messages, null, module); } - public static void addMessages(@NotNull Map> messages, - @NotNull Map> toAdd) { - for (Map.Entry> entry : toAdd.entrySet()) { - List list = messages.get(entry.getKey()); - if (list == null) { - list = new ArrayList(); - messages.put(entry.getKey(), list); - } - list.addAll(entry.getValue()); - } - } - static void addMessages(final CompileContext context, final Map> messages, @Nullable final Map presentableFilesMap, @@ -711,95 +695,6 @@ public class AndroidCompileUtil { } } - public static void collectAllResources(@NotNull final AndroidFacet facet, final Set resourceSet) { - final LocalResourceManager manager = facet.getLocalResourceManager(); - final Project project = facet.getModule().getProject(); - - for (final ResourceType resType : AndroidResourceUtil.VALUE_RESOURCE_TYPES) { - for (final ResourceElement element : manager.getValueResources(resType.getName())) { - waitForSmartMode(project); - - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - if (!element.isValid() || facet.getModule().isDisposed() || project.isDisposed()) { - return; - } - final String name = element.getName().getValue(); - - if (name != null) { - resourceSet.add(new ResourceEntry(resType.getName(), name)); - } - } - }); - } - } - - for (final Pair pair : manager.getResourceElements()) { - final Resources resources = pair.getFirst(); - waitForSmartMode(project); - - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - if (!resources.isValid() || facet.getModule().isDisposed() || project.isDisposed()) { - return; - } - - for (final Attr attr : resources.getAttrs()) { - final String name = attr.getName().getValue(); - - if (name != null) { - resourceSet.add(new ResourceEntry(ResourceType.ATTR.getName(), name)); - } - } - - for (final DeclareStyleable styleable : resources.getDeclareStyleables()) { - final String name = styleable.getName().getValue(); - - if (name != null) { - resourceSet.add(new ResourceEntry(ResourceType.DECLARE_STYLEABLE.getName(), name)); - } - } - } - }); - } - - waitForSmartMode(project); - - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - if (facet.getModule().isDisposed() || project.isDisposed()) { - return; - } - - for (String id : manager.getIds()) { - resourceSet.add(new ResourceEntry(ResourceType.ID.getName(), id)); - } - } - }); - final HashSet visited = new HashSet(); - - for (VirtualFile subdir : manager.getResourceSubdirs(null)) { - final HashSet resourceFiles = new HashSet(); - AndroidUtils.collectFiles(subdir, visited, resourceFiles); - - for (VirtualFile file : resourceFiles) { - final String subdirName = subdir.getName(); - final int index = subdirName.indexOf('-'); - final String type = index >= 0 ? subdirName.substring(0, index) : subdirName; - resourceSet.add(new ResourceEntry(type, FileUtil.getNameWithoutExtension(file.getName()))); - } - } - } - - private static void waitForSmartMode(Project project) { - if (!ApplicationManager.getApplication().isReadAccessAllowed()) { - DumbService.getInstance(project).waitForSmartMode(); - } - } - @NotNull public static String[] toOsPaths(@NotNull VirtualFile[] classFilesDirs) { final String[] classFilesDirOsPaths = new String[classFilesDirs.length]; diff --git a/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java b/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java index af899538c7a6..a49a159a0e39 100644 --- a/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java +++ b/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java @@ -160,7 +160,7 @@ public class ResourceReferenceConverter extends ResolvingConverter getResourceTypesInCurrentModule(@NotNull AndroidFacet facet) { + public static Set getResourceTypesInCurrentModule(@NotNull AndroidFacet facet) { final Set result = new HashSet(); final LocalResourceManager manager = facet.getLocalResourceManager(); @@ -173,6 +173,9 @@ public class ResourceReferenceConverter extends ResolvingConverter 0) { + result.add(ResourceType.ID.getName()); + } return result; } @@ -205,17 +208,10 @@ public class ResourceReferenceConverter extends ResolvingConverter result, boolean explicitResourceType) { - ResourceManager manager = facet.getResourceManager(resPackage); - if (manager == null) return; - for (String name : manager.getValueResourceNames(type)) { - result.add(referenceTo(type, resPackage, name, explicitResourceType)); - } - for (String file : manager.getFileResourcesNames(type)) { - result.add(referenceTo(type, resPackage, file, explicitResourceType)); - } - if (type.equals("id")) { - for (String id : manager.getIds()) { - result.add(referenceTo(type, resPackage, id, explicitResourceType)); + final ResourceManager manager = facet.getResourceManager(resPackage); + if (manager != null) { + for (String name : manager.getResourceNames(type)) { + result.add(referenceTo(type, resPackage, name, explicitResourceType)); } } } diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java index 813a3d6592f8..89f99681da8e 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java @@ -40,7 +40,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; @@ -87,7 +86,6 @@ import org.jetbrains.android.sdk.*; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; -import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -446,12 +444,6 @@ public class AndroidFacet extends Facet { return; } - DumbService.getInstance(project).waitForSmartMode(); - - final HashSet resourceSet = new HashSet(); - AndroidCompileUtil.collectAllResources(AndroidFacet.this, resourceSet); - myListener.setResourceSet(resourceSet); - if (AndroidAptCompiler.isToCompileModule(module, getConfiguration())) { AndroidCompileUtil.generate(module, AndroidAutogeneratorMode.AAPT); } diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.java index 10d4fde3fc36..ce5b2d4f178c 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.java @@ -422,10 +422,6 @@ public class AndroidFacetEditorTab extends FacetEditorTab { String absLibsPath = myNativeLibsFolder.getText().trim(); myConfiguration.LIBS_FOLDER_RELATIVE_PATH = absLibsPath.length() > 0 ? '/' + getAndCheckRelativePath(absLibsPath, false) : ""; - if (myConfiguration.LIBRARY_PROJECT != myIsLibraryProjectCheckbox.isSelected()) { - runApt = true; - } - myConfiguration.CUSTOM_DEBUG_KEYSTORE_PATH = getSelectedCustomKeystorePath(); myConfiguration.LIBRARY_PROJECT = myIsLibraryProjectCheckbox.isSelected(); @@ -454,9 +450,6 @@ public class AndroidFacetEditorTab extends FacetEditorTab { boolean useCustomAptSrc = myUseCustomSourceDirectoryRadio.isSelected(); - if (myConfiguration.USE_CUSTOM_APK_RESOURCE_FOLDER != useCustomAptSrc) { - runApt = true; - } myConfiguration.USE_CUSTOM_APK_RESOURCE_FOLDER = useCustomAptSrc; String absAptSourcePath = myCustomAptSourceDirField.getText().trim(); @@ -464,11 +457,7 @@ public class AndroidFacetEditorTab extends FacetEditorTab { if (absAptSourcePath.length() == 0) { throw new ConfigurationException("Resources folder not specified"); } - String newCustomAptSourceFolder = '/' + getAndCheckRelativePath(absAptSourcePath, false); - if (!newCustomAptSourceFolder.equals(myConfiguration.CUSTOM_APK_RESOURCE_FOLDER)) { - runApt = true; - } - myConfiguration.CUSTOM_APK_RESOURCE_FOLDER = newCustomAptSourceFolder; + myConfiguration.CUSTOM_APK_RESOURCE_FOLDER = '/' + getAndCheckRelativePath(absAptSourcePath, false); } else { String relPath = toRelativePath(absAptSourcePath); diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java index 40f84fa2a1e0..d356350f68e3 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java @@ -20,11 +20,9 @@ import com.android.resources.ResourceFolderType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.*; -import com.intellij.util.containers.HashSet; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import org.jetbrains.android.compiler.AndroidAptCompiler; @@ -35,14 +33,12 @@ import org.jetbrains.android.fileTypes.AndroidIdlFileType; import org.jetbrains.android.fileTypes.AndroidRenderscriptFileType; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; -import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Set; import static org.jetbrains.android.util.AndroidUtils.findSourceRoot; @@ -59,9 +55,6 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { private final AndroidFacet myFacet; private String myCachedPackage = null; - private volatile Set myResourceSet = new HashSet(); - private static final Object RESOURCES_SET_LOCK = new Object(); - public AndroidResourceFilesListener(final AndroidFacet facet) { myFacet = facet; myQueue = new MergingUpdateQueue("AndroidResourcesCompilationQueue", 300, true, null, myFacet, null, false); @@ -128,12 +121,6 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { } } - public void setResourceSet(@NotNull Set resourceSet) { - synchronized (RESOURCES_SET_LOCK) { - myResourceSet = resourceSet; - } - } - private class MyUpdate extends Update { private final VirtualFileEvent myEvent; @@ -160,23 +147,6 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { } for (AndroidAutogeneratorMode autogenerationMode : autogenerationModes) { - if (autogenerationMode == AndroidAutogeneratorMode.AAPT && - AndroidRootUtil.getManifestFile(myFacet) != myEvent.getFile()) { - - final HashSet resourceSet = new HashSet(); - - DumbService.getInstance(myFacet.getModule().getProject()).waitForSmartMode(); - - AndroidCompileUtil.collectAllResources(myFacet, resourceSet); - - synchronized (RESOURCES_SET_LOCK) { - if (resourceSet.equals(myResourceSet) && - !myFacet.areSourcesGeneratedWithErrors(AndroidAutogeneratorMode.AAPT)) { - return; - } - myResourceSet = resourceSet; - } - } AndroidCompileUtil.generate(myFacet.getModule(), autogenerationMode, true); } } @@ -216,7 +186,7 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { final List modes = new ArrayList(); - if (AndroidAptCompiler.isToCompileModule(module, myFacet.getConfiguration()) && (gp == resourceDir || manifestFile == file)) { + if (AndroidAptCompiler.isToCompileModule(module, myFacet.getConfiguration()) && manifestFile == file) { final Manifest manifest = myFacet.getManifest(); final String aPackage = manifest != null ? manifest.getPackage().getValue() : null; diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java index 4615664addd9..98feb277c883 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java @@ -262,6 +262,17 @@ public abstract class ResourceManager { return result; } + @NotNull + public Collection getResourceNames(@NotNull String type) { + final Set result = new HashSet(); + result.addAll(getValueResourceNames(type)); + result.addAll(getFileResourcesNames(type)); + if (type.equals("id")) { + result.addAll(getIds()); + } + return result; + } + @Nullable public abstract AttributeDefinitions getAttributeDefinitions(); diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java index 0fdc4a277520..28976d7b4312 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java @@ -572,6 +572,15 @@ public class AndroidResourceUtil { 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 getNames(@NotNull Collection resourceTypes) { if (resourceTypes.size() == 0) { return Collections.emptyList(); @@ -803,4 +812,9 @@ public class AndroidResourceUtil { } return AndroidFileTemplateProvider.RESOURCE_FILE_TEMPLATE; } + + @NotNull + public static String getFieldNameByResourceName(@NotNull String fieldName) { + return fieldName.replace('.', '_').replace('-', '_'); + } } diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java index f486bd8b733c..d901f36fe38d 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java @@ -109,6 +109,7 @@ public class AndroidUtils { @NonNls public static final String APPLICATION_CLASS_NAME = "android.app.Application"; @NonNls public static final String ACTIVITY_BASE_CLASS_NAME = "android.app.Activity"; @NonNls public static final String R_CLASS_NAME = "R"; + @NonNls public static final String MANIFEST_CLASS_NAME = "Manifest"; @NonNls public static final String LAUNCH_ACTION_NAME = "android.intent.action.MAIN"; @NonNls public static final String LAUNCH_CATEGORY_NAME = "android.intent.category.LAUNCHER"; @NonNls public static final String INSTRUMENTATION_RUNNER_BASE_CLASS = "android.app.Instrumentation"; diff --git a/plugins/android/testData/dom/R.java b/plugins/android/testData/dom/R.java index 8534820abf58..8dd79eb2cd09 100644 --- a/plugins/android/testData/dom/R.java +++ b/plugins/android/testData/dom/R.java @@ -1,12 +1,4 @@ package p1.p2; public final class R { - public static final class string { - public static final int welcome=0x7f040001; - public static final int hello=0x7f040000; - } - - public static final class drawable { - public static final int picture3=0x7f040002; - } } diff --git a/plugins/android/testData/dom/layout/JavaCompletion1.java b/plugins/android/testData/dom/layout/JavaCompletion1.java new file mode 100644 index 000000000000..c44d4e3b910f --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaCompletion1.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion { + public void f() { + R.layo + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaCompletion1_after.java b/plugins/android/testData/dom/layout/JavaCompletion1_after.java new file mode 100644 index 000000000000..02f5932e2e53 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaCompletion1_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion { + public void f() { + R.layout + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaCompletion2.java b/plugins/android/testData/dom/layout/JavaCompletion2.java new file mode 100644 index 000000000000..da1295f7eb9b --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaCompletion2.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion { + public void f() { + R.layout.mai + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaCompletion2_after.java b/plugins/android/testData/dom/layout/JavaCompletion2_after.java new file mode 100644 index 000000000000..eaa8a6ef0ca0 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaCompletion2_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion { + public void f() { + R.layout.main + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaCompletion3.java b/plugins/android/testData/dom/layout/JavaCompletion3.java new file mode 100644 index 000000000000..f91454d35e1c --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaCompletion3.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion { + public void f() { + R.xm + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaCompletion3_after.java b/plugins/android/testData/dom/layout/JavaCompletion3_after.java new file mode 100644 index 000000000000..1e3b67ae88a3 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaCompletion3_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion { + public void f() { + R.xm + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaHighlighting1.java b/plugins/android/testData/dom/layout/JavaHighlighting1.java new file mode 100644 index 000000000000..85e118f5e804 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaHighlighting1.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaHighlighting1 { + public void f() { + int n = R.xml.main; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaHighlighting2.java b/plugins/android/testData/dom/layout/JavaHighlighting2.java new file mode 100644 index 000000000000..47b9e01b1f5d --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaHighlighting2.java @@ -0,0 +1,7 @@ +package p1; + +public class JavaHighlighting6 { + public void f() { + int n = p1.p2.R.layout.main; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaHighlighting3.java b/plugins/android/testData/dom/layout/JavaHighlighting3.java new file mode 100644 index 000000000000..499eda30888f --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaHighlighting3.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaHighlighting3 { + public void f() { + int n = R.layout.unknown; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaHighlighting4.java b/plugins/android/testData/dom/layout/JavaHighlighting4.java new file mode 100644 index 000000000000..66806e9ab4b1 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaHighlighting4.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaHighlighting4 { + public void f() { + int n = R.layout.main; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaHighlighting5.java b/plugins/android/testData/dom/layout/JavaHighlighting5.java new file mode 100644 index 000000000000..25387aced362 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaHighlighting5.java @@ -0,0 +1,7 @@ +package p1; + +public class JavaHighlighting5 { + public void f() { + int n = R.layout.main; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaIdCompletion.java b/plugins/android/testData/dom/layout/JavaIdCompletion.java new file mode 100644 index 000000000000..77771a1b1280 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaIdCompletion.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaIdCompletion { + public void f() { + int n = R.id.myComp; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/JavaIdCompletion_after.java b/plugins/android/testData/dom/layout/JavaIdCompletion_after.java new file mode 100644 index 000000000000..ac6b96173d27 --- /dev/null +++ b/plugins/android/testData/dom/layout/JavaIdCompletion_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaIdCompletion { + public void f() { + int n = R.id.myComponent; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/main.xml b/plugins/android/testData/dom/layout/main.xml new file mode 100644 index 000000000000..67acba66b044 --- /dev/null +++ b/plugins/android/testData/dom/layout/main.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/android/testData/dom/manifest/JavaHighlighting.java b/plugins/android/testData/dom/manifest/JavaHighlighting.java new file mode 100644 index 000000000000..bcfb4435c5cb --- /dev/null +++ b/plugins/android/testData/dom/manifest/JavaHighlighting.java @@ -0,0 +1,17 @@ +package p1.p2; + +import java.lang.String; + +public class JavaCompletion { + public void f() { + String s = Manifest.permission.perm1; + s = Manifest.permission.perm2; + s = Manifest.permission.unknown; + s = Manifest.permissio.perm1; + s = Manifest.permission.aba_perm; + + s = Manifest.permission_group.group1; + s = Manifest.permission_group.unknown; + s = Manifest.permission_group.aba_group; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/manifest/Manifest.java b/plugins/android/testData/dom/manifest/Manifest.java new file mode 100644 index 000000000000..078d765f9d84 --- /dev/null +++ b/plugins/android/testData/dom/manifest/Manifest.java @@ -0,0 +1,4 @@ +package p1.p2; + +public class Manifest { +} \ No newline at end of file diff --git a/plugins/android/testData/dom/manifest/PermissionsManifest.xml b/plugins/android/testData/dom/manifest/PermissionsManifest.xml new file mode 100644 index 000000000000..9fc3251d9696 --- /dev/null +++ b/plugins/android/testData/dom/manifest/PermissionsManifest.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/plugins/android/testData/dom/resources/JavaCompletion1.java b/plugins/android/testData/dom/resources/JavaCompletion1.java new file mode 100644 index 000000000000..f3d0e6365705 --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion1.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.string.my_str; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaCompletion1_after.java b/plugins/android/testData/dom/resources/JavaCompletion1_after.java new file mode 100644 index 000000000000..3baca951ac29 --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion1_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.string.my_string; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaCompletion2.java b/plugins/android/testData/dom/resources/JavaCompletion2.java new file mode 100644 index 000000000000..d3905fc1a8c7 --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion2.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.stri; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaCompletion2_after.java b/plugins/android/testData/dom/resources/JavaCompletion2_after.java new file mode 100644 index 000000000000..b8dc9bd578d7 --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion2_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.string; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaCompletion3.java b/plugins/android/testData/dom/resources/JavaCompletion3.java new file mode 100644 index 000000000000..2be43c4890bb --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion3.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.string.unknow; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaCompletion3_after.java b/plugins/android/testData/dom/resources/JavaCompletion3_after.java new file mode 100644 index 000000000000..e1db3724d1dc --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion3_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.string.unknow; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaCompletion4.java b/plugins/android/testData/dom/resources/JavaCompletion4.java new file mode 100644 index 000000000000..cc723f5d48fb --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion4.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.style.Theme_My; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaCompletion4_after.java b/plugins/android/testData/dom/resources/JavaCompletion4_after.java new file mode 100644 index 000000000000..b85158e6ceb1 --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaCompletion4_after.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.style.Theme_MyCustomStyle + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaHighlighting1.java b/plugins/android/testData/dom/resources/JavaHighlighting1.java new file mode 100644 index 000000000000..3baca951ac29 --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaHighlighting1.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.string.my_string; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/JavaHighlighting2.java b/plugins/android/testData/dom/resources/JavaHighlighting2.java new file mode 100644 index 000000000000..8b14d351ca08 --- /dev/null +++ b/plugins/android/testData/dom/resources/JavaHighlighting2.java @@ -0,0 +1,7 @@ +package p1.p2; + +public class JavaCompletion1 { + public void f() { + int n = R.string.unknown; + } +} \ No newline at end of file diff --git a/plugins/android/testData/dom/resources/value_resources.xml b/plugins/android/testData/dom/resources/value_resources.xml new file mode 100644 index 000000000000..785906d52e3e --- /dev/null +++ b/plugins/android/testData/dom/resources/value_resources.xml @@ -0,0 +1,4 @@ + + abacaba + + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidDomTest.java index a924b65a8d4b..ac05b5376693 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidDomTest.java @@ -39,6 +39,14 @@ abstract class AndroidDomTest extends AndroidTestCase { return "dom/res"; } + protected void doTestJavaCompletion(String aPackage) throws Throwable { + final String fileName = getTestName(false) + ".java"; + final VirtualFile file = copyFileToProject(fileName, "src/" + aPackage.replace('/', '.') + '/' + fileName); + myFixture.configureFromExistingVirtualFile(file); + myFixture.complete(CompletionType.BASIC); + myFixture.checkResultByFile(testFolder + '/' + getTestName(false) + "_after.java"); + } + protected static String[] withNamespace(String... arr) { List list = new ArrayList(); for (String s : arr) { @@ -66,6 +74,13 @@ abstract class AndroidDomTest extends AndroidTestCase { myFixture.checkHighlighting(true, false, false); } + protected void doTestJavaHighlighting(String aPackage) throws Throwable { + final String fileName = getTestName(false) + ".java"; + final VirtualFile virtualFile = copyFileToProject(fileName, "src/" + aPackage.replace('.', '/') + '/' + fileName); + myFixture.configureFromExistingVirtualFile(virtualFile); + myFixture.checkHighlighting(true, false, false); + } + protected void doTestCompletion() throws Throwable { doTestCompletion(true); } diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java index d859ac793252..d43a2bef3d54 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java @@ -385,5 +385,50 @@ public class AndroidLayoutDomTest extends AndroidDomTest { copyFileToProject("OnClick_Class3.java", "src/p1/p2/OnClick_Class3.java"); copyFileToProject("OnClick_Class4.java", "src/p1/p2/OnClick_Class4.java"); } + + public void testJavaCompletion1() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaCompletion2() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaCompletion3() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaIdCompletion() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaHighlighting1() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaHighlighting("p1.p2"); + } + + public void testJavaHighlighting2() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaHighlighting("p1"); + } + + public void testJavaHighlighting3() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaHighlighting("p1.p2"); + } + + public void testJavaHighlighting4() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaHighlighting("p1.p2"); + } + + public void testJavaHighlighting5() throws Throwable { + copyFileToProject("main.xml", "res/layout/main.xml"); + doTestJavaHighlighting("p1"); + } } diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java index 5255eafb741f..877c73d8c318 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java @@ -192,4 +192,11 @@ public class AndroidManifestDomTest extends AndroidDomTest { copyFileToProject("myIntResource.xml", "res/values/myIntResource.xml"); doTestCompletion(); } + + public void testJavaHighlighting() throws Throwable { + copyFileToProject("PermissionsManifest.xml", "AndroidManifest.xml"); + copyFileToProject("Manifest.java", "src/p1/p2/Manifest.java"); + doTestJavaHighlighting("p1.p2"); + } + } diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java index 93c97fe11840..e6b46fa6ff15 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java @@ -215,4 +215,34 @@ public class AndroidValueResourcesTest extends AndroidDomTest { }.execute(); myFixture.checkResultByFile(testFolder + '/' + getTestName(true) + "_after.xml", true); } + + public void testJavaCompletion1() throws Throwable { + copyFileToProject("value_resources.xml", "res/values/value_resources.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaCompletion2() throws Throwable { + copyFileToProject("value_resources.xml", "res/values/value_resources.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaCompletion3() throws Throwable { + copyFileToProject("value_resources.xml", "res/values/value_resources.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaCompletion4() throws Throwable { + copyFileToProject("value_resources.xml", "res/values/value_resources.xml"); + doTestJavaCompletion("p1.p2"); + } + + public void testJavaHighlighting1() throws Throwable { + copyFileToProject("value_resources.xml", "res/values/value_resources.xml"); + doTestJavaHighlighting("p1.p2"); + } + + public void testJavaHighlighting2() throws Throwable { + copyFileToProject("value_resources.xml", "res/values/value_resources.xml"); + doTestJavaHighlighting("p1.p2"); + } }