refactoring, create android resources from java usages

This commit is contained in:
Eugene Kudelevsky
2012-06-09 18:41:34 +04:00
parent c6ff7e0a62
commit e5d1fec8f7
19 changed files with 601 additions and 444 deletions
@@ -412,4 +412,5 @@ deployment.target.settings.min.sdk.info.message=Only compatible AVDs are shown.
android.compilation.warning.circular.app.dependency=Generated fields in {0}.R class in module ''{1}'' won''t be final, because of circular dependency on module ''{2}''
cannot.find.zip.align=The zipalign tool was not found in the SDK.\n\nPlease update to the latest SDK and re-export your application\nor run zipalign manually.\n\nAligning applications allows Android to use application resources\nmore efficiently.
file.name.not.specified.error=File name is not specified
root.element.not.specified.error=Root element is not specified
root.element.not.specified.error=Root element is not specified
directory.not.specified.error=Directory is not specified
+2
View File
@@ -219,6 +219,8 @@
<usageTargetProvider implementation="org.jetbrains.android.AndroidUsagesTargetProvider"/>
<renameHandler implementation="org.jetbrains.android.AndroidRenameHandler" order="first"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.android.inspections.AndroidQuickFixProvider"/>
<packaging.elementType implementation="org.jetbrains.android.compiler.artifact.AndroidFinalPackageElementType"/>
<packaging.sourceItemProvider implementation="org.jetbrains.android.compiler.artifact.AndroidSourceItemsProvider"/>
<packaging.artifactPropertiesProvider implementation="org.jetbrains.android.compiler.artifact.AndroidArtifactPropertiesProvider"/>
@@ -19,7 +19,11 @@ import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.meta.PsiMetaOwner;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttributeValue;
@@ -27,7 +31,6 @@ import org.jetbrains.android.dom.wrappers.FileResourceElementWrapper;
import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import java.util.List;
@@ -51,47 +54,22 @@ public class AndroidGotoDeclarationHandler implements GotoDeclarationHandler {
return null;
}
final String resFieldName = refExp.getReferenceName();
if (resFieldName == null || resFieldName.length() == 0) {
return null;
}
PsiExpression qExp = refExp.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 || !AndroidResourceUtil.isRJavaFile(facet, containingFile)) {
final Pair<String, String> pair = AndroidResourceUtil.getReferredResourceField(facet, refExp);
if (pair == null) {
return null;
}
final String resClassName = pair.getFirst();
final String resFieldName = pair.getSecond();
final List<PsiElement> resourceList = facet.getLocalResourceManager().findResourcesByFieldName(resClassName, resFieldName);
final PsiElement[] resources = resourceList.toArray(new PsiElement[resourceList.size()]);
final PsiElement[] wrappedResources = new PsiElement[resources.length];
for (int i = 0; i < resources.length; i++) {
final PsiElement resource = resources[i];
if (resource instanceof XmlAttributeValue &&
resource instanceof PsiMetaOwner &&
if (resource instanceof XmlAttributeValue &&
resource instanceof PsiMetaOwner &&
resource instanceof NavigationItem) {
wrappedResources[i] = new ValueResourceElementWrapper((XmlAttributeValue)resource);
}
@@ -16,6 +16,7 @@
package org.jetbrains.android.actions;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.intellij.CommonBundle;
import com.intellij.facet.ProjectFacetManager;
@@ -57,7 +58,7 @@ public class AndroidCreateLayoutFileAction extends CreateTypedResourceFileAction
private String myLastRootComponentName;
public AndroidCreateLayoutFileAction() {
super("Layout", "layout", "LinearLayout", false, false);
super("Layout", ResourceFolderType.LAYOUT, false, false);
}
@NotNull
@@ -75,7 +76,7 @@ public class AndroidCreateLayoutFileAction extends CreateTypedResourceFileAction
@Override
protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception {
assert myLastRootComponentName != null;
return doCreate(newName, directory, myLastRootComponentName, false);
return doCreateAndNavigate(newName, directory, myLastRootComponentName, false);
}
@Override
@@ -73,15 +73,19 @@ public class CreateResourceDialog extends DialogWrapper {
private final Map<String, CreateTypedResourceFileAction> myResType2ActionMap = new HashMap<String, CreateTypedResourceFileAction>();
private final DeviceConfiguratorPanel myDeviceConfiguratorPanel;
private final AndroidFacet myFacet;
private final ResourceType myPredefinedResourceType;
public CreateResourceDialog(@NotNull AndroidFacet facet,
Collection<CreateTypedResourceFileAction> actions,
@Nullable ResourceType predefinedResourceType,
@Nullable String predefinedFileName,
boolean chooseFileName,
@NotNull Module module,
boolean chooseModule) {
super(facet.getModule().getProject());
myFacet = facet;
myPredefinedResourceType = predefinedResourceType;
myResTypeLabel.setLabelFor(myResourceTypeCombo);
myResourceTypeCombo.registerUpDownHint(myFileNameField);
myUpDownHint.setIcon(PlatformIcons.UP_DOWN_ARROWS);
@@ -137,7 +141,7 @@ public class CreateResourceDialog extends DialogWrapper {
@Override
public void actionPerformed(ActionEvent e) {
myDeviceConfiguratorPanel.applyEditors();
updateRootElementCombo();
updateRootElementTextField();
}
});
@@ -153,8 +157,10 @@ public class CreateResourceDialog extends DialogWrapper {
}
if (predefinedFileName != null) {
myFileNameField.setVisible(false);
myFileNameLabel.setVisible(false);
if (!chooseFileName) {
myFileNameField.setVisible(false);
myFileNameLabel.setVisible(false);
}
myFileNameField.setText(predefinedFileName);
}
@@ -183,11 +189,13 @@ public class CreateResourceDialog extends DialogWrapper {
myDeviceConfiguratorPanel.updateAll();
myDeviceConfiguratorWrapper.add(myDeviceConfiguratorPanel, BorderLayout.CENTER);
setOKActionEnabled(myDirectoryNameTextField.getText().length() > 0);
updateRootElementCombo();
updateRootElementTextField();
init();
setTitle(AndroidBundle.message("new.resource.dialog.title"));
}
private void updateRootElementCombo() {
private void updateRootElementTextField() {
final CreateTypedResourceFileAction action = getSelectedAction();
if (action != null) {
@@ -195,12 +203,19 @@ public class CreateResourceDialog extends DialogWrapper {
myRootElementField = new TextFieldWithAutoCompletion<String>(
myFacet.getModule().getProject(), new TextFieldWithAutoCompletion.StringsCompletionProvider(allowedTagNames, null), true);
myRootElementField.setEnabled(allowedTagNames.size() > 1);
myRootElementField.setText(!action.isChooseTagName() ? action.getDefaultRootTag() : "");
myRootElementField.setText(!action.isChooseTagName() && myPredefinedResourceType != ResourceType.LAYOUT
? action.getDefaultRootTag()
: "");
myRootElementFieldWrapper.removeAll();
myRootElementFieldWrapper.add(myRootElementField, BorderLayout.CENTER);
}
}
@NotNull
public String getFileName() {
return myFileNameField.getText().trim();
}
private static boolean containsElement(@NotNull ListModel model, @NotNull Object objectToFind) {
for (int i = 0, n = model.getSize(); i < n; i++) {
if (objectToFind.equals(model.getElementAt(i))) {
@@ -232,7 +247,10 @@ public class CreateResourceDialog extends DialogWrapper {
}
final String subdirName = getSubdirName();
assert subdirName.length() > 0;
if (subdirName.length() == 0) {
Messages.showErrorDialog(myPanel, AndroidBundle.message("directory.not.specified.error"), CommonBundle.getErrorTitle());
return;
}
myValidator = createValidator(subdirName);
if (myValidator == null || myValidator.checkInput(fileName) && myValidator.canClose(fileName)) {
super.doOKAction();
@@ -266,7 +284,7 @@ public class CreateResourceDialog extends DialogWrapper {
@Override
public JComponent getPreferredFocusedComponent() {
if (myFileNameField.isVisible()) {
if (myFileNameField.getText().length() == 0) {
return myFileNameField;
}
else if (myResourceTypeCombo.isVisible()) {
@@ -275,7 +293,10 @@ public class CreateResourceDialog extends DialogWrapper {
else if (myModuleCombo.isVisible()) {
return myModuleCombo;
}
return myDeviceConfiguratorPanel.getAvailableQualifiersList();
else if (myRootElementFieldWrapper.isVisible()) {
return myRootElementField;
}
return myDirectoryNameTextField;
}
public CreateTypedResourceFileAction getSelectedAction() {
@@ -30,6 +30,7 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidator;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -90,39 +91,48 @@ public class CreateResourceFileAction extends CreateElementActionBase {
});
}
// must be invoked in a write action
public static PsiElement[] createResourceFile(final Project project,
@NotNull AndroidFacet facet,
@NotNull
public static PsiElement[] createFileResource(@NotNull AndroidFacet facet,
@NotNull final ResourceType resType,
@NotNull String resName,
boolean chooseDirectory) {
boolean chooseResName) {
final CreateResourceFileAction action = getInstance();
String subdirName = resType.getName();
VirtualFile resourceDir = facet.getLocalResourceManager().getResourceDir();
if (chooseDirectory) {
final MyDialog dialog = new MyDialog(facet, action.mySubactions.values(), resType, resName, action, facet.getModule(), true);
dialog.setTitle(AndroidBundle.message("new.resource.dialog.title"));
dialog.show();
if (!dialog.isOK()) {
return PsiElement.EMPTY_ARRAY;
}
subdirName = dialog.getSubdirName();
final AndroidFacet selectedFacet = AndroidFacet.getInstance(dialog.getSelectedModule());
LOG.assertTrue(selectedFacet != null);
resourceDir = selectedFacet.getLocalResourceManager().getResourceDir();
final MyDialog dialog =
new MyDialog(facet, action.mySubactions.values(), resType, resName, chooseResName, action, facet.getModule(), true);
dialog.show();
if (!dialog.isOK()) {
return PsiElement.EMPTY_ARRAY;
}
if (resourceDir != null) {
final PsiDirectory psiResDir = PsiManager.getInstance(project).findDirectory(resourceDir);
if (psiResDir != null) {
CreateElementActionBase.MyInputValidator validator = action.createValidator(project, psiResDir, subdirName);
if (validator.checkInput(resName) && validator.canClose(resName)) {
return validator.getCreatedElements();
}
}
if (chooseResName) {
resName = dialog.getFileName();
}
return PsiElement.EMPTY_ARRAY;
final String subdirName = dialog.getSubdirName();
final AndroidFacet selectedFacet = AndroidFacet.getInstance(dialog.getSelectedModule());
LOG.assertTrue(selectedFacet != null);
final VirtualFile resourceDir = selectedFacet.getLocalResourceManager().getResourceDir();
final Project project = facet.getModule().getProject();
final PsiDirectory psiResDir = resourceDir != null ? PsiManager.getInstance(project).findDirectory(resourceDir) : null;
if (psiResDir == null) {
Messages.showErrorDialog(project, "Cannot find resource directory for module " + selectedFacet.getModule().getName(),
CommonBundle.getErrorTitle());
return PsiElement.EMPTY_ARRAY;
}
final String finalResName = resName;
final PsiElement[] elements = ApplicationManager.getApplication().runWriteAction(new Computable<PsiElement[]>() {
@Nullable
@Override
public PsiElement[] compute() {
MyInputValidator validator = action.createValidator(project, psiResDir, subdirName);
return validator.checkInput(finalResName) && validator.canClose(finalResName)
? validator.getCreatedElements()
: null;
}
});
return elements != null ? elements : PsiElement.EMPTY_ARRAY;
}
@NotNull
@@ -131,13 +141,13 @@ public class CreateResourceFileAction extends CreateElementActionBase {
final AndroidFacet facet = AndroidFacet.getInstance(directory);
LOG.assertTrue(facet != null);
MyDialog dialog = new MyDialog(facet, mySubactions.values(), null, null, CreateResourceFileAction.this, facet.getModule(), false) {
final MyDialog dialog =
new MyDialog(facet, mySubactions.values(), null, null, true, CreateResourceFileAction.this, facet.getModule(), false) {
@Override
protected InputValidator createValidator(@NotNull String subdirName) {
return CreateResourceFileAction.this.createValidator(project, directory, subdirName);
}
};
dialog.setTitle(AndroidBundle.message("new.resource.dialog.title"));
dialog.show();
return PsiElement.EMPTY_ARRAY;
}
@@ -159,8 +169,11 @@ public class CreateResourceFileAction extends CreateElementActionBase {
@Override
protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception {
CreateTypedResourceFileAction action = getActionByDir(directory);
if (action == null) {
throw new IllegalArgumentException("Incorrect directory");
}
if (myRootElement != null && myRootElement.length() > 0) {
return action.doCreate(newName, directory, myRootElement, false);
return action.doCreateAndNavigate(newName, directory, myRootElement, false);
}
return action.create(newName, directory);
}
@@ -204,10 +217,11 @@ public class CreateResourceFileAction extends CreateElementActionBase {
Collection<CreateTypedResourceFileAction> actions,
@Nullable ResourceType predefinedResourceType,
@Nullable String predefinedFileName,
boolean chooseFileName,
@NotNull CreateResourceFileAction action,
@NotNull Module module,
boolean chooseModule) {
super(facet, actions, predefinedResourceType, predefinedFileName, module, chooseModule);
super(facet, actions, predefinedResourceType, predefinedFileName, chooseFileName, module, chooseModule);
myAction = action;
}
@@ -16,6 +16,7 @@
package org.jetbrains.android.actions;
import com.android.resources.ResourceFolderType;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import org.jetbrains.android.dom.animation.AndroidAnimationUtils;
import org.jetbrains.android.dom.animator.AndroidAnimatorUtil;
@@ -40,7 +41,7 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup {
CreateResourceFileAction a = new CreateResourceFileAction();
a.add(new AndroidCreateLayoutFileAction());
a.add(new CreateTypedResourceFileAction("XML", "xml", "PreferenceScreen", false, true) {
a.add(new CreateTypedResourceFileAction("XML", ResourceFolderType.XML, false, true) {
@NotNull
@Override
public List<String> getAllowedTagNames(@NotNull AndroidFacet facet) {
@@ -48,7 +49,7 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup {
}
});
a.add(new CreateTypedResourceFileAction("Drawable", "drawable", "selector", false, true) {
a.add(new CreateTypedResourceFileAction("Drawable", ResourceFolderType.DRAWABLE, false, true) {
@NotNull
@Override
public List<String> getAllowedTagNames(@NotNull AndroidFacet facet) {
@@ -56,11 +57,11 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup {
}
});
a.add(new CreateTypedResourceFileAction("Color", "color", "selector", false, false));
a.add(new CreateTypedResourceFileAction("Values", "values", "resources", true, false));
a.add(new CreateTypedResourceFileAction("Menu", "menu", "menu", false, false));
a.add(new CreateTypedResourceFileAction("Color", ResourceFolderType.COLOR, false, false));
a.add(new CreateTypedResourceFileAction("Values", ResourceFolderType.VALUES, true, false));
a.add(new CreateTypedResourceFileAction("Menu", ResourceFolderType.MENU, false, false));
a.add(new CreateTypedResourceFileAction("Animation", "anim", "set", false, true) {
a.add(new CreateTypedResourceFileAction("Animation", ResourceFolderType.ANIM, false, true) {
@NotNull
@Override
public List<String> getAllowedTagNames(@NotNull AndroidFacet facet) {
@@ -68,7 +69,7 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup {
}
});
a.add(new CreateTypedResourceFileAction("Animator", "animator", "set", false, true) {
a.add(new CreateTypedResourceFileAction("Animator", ResourceFolderType.ANIMATOR, false, true) {
@NotNull
@Override
public List<String> getAllowedTagNames(@NotNull AndroidFacet facet) {
@@ -16,11 +16,9 @@
package org.jetbrains.android.actions;
import com.android.resources.ResourceFolderType;
import com.intellij.CommonBundle;
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.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.application.ApplicationManager;
@@ -38,7 +36,6 @@ import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.PsiNavigateUtil;
import com.intellij.xml.refactoring.XmlTagInplaceRenamer;
import org.jetbrains.android.AndroidFileTemplateProvider;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
@@ -48,36 +45,33 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* @author Eugene.Kudelevsky
*/
public class CreateTypedResourceFileAction extends CreateElementActionBase {
static final String ROOT_TAG_PROPERTY = "ROOT_TAG";
private final String myResourceType;
private final ResourceFolderType myResourceType;
private final String myResourcePresentableName;
protected final String myDefaultRootTag;
private final boolean myValuesResourceFile;
private final boolean myChooseTagName;
public CreateTypedResourceFileAction(@NotNull String resourcePresentableName,
@NotNull String resourceType,
@NotNull String defaultRootTag,
@NotNull ResourceFolderType resourceFolderType,
boolean valuesResourceFile,
boolean chooseTagName) {
super(AndroidBundle.message("new.typed.resource.action.title", resourcePresentableName),
AndroidBundle.message("new.typed.resource.action.description", resourcePresentableName), StdFileTypes.XML.getIcon());
myResourceType = resourceType;
myResourceType = resourceFolderType;
myResourcePresentableName = resourcePresentableName;
myDefaultRootTag = defaultRootTag;
myDefaultRootTag = getDefaultRootTabByResourceType(resourceFolderType);
myValuesResourceFile = valuesResourceFile;
myChooseTagName = chooseTagName;
}
public String getResourceType() {
return myResourceType;
return myResourceType.getName();
}
@NotNull
@@ -93,20 +87,12 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase {
@NotNull
@Override
protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception {
return doCreate(newName, directory, myDefaultRootTag, myChooseTagName);
return doCreateAndNavigate(newName, directory, myDefaultRootTag, myChooseTagName);
}
PsiElement[] doCreate(String newName, PsiDirectory directory, String rootTagName, boolean chooseTagName) throws Exception {
FileTemplateManager manager = FileTemplateManager.getInstance();
String templateName = getTemplateName();
FileTemplate template = manager.getJ2eeTemplate(templateName);
Properties properties = new Properties();
if (!myValuesResourceFile) {
properties.setProperty(ROOT_TAG_PROPERTY, rootTagName);
}
PsiElement createdElement = FileTemplateUtil.createFromTemplate(template, newName, properties, directory);
assert createdElement instanceof XmlFile;
final XmlFile file = (XmlFile)createdElement;
PsiElement[] doCreateAndNavigate(String newName, PsiDirectory directory, String rootTagName, boolean chooseTagName) throws Exception {
final XmlFile file = AndroidResourceUtil
.createFileResource(newName, directory, rootTagName, myResourceType.getName(), myValuesResourceFile);
doNavigate(file);
if (chooseTagName) {
@@ -124,26 +110,16 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase {
}
}
}
return new PsiElement[]{createdElement};
return new PsiElement[]{file};
}
protected void doNavigate(XmlFile file) {
PsiNavigateUtil.navigate(file);
}
private String getTemplateName() {
if (myValuesResourceFile) {
return AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE;
}
if ("layout".equals(myResourceType)) {
return AndroidFileTemplateProvider.LAYOUT_RESOURCE_FILE_TEMPLATE;
}
return AndroidFileTemplateProvider.RESOURCE_FILE_TEMPLATE;
}
@Override
protected boolean isAvailable(DataContext context) {
return super.isAvailable(context) && doIsAvailable(context, myResourceType);
return super.isAvailable(context) && doIsAvailable(context, myResourceType.getName());
}
public boolean isChooseTagName() {
@@ -202,4 +178,28 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase {
public String toString() {
return myResourcePresentableName;
}
@NotNull
public static String getDefaultRootTabByResourceType(@NotNull ResourceFolderType resourceType) {
switch (resourceType) {
case XML:
return "PreferenceScreen";
case DRAWABLE:
return "selector";
case COLOR:
return "selector";
case VALUES:
return "resources";
case MENU:
return "menu";
case ANIM:
return "set";
case ANIMATOR:
return "set";
case LAYOUT:
return "LinearLayout";
default:
}
throw new IllegalArgumentException("Incorrect resource folder type");
}
}
@@ -83,13 +83,16 @@ public class CreateXmlResourceDialog extends DialogWrapper {
public CreateXmlResourceDialog(@NotNull Module module,
@NotNull ResourceType resourceType,
@Nullable String predefinedName,
@Nullable String predefinedValue) {
@Nullable String predefinedValue,
boolean chooseName) {
super(module.getProject());
myResourceType = resourceType;
if (predefinedName != null && predefinedName.length() > 0) {
myNameLabel.setVisible(false);
myNameField.setVisible(false);
if (!chooseName) {
myNameLabel.setVisible(false);
myNameField.setVisible(false);
}
myNameField.setText(predefinedName);
}
@@ -390,7 +393,7 @@ public class CreateXmlResourceDialog extends DialogWrapper {
@Override
public JComponent getPreferredFocusedComponent() {
return myNameField.isVisible() ? myNameField : myValueField;
return myNameField.getText().length() == 0 ? myNameField : myValueField;
}
@Override
@@ -15,38 +15,26 @@
*/
package org.jetbrains.android.dom.converters;
import com.android.AndroidConstants;
import com.android.resources.ResourceType;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.xml.*;
import org.jetbrains.android.actions.CreateXmlResourceDialog;
import org.jetbrains.android.dom.AdditionalConverter;
import org.jetbrains.android.dom.AndroidResourceType;
import org.jetbrains.android.dom.resources.Item;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.inspections.CreateFileResourceQuickFix;
import org.jetbrains.android.inspections.CreateValueResourceQuickFix;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NonNls;
@@ -61,11 +49,6 @@ import static org.jetbrains.android.util.AndroidUtils.SYSTEM_RESOURCE_PACKAGE;
* @author yole
*/
public class ResourceReferenceConverter extends ResolvingConverter<ResourceValue> implements CustomReferenceConverter<ResourceValue> {
private 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);
private final List<String> myResourceTypes;
private ResolvingConverter<String> myAdditionalConverter;
private boolean myAdditionalConverterSoft = false;
@@ -313,10 +296,10 @@ public class ResourceReferenceConverter extends ResolvingConverter<ResourceValue
final List<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>();
if (AndroidResourceUtil.VALUE_RESOURCE_TYPES.contains(resType)) {
fixes.add(new MyCreateValueResourceQuickFix(facet, resType, resourceName, context.getFile()));
fixes.add(new CreateValueResourceQuickFix(facet, resType, resourceName, context.getFile(), false));
}
if (XML_FILE_RESOURCE_TYPES.contains(resType)) {
fixes.add(new MyCreateFileResourceQuickFix(facet, resType, resourceName, context.getFile()));
if (AndroidResourceUtil.XML_FILE_RESOURCE_TYPES.contains(resType)) {
fixes.add(new CreateFileResourceQuickFix(facet, resType, resourceName, context.getFile(), false));
}
return fixes.toArray(new LocalQuickFix[fixes.size()]);
}
@@ -350,163 +333,4 @@ public class ResourceReferenceConverter extends ResolvingConverter<ResourceValue
}
return PsiReference.EMPTY_ARRAY;
}
public static class MyCreateValueResourceQuickFix implements LocalQuickFix, IntentionAction {
private final AndroidFacet myFacet;
private final ResourceType myResourceType;
private final String myResourceName;
private final PsiFile myFile;
public MyCreateValueResourceQuickFix(@NotNull AndroidFacet facet,
@NotNull ResourceType resourceType,
@NotNull String resourceName,
@NotNull PsiFile file) {
myFacet = facet;
myResourceType = resourceType;
myResourceName = resourceName;
myFile = file;
}
@NotNull
public String getName() {
return AndroidBundle.message("create.value.resource.quickfix.name", myResourceName,
AndroidResourceUtil.getDefaultResourceFileName(myResourceType.getName()));
}
@NotNull
@Override
public String getText() {
return AndroidBundle.message("create.value.resource.intention.name", myResourceType, myResourceName);
}
@NotNull
public String getFamilyName() {
return AndroidBundle.message("quick.fixes.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (ApplicationManager.getApplication().isUnitTestMode()) {
final String fileName = AndroidResourceUtil.getDefaultResourceFileName(myResourceType.getName());
assert fileName != null;
if (!AndroidResourceUtil.createValueResource(myFacet.getModule(), myResourceName, myResourceType,
fileName, Collections.singletonList(AndroidConstants.FD_RES_VALUES), "a")) {
return;
}
}
else {
final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(myFacet.getModule(), myResourceType, myResourceName, null);
dialog.setTitle("New " + StringUtil.capitalize(myResourceType.getDisplayName()) + " Value Resource");
dialog.show();
if (!dialog.isOK()) {
return;
}
final Module moduleToPlaceResource = dialog.getModule();
if (moduleToPlaceResource == null) {
return;
}
if (!AndroidResourceUtil.createValueResource(moduleToPlaceResource, dialog.getResourceName(), myResourceType,
dialog.getFileName(), dialog.getDirNames(), dialog.getValue())) {
return;
}
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
UndoUtil.markPsiFileForUndo(file);
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ApplicationManager.getApplication().saveAll();
}
});
}
@Override
public boolean startInWriteAction() {
return true;
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
// simplified resource creating for batch mode
LocalResourceManager manager = myFacet.getLocalResourceManager();
String initialValue = !myResourceType.equals(ResourceType.ID) ? "value" : null;
ResourceElement resElement = manager.addValueResource(myResourceType.getName(), myResourceName, initialValue);
if (resElement != null) {
if (!(resElement instanceof Item)) {
// then it is ID
List<ResourceElement> list = manager.findValueResources(myResourceType.getName(), myResourceName);
if (list.size() == 1) {
ResourceElement element = list.get(0);
XmlTag tag = element.getXmlTag();
tag.getValue().setText("");
}
}
}
UndoUtil.markPsiFileForUndo(myFile);
}
}
public static class MyCreateFileResourceQuickFix implements LocalQuickFix, IntentionAction {
private final AndroidFacet myFacet;
private final ResourceType myResourceType;
private final String myResourceName;
private final PsiFile myFile;
public MyCreateFileResourceQuickFix(@NotNull AndroidFacet facet,
@NotNull ResourceType resourceType,
@NotNull String resourceName,
@NotNull PsiFile file) {
myFacet = facet;
myResourceType = resourceType;
myResourceName = resourceName;
myFile = file;
}
@NotNull
public String getName() {
return AndroidBundle.message("create.file.resource.quickfix.name", myResourceName,
'\'' + myResourceType.getName() + "' directory");
}
@NotNull
@Override
public String getText() {
return AndroidBundle.message("create.file.resource.intention.name", myResourceType, myResourceName + ".xml");
}
@NotNull
public String getFamilyName() {
return AndroidBundle.message("quick.fixes.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
// simplified resource creating for batch mode
myFacet.getLocalResourceManager().addResourceFileAndNavigate(myResourceName, myResourceType, true);
UndoUtil.markPsiFileForUndo(myFile);
}
@Override
public boolean startInWriteAction() {
return true;
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
// simplified resource creating for batch mode
myFacet.getLocalResourceManager().addResourceFileAndNavigate(myResourceName, myResourceType, false);
UndoUtil.markPsiFileForUndo(myFile);
}
}
}
@@ -0,0 +1,74 @@
package org.jetbrains.android.inspections;
import com.android.resources.ResourceType;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReferenceExpression;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidQuickFixProvider extends UnresolvedReferenceQuickFixProvider<PsiReferenceExpression> {
@Override
public void registerFixes(PsiReferenceExpression exp, QuickFixActionRegistrar registrar) {
final Module contextModule = ModuleUtil.findModuleForPsiElement(exp);
if (contextModule == null) {
return;
}
final AndroidFacet facet = AndroidFacet.getInstance(contextModule);
if (facet == null) {
return;
}
final Manifest manifest = facet.getManifest();
if (manifest == null) {
return;
}
final String aPackage = manifest.getPackage().getValue();
if (aPackage == null) {
return;
}
final PsiFile contextFile = exp.getContainingFile();
if (contextFile == null) {
return;
}
final Pair<String, String> pair = AndroidResourceUtil.getReferredResourceField(facet, exp);
if (pair == null) {
return;
}
final String resClassName = pair.getFirst();
final String resFieldName = pair.getSecond();
final ResourceType resourceType = ResourceType.getEnum(resClassName);
if (resourceType == ResourceType.STYLEABLE || resourceType == ResourceType.ATTR) {
// todo: support
return;
}
if (AndroidResourceUtil.VALUE_RESOURCE_TYPES.contains(resourceType)) {
registrar
.register(new CreateValueResourceQuickFix(facet, resourceType, resFieldName, contextFile, true));
}
if (AndroidResourceUtil.XML_FILE_RESOURCE_TYPES.contains(resourceType)) {
registrar.register(new CreateFileResourceQuickFix(facet, resourceType, resFieldName, contextFile, true));
}
}
@NotNull
@Override
public Class<PsiReferenceExpression> getReferenceClass() {
return PsiReferenceExpression.class;
}
}
@@ -0,0 +1,117 @@
package org.jetbrains.android.inspections;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.actions.CreateResourceFileAction;
import org.jetbrains.android.actions.CreateTypedResourceFileAction;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
*/
public class CreateFileResourceQuickFix implements LocalQuickFix, IntentionAction, HighPriorityAction {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.CreateFileResourceQuickFix");
private final AndroidFacet myFacet;
private final ResourceType myResourceType;
private final String myResourceName;
private final PsiFile myFile;
private final boolean myChooseResName;
public CreateFileResourceQuickFix(@NotNull AndroidFacet facet,
@NotNull ResourceType resourceType,
@NotNull String resourceName,
@NotNull PsiFile file,
boolean chooseResName) {
myFacet = facet;
myResourceType = resourceType;
myResourceName = resourceName;
myFile = file;
myChooseResName = chooseResName;
}
@NotNull
public String getName() {
return AndroidBundle.message("create.file.resource.quickfix.name", myResourceName,
'\'' + myResourceType.getName() + "' directory");
}
@NotNull
@Override
public String getText() {
return AndroidBundle.message("create.file.resource.intention.name", myResourceType, myResourceName + ".xml");
}
@NotNull
public String getFamilyName() {
return AndroidBundle.message("quick.fixes.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
final PsiElement[] createdElements =
CreateResourceFileAction.createFileResource(myFacet, myResourceType, myResourceName + ".xml", myChooseResName);
if (createdElements.length > 0) {
UndoUtil.markPsiFileForUndo(myFile);
}
}
@Override
public boolean startInWriteAction() {
return false;
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final VirtualFile resourceDir = myFacet.getLocalResourceManager().getResourceDir();
if (resourceDir == null) {
return;
}
final PsiDirectory psiResDir = PsiManager.getInstance(project).findDirectory(resourceDir);
if (psiResDir == null) {
return;
}
final String resDirName = myResourceType.getName();
PsiDirectory resSubdir = psiResDir.findSubdirectory(resDirName);
if (resSubdir == null) {
resSubdir = ApplicationManager.getApplication().runWriteAction(new Computable<PsiDirectory>() {
public PsiDirectory compute() {
return psiResDir.createSubdirectory(resDirName);
}
});
}
try {
AndroidResourceUtil.createFileResource(myResourceName, resSubdir, CreateTypedResourceFileAction.getDefaultRootTabByResourceType(
ResourceFolderType.getFolderType(resDirName)), resDirName, false);
UndoUtil.markPsiFileForUndo(myFile);
}
catch (Exception e) {
LOG.error(e);
}
}
}
@@ -0,0 +1,126 @@
package org.jetbrains.android.inspections;
import com.android.AndroidConstants;
import com.android.resources.ResourceType;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.actions.CreateXmlResourceDialog;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class CreateValueResourceQuickFix implements LocalQuickFix, IntentionAction, HighPriorityAction {
private final AndroidFacet myFacet;
private final ResourceType myResourceType;
private final String myResourceName;
private final PsiFile myFile;
private final boolean myChooseName;
public CreateValueResourceQuickFix(@NotNull AndroidFacet facet,
@NotNull ResourceType resourceType,
@NotNull String resourceName,
@NotNull PsiFile file,
boolean chooseName) {
myFacet = facet;
myResourceType = resourceType;
myResourceName = resourceName;
myFile = file;
myChooseName = chooseName;
}
@NotNull
public String getName() {
return AndroidBundle.message("create.value.resource.quickfix.name", myResourceName,
AndroidResourceUtil.getDefaultResourceFileName(myResourceType.getName()));
}
@NotNull
@Override
public String getText() {
return AndroidBundle.message("create.value.resource.intention.name", myResourceType, myResourceName);
}
@NotNull
public String getFamilyName() {
return AndroidBundle.message("quick.fixes.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
doInvoke();
}
protected boolean doInvoke() {
if (ApplicationManager.getApplication().isUnitTestMode()) {
final String fileName = AndroidResourceUtil.getDefaultResourceFileName(myResourceType.getName());
assert fileName != null;
if (!AndroidResourceUtil.createValueResource(myFacet.getModule(), myResourceName, myResourceType, fileName,
Collections.singletonList(AndroidConstants.FD_RES_VALUES), "a")) {
return false;
}
}
else {
final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(myFacet.getModule(), myResourceType, myResourceName, null,
myChooseName);
dialog.setTitle("New " + StringUtil.capitalize(myResourceType.getDisplayName()) + " Value Resource");
dialog.show();
if (!dialog.isOK()) {
return false;
}
final Module moduleToPlaceResource = dialog.getModule();
if (moduleToPlaceResource == null) {
return false;
}
final String fileName = dialog.getFileName();
final List<String> dirNames = dialog.getDirNames();
final String resValue = dialog.getValue();
final String resName = dialog.getResourceName();
if (!AndroidResourceUtil.createValueResource(moduleToPlaceResource, resName, myResourceType, fileName, dirNames, resValue)) {
return false;
}
}
PsiDocumentManager.getInstance(myFile.getProject()).commitAllDocuments();
UndoUtil.markPsiFileForUndo(myFile);
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ApplicationManager.getApplication().saveAll();
}
});
return true;
}
@Override
public boolean startInWriteAction() {
return false;
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
// todo: implement local fix
}
}
@@ -35,10 +35,8 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
@@ -180,7 +178,7 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction impl
}
if (resName == null) {
final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(facet.getModule(), ResourceType.STRING, null, value);
final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(facet.getModule(), ResourceType.STRING, null, value, false);
dialog.setTitle("Extract String Resource");
dialog.show();
@@ -244,7 +242,7 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction impl
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
createStubResourceField(module, aPackage, resType, rJavaFieldName);
AndroidResourceUtil.createStubResourceField(module, aPackage, resType, rJavaFieldName);
}
});
@@ -292,37 +290,6 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction impl
});
}
private static void createStubResourceField(final Module module,
final String aPackage,
final String resType,
final String fieldName) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final Project project = module.getProject();
final PsiClass[] classes =
JavaPsiFacade.getInstance(project).findClasses(aPackage + ".R", GlobalSearchScope.moduleScope(module));
if (classes.length == 1) {
final PsiClass aClass = classes[0];
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
PsiClass resTypeClass = aClass.findInnerClassByName(resType, false);
if (resTypeClass == null) {
resTypeClass = (PsiClass)aClass.add(factory.createClass(resType));
}
else if (resTypeClass.findFieldByName(fieldName, false) != null) {
return;
}
final PsiField psiField = (PsiField)resTypeClass.add(factory.createField(fieldName, PsiType.INT));
PsiUtil.setModifierProperty(psiField, PsiModifier.PUBLIC, true);
PsiUtil.setModifierProperty(psiField, PsiModifier.STATIC, true);
PsiUtil.setModifierProperty(psiField, PsiModifier.FINAL, true);
}
}
});
}
@Nullable
private static String getPackage(@NotNull AndroidFacet facet) {
Manifest manifest = facet.getManifest();
@@ -17,6 +17,7 @@
package org.jetbrains.android.newProject;
import com.android.AndroidConstants;
import com.android.resources.ResourceType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.intellij.CommonBundle;
@@ -78,6 +79,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
@@ -425,7 +427,10 @@ public class AndroidModuleBuilder extends JavaModuleBuilder {
final String normalizedAppName = AndroidResourceUtil.normalizeXmlResourceValue(myApplicationName.replace("\\", "\\\\"));
if (appNameResElement == null) {
manager.addValueResource("string", appNameResource, normalizedAppName);
final String fileName = AndroidResourceUtil.getDefaultResourceFileName(ResourceType.STRING.getName());
assert fileName != null;
AndroidResourceUtil.createValueResource(facet.getModule(), appNameResource, ResourceType.STRING, fileName, Collections
.singletonList(AndroidConstants.FD_RES_VALUES), normalizedAppName);
}
else {
appNameResElement.setStringValue(normalizedAppName);
@@ -22,10 +22,8 @@ import com.intellij.CommonBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
@@ -33,13 +31,11 @@ import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.android.AndroidFileTemplateProvider;
import org.jetbrains.android.AndroidValueResourcesIndex;
import org.jetbrains.android.actions.CreateResourceFileAction;
import org.jetbrains.android.dom.attrs.AttributeDefinitions;
import org.jetbrains.android.dom.resources.Attr;
import org.jetbrains.android.dom.resources.DeclareStyleable;
@@ -57,8 +53,6 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.*;
import static org.jetbrains.android.util.AndroidUtils.loadDomElement;
/**
* @author Eugene.Kudelevsky
*/
@@ -263,50 +257,6 @@ public class LocalResourceManager extends ResourceManager {
return result;
}
// must be invoked in a write action
@Nullable
public VirtualFile addResourceFileAndNavigate(@NotNull final String fileOrResourceName,
@NotNull ResourceType resType,
boolean chooseDirectory) {
Project project = myModule.getProject();
PsiElement[] createdElements =
CreateResourceFileAction.createResourceFile(project, myFacet, resType, fileOrResourceName, chooseDirectory);
if (createdElements.length == 0) return null;
assert createdElements.length == 1;
PsiElement element = createdElements[0];
assert element instanceof PsiFile;
return ((PsiFile)element).getVirtualFile();
}
// must be invoked in a write action
@Nullable
public ResourceElement addValueResource(@NotNull final String type, @NotNull final String name, @Nullable final String value) {
String resourceFileName = AndroidResourceUtil.getDefaultResourceFileName(type);
if (resourceFileName == null) {
throw new IllegalArgumentException("Incorrect resource type");
}
VirtualFile resFile = findOrCreateResourceFile(resourceFileName);
if (resFile == null ||
!ReadonlyStatusHandler.ensureFilesWritable(myModule.getProject(), resFile)) {
return null;
}
final Resources resources = loadDomElement(myModule, resFile, Resources.class);
if (resources == null) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new IncorrectOperationException("invalid strings.xml");
}
Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("not.resource.file.error", resourceFileName),
CommonBundle.getErrorTitle());
return null;
}
ResourceElement element = AndroidResourceUtil.addValueResource(type, resources);
element.getName().setValue(name);
if (value != null) {
element.setStringValue(value);
}
return element;
}
@Nullable
private VirtualFile findOrCreateChildDir(@NotNull final VirtualFile dir, @NotNull final String name) {
try {
@@ -21,6 +21,9 @@ import com.android.resources.ResourceType;
import com.android.sdklib.SdkConstants;
import com.intellij.CommonBundle;
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;
@@ -30,13 +33,17 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModulePackageIndex;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.ui.Messages;
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.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
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.IncorrectOperationException;
@@ -68,6 +75,11 @@ public class AndroidResourceUtil {
ResourceType.ID, ResourceType.BOOL, ResourceType.INTEGER);
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";
private AndroidResourceUtil() {
}
@@ -606,11 +618,11 @@ public class AndroidResourceUtil {
}
private static boolean addValueResource(@NotNull AndroidFacet facet,
@NotNull String resourceName,
@NotNull ResourceType resourceType,
@NotNull final String resourceName,
@NotNull final ResourceType resourceType,
@NotNull String fileName,
@NotNull List<String> dirNames,
@NotNull String value) throws Exception {
@NotNull final String value) throws Exception {
if (dirNames.size() == 0) {
return false;
}
@@ -638,14 +650,19 @@ public class AndroidResourceUtil {
resourcesElements[i] = resources;
}
for (Resources resources : resourcesElements) {
final ResourceElement element = addValueResource(resourceType.getName(), resources);
element.getName().setValue(resourceName);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
for (Resources resources : resourcesElements) {
final ResourceElement element = addValueResource(resourceType.getName(), resources);
element.getName().setValue(resourceName);
if (value.length() > 0) {
element.setStringValue(value);
if (value.length() > 0) {
element.setStringValue(value);
}
}
}
}
});
return true;
}
@@ -691,4 +708,99 @@ public class AndroidResourceUtil {
Messages.showErrorDialog(project, message, CommonBundle.getErrorTitle());
}
}
@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);
}
public static void createStubResourceField(@NotNull final Module module,
@NotNull final String aPackage,
@NotNull final String resClassName,
@NotNull final String resFieldName) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final Project project = module.getProject();
final PsiClass[] classes =
JavaPsiFacade.getInstance(project).findClasses(aPackage + ".R", GlobalSearchScope.moduleScope(module));
if (classes.length == 1) {
final PsiClass aClass = classes[0];
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
PsiClass resTypeClass = aClass.findInnerClassByName(resClassName, false);
if (resTypeClass == null) {
resTypeClass = (PsiClass)aClass.add(factory.createClass(resClassName));
}
else if (resTypeClass.findFieldByName(resFieldName, false) != null) {
return;
}
final PsiField psiField = (PsiField)resTypeClass.add(factory.createField(resFieldName, PsiType.INT));
PsiUtil.setModifierProperty(psiField, PsiModifier.PUBLIC, true);
PsiUtil.setModifierProperty(psiField, PsiModifier.STATIC, true);
PsiUtil.setModifierProperty(psiField, PsiModifier.FINAL, true);
}
}
});
}
@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);
FileTemplate template = manager.getJ2eeTemplate(templateName);
Properties properties = new Properties();
if (!valuesResourceFile) {
properties.setProperty(ROOT_TAG_PROPERTY, rootTagName);
}
PsiElement createdElement = FileTemplateUtil.createFromTemplate(template, fileName, properties, resSubdir);
assert createdElement instanceof XmlFile;
return (XmlFile)createdElement;
}
private static String getTemplateName(String resourceType, boolean valuesResourceFile) {
if (valuesResourceFile) {
return AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE;
}
if ("layout".equals(resourceType)) {
return AndroidFileTemplateProvider.LAYOUT_RESOURCE_FILE_TEMPLATE;
}
return AndroidFileTemplateProvider.RESOURCE_FILE_TEMPLATE;
}
}
@@ -4,13 +4,12 @@ import com.android.sdklib.SdkConstants;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.actions.CleanupInspectionIntention;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import org.jetbrains.android.dom.converters.ResourceReferenceConverter;
import org.jetbrains.android.inspections.CreateValueResourceQuickFix;
import java.io.IOException;
import java.util.ArrayList;
@@ -351,7 +350,7 @@ public class AndroidLayoutDomTest extends AndroidDomTest {
if (ranges != null) {
for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : ranges) {
final IntentionAction action = pair.getFirst().getAction();
if (action instanceof ResourceReferenceConverter.MyCreateValueResourceQuickFix) {
if (action instanceof CreateValueResourceQuickFix) {
actions.add(action);
}
}
@@ -368,44 +367,6 @@ public class AndroidLayoutDomTest extends AndroidDomTest {
myFixture.checkResultByFile("res/values/drawables.xml", testFolder + '/' + getTestName(true) + "_drawable_after.xml", true);
}
public void testCreateResourceFromUsageCleanUp() throws Throwable {
final VirtualFile virtualFile = copyFileToProject(getTestName(true) + ".xml");
myFixture.configureFromExistingVirtualFile(virtualFile);
final List<HighlightInfo> infos = myFixture.doHighlighting();
final List<IntentionAction> actions = new ArrayList<IntentionAction>();
for (HighlightInfo info : infos) {
final List<Pair<HighlightInfo.IntentionActionDescriptor, TextRange>> ranges = info.quickFixActionRanges;
if (ranges != null) {
for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : ranges) {
final HighlightInfo.IntentionActionDescriptor descriptor = pair.getFirst();
if (descriptor.getAction() instanceof ResourceReferenceConverter.MyCreateValueResourceQuickFix) {
final List<IntentionAction> options = descriptor.getOptions(myFixture.getFile(), myFixture.getEditor());
if (options != null) {
for (IntentionAction option : options) {
if (option instanceof CleanupInspectionIntention) {
actions.add(option);
}
}
}
}
}
}
}
assertEquals(1, actions.size());
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
actions.get(0).invoke(getProject(), myFixture.getEditor(), myFixture.getFile());
}
}.execute();
myFixture.checkResultByFile("res/values/drawables.xml", testFolder + '/' + getTestName(true) + "_drawable_after.xml", true);
}
public void testXsdFile1() throws Throwable {
final VirtualFile virtualFile = copyFileToProject("XsdFile.xsd", "res/raw/XsdFile.xsd");
myFixture.configureFromExistingVirtualFile(virtualFile);
@@ -27,7 +27,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.xml.XmlAttributeValue;
import org.jetbrains.android.dom.converters.ResourceReferenceConverter;
import org.jetbrains.android.inspections.CreateValueResourceQuickFix;
import java.util.ArrayList;
import java.util.List;
@@ -199,7 +199,7 @@ public class AndroidValueResourcesTest extends AndroidDomTest {
if (ranges != null) {
for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : ranges) {
final IntentionAction action = pair.getFirst().getAction();
if (action instanceof ResourceReferenceConverter.MyCreateValueResourceQuickFix) {
if (action instanceof CreateValueResourceQuickFix) {
actions.add(action);
}
}