use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.
the class AndroidResourceRenameResourceProcessor method findExistingNameConflicts.
@Override
public void findExistingNameConflicts(final PsiElement originalElement, String newName, final MultiMap<PsiElement, String> conflicts) {
ResourceType type = getResourceType(originalElement);
if (type == null) {
return;
}
PsiElement element = LazyValueResourceElementWrapper.computeLazyElement(originalElement);
if (element == null) {
return;
}
AndroidFacet facet = AndroidFacet.getInstance(element);
if (facet == null) {
return;
}
// First check to see if the new name is conflicting with an existing resource
if (element instanceof PsiFile) {
// The name of a file resource is the name of the file without the extension.
// So when dealing with a file, we must first remove the extension in the name
// before checking if it is already used.
newName = AndroidCommonUtils.getResourceName(type.getName(), newName);
}
AppResourceRepository appResources = AppResourceRepository.getAppResources(facet, true);
if (appResources.hasResourceItem(type, newName)) {
boolean foundElements = false;
PsiField[] resourceFields = AndroidResourceUtil.findResourceFields(facet, type.getName(), newName, true);
String message = String.format("Resource @%1$s/%2$s already exists", type, newName);
if (resourceFields.length > 0) {
// Use find usages to find the actual declaration location such that they can be shown in the conflicts view
AndroidFindUsagesHandlerFactory factory = new AndroidFindUsagesHandlerFactory();
if (factory.canFindUsages(originalElement)) {
FindUsagesHandler handler = factory.createFindUsagesHandler(resourceFields[0], false);
if (handler != null) {
PsiElement[] elements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements());
for (PsiElement e : elements) {
if (e instanceof LightElement) {
// AndroidLightField does not work in the conflicts view; UsageInfo throws NPE
continue;
}
conflicts.putValue(e, message);
foundElements = true;
}
}
}
}
if (!foundElements) {
conflicts.putValue(originalElement, message);
}
}
// Next see if the renamed resource is also defined externally, in which case we should ask the
// user if they really want to continue. Despite the name of this method ("findExistingNameConflicts")
// and the dialog used to show the message (ConflictsDialog), this isn't conflict specific; the
// dialog title simply says "Problems Detected" and the label for the text view is "The following
// problems were found". We need to use this because it's the only facility in the rename processor
// which lets us ask the user whether to continue and to have the answer either bail out of the operation
// or to resume.
// See if this is a locally defined resource (you can't rename fields from libraries such as appcompat)
// e.g. ?attr/listPreferredItemHeightSmall
String name = getResourceName(originalElement);
if (name != null) {
Project project = facet.getModule().getProject();
List<ResourceItem> all = appResources.getResourceItem(type, name);
if (all == null) {
all = Collections.emptyList();
}
List<ResourceItem> local = ProjectResourceRepository.getProjectResources(facet, true).getResourceItem(type, name);
if (local == null) {
local = Collections.emptyList();
}
HtmlBuilder builder = null;
if (local.size() == 0 && all.size() > 0) {
builder = new HtmlBuilder(new StringBuilder(300));
builder.add("Resource is also only defined in external libraries and cannot be renamed.");
} else if (local.size() < all.size()) {
// This item is also defined in one of the libraries, not just locally: we can't rename it. Should we
// display some sort of warning?
builder = new HtmlBuilder(new StringBuilder(300));
builder.add("The resource ").beginBold().add(PREFIX_RESOURCE_REF).add(type.getName()).add("/").add(name).endBold();
builder.add(" is defined outside of the project (in one of the libraries) and cannot ");
builder.add("be updated. This can change the behavior of the application.").newline().newline();
builder.add("Are you sure you want to do this?");
}
if (builder != null) {
appendUnhandledReferences(project, facet, all, local, builder);
conflicts.putValue(originalElement, builder.getHtml());
}
}
}
use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.
the class AndroidResourceRenameResourceProcessor method appendUnhandledReferences.
/** Writes into the given {@link HtmlBuilder} a set of references
* that are defined in a library (and may or may not also be defined locally) */
private static void appendUnhandledReferences(@NotNull Project project, @NotNull AndroidFacet facet, @NotNull List<ResourceItem> all, @NotNull List<ResourceItem> local, @NotNull HtmlBuilder builder) {
File root = VfsUtilCore.virtualToIoFile(project.getBaseDir());
Collection<AndroidLibrary> libraries = null;
// Write a set of descriptions to library references. Put them in a list first such that we can
// sort the (to for example make test output stable.)
List<String> descriptions = Lists.newArrayList();
for (ResourceItem item : all) {
if (!local.contains(item)) {
ResourceFile source = item.getSource();
if (libraries == null) {
libraries = AppResourceRepository.findAarLibraries(facet);
}
if (source != null) {
File sourceFile = source.getFile();
// TODO: Look up the corresponding AAR artifact, and then use library.getRequestedCoordinates() or
// library.getResolvedCoordinates() here and append the coordinate. However, until b.android.com/77341
// is fixed this doesn't work.
/*
// Attempt to find the corresponding AAR artifact
AndroidLibrary library = null;
for (AndroidLibrary l : libraries) {
File res = l.getResFolder();
if (res.exists() && FileUtil.isAncestor(res, sourceFile, true)) {
library = l;
break;
}
}
*/
// Look for exploded-aar and strip off the prefix path to it
File localRoot = root;
File prev = sourceFile;
File current = sourceFile.getParentFile();
while (current != null) {
String name = current.getName();
if (EXPLODED_AAR.equals(name)) {
localRoot = prev;
break;
}
prev = current;
current = current.getParentFile();
}
if (FileUtil.isAncestor(localRoot, sourceFile, true)) {
descriptions.add(FileUtil.getRelativePath(localRoot, sourceFile));
} else {
descriptions.add(sourceFile.getPath());
}
}
}
}
Collections.sort(descriptions);
builder.newline().newline();
builder.add("Unhandled references:");
builder.newline();
int count = 0;
for (String s : descriptions) {
builder.add(s).newline();
count++;
if (count == 10) {
builder.add("...").newline();
builder.add("(Additional results truncated)");
break;
}
}
}
use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.
the class ProjectResourceRepositoryTest method testInvalidateIds.
// Ensure that we invalidate the id cache when the file is rescanned but ids don't change
// (this was broken)
public void testInvalidateIds() {
// Like testOverlayUpdates1, but rather than testing changes to layout resources (file-based resource)
// perform document edits in value-documents
VirtualFile layoutFile = myFixture.copyFileToProject(LAYOUT, "res/layout/layout1.xml");
VirtualFile res1 = myFixture.copyFileToProject(VALUES, "res/values/values.xml").getParent().getParent();
VirtualFile res2 = myFixture.copyFileToProject(VALUES_OVERLAY1, "res2/values/values.xml").getParent().getParent();
VirtualFile res3 = myFixture.copyFileToProject(VALUES_OVERLAY2, "res3/values/nameDoesNotMatter.xml").getParent().getParent();
myFixture.copyFileToProject(VALUES_OVERLAY2_NO, "res3/values-no/values.xml");
assertNotSame(res1, res2);
assertNotSame(res1, res3);
assertNotSame(res2, res3);
// Just need an empty repository to make it a real module -set-; otherwise with a single
// module we just get a module repository, not a module set repository
LocalResourceRepository other = new LocalResourceRepository("unit test") {
@NonNull
@Override
protected Map<ResourceType, ListMultimap<String, ResourceItem>> getMap() {
return Collections.emptyMap();
}
@Nullable
@Override
protected ListMultimap<String, ResourceItem> getMap(ResourceType type, boolean create) {
return ArrayListMultimap.create();
}
@NotNull
@Override
protected Set<VirtualFile> computeResourceDirs() {
return ImmutableSet.of();
}
};
ModuleResourceRepository module = ModuleResourceRepository.createForTest(myFacet, Arrays.asList(res1, res2, res3));
final ProjectResourceRepository resources = ProjectResourceRepository.createForTest(myFacet, Arrays.asList(module, other));
PsiFile layoutPsiFile = PsiManager.getInstance(getProject()).findFile(layoutFile);
assertNotNull(layoutPsiFile);
assertTrue(resources.hasResourceItem(ResourceType.ID, "btn_title_refresh"));
final ResourceItem item = getFirstItem(resources, ResourceType.ID, "btn_title_refresh");
final long generation = resources.getModificationCount();
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
final Document document = documentManager.getDocument(layoutPsiFile);
assertNotNull(document);
WriteCommandAction.runWriteCommandAction(null, () -> {
String string = "<ImageView style=\"@style/TitleBarSeparator\" />";
int offset = document.getText().indexOf(string);
document.deleteString(offset, offset + string.length());
documentManager.commitDocument(document);
});
assertTrue(resources.isScanPending(layoutPsiFile));
ApplicationManager.getApplication().invokeLater(() -> {
assertTrue(generation < resources.getModificationCount());
// Should still be defined:
assertTrue(resources.hasResourceItem(ResourceType.ID, "btn_title_refresh"));
ResourceItem newItem = getFirstItem(resources, ResourceType.ID, "btn_title_refresh");
assertNotNull(newItem.getSource());
// However, should be a different item
assertNotSame(item, newItem);
});
UIUtil.dispatchAllInvocationEvents();
}
use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.
the class ResourceFolderRepositoryTest method testComputeResourceStrings.
public void testComputeResourceStrings() throws Exception {
// Tests the handling of markup to raw strings
// For example, for this strings definition
// <string name="title_template_step">Step <xliff:g id="step_number">%1$d</xliff:g>: Lorem Ipsum</string>
// the resource value should be
// Step %1$d: Lorem Ipsum
VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml");
PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1);
assertNotNull(psiFile1);
ResourceFolderRepository resources = createRepository();
assertNotNull(resources);
List<ResourceItem> labelList = resources.getResourceItem(ResourceType.STRING, "title_template_step");
assertNotNull(labelList);
assertEquals(1, labelList.size());
ResourceItem label = labelList.get(0);
ResourceValue resourceValue = label.getResourceValue(false);
assertNotNull(resourceValue);
// In the file, there's whitespace unlike example above
assertEquals("Step ${step_number}: Lorem Ipsum", resourceValue.getValue());
// Test unicode escape handling: <string name="ellipsis">Here it is: …!</string>
labelList = resources.getResourceItem(ResourceType.STRING, "ellipsis");
assertNotNull(labelList);
assertEquals(1, labelList.size());
label = labelList.get(0);
resourceValue = label.getResourceValue(false);
assertNotNull(resourceValue);
assertEquals("Here it is: …!", resourceValue.getValue());
// Make sure we pick up id's defined using types
assertTrue(resources.hasResourceItem(ResourceType.ID, "action_next"));
assertFalse(resources.hasResourceItem(ResourceType.ID, "action_next2"));
}
use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.
the class ResourceFolderRepositoryTest method testMoveValueResourceFileToNewConfiguration.
public void testMoveValueResourceFileToNewConfiguration() throws Exception {
// Move a value file from one configuration to another; verify that
// items are preserved, generation changed (since it can affect config matching),
// and resource files updated.
final VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values-en/layout1.xml");
final VirtualFile file2 = myFixture.copyFileToProject(VALUES1, "res/values-no/dummy.ignore");
PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1);
assertNotNull(psiFile1);
ResourceFolderRepository resources = createRepository();
assertNotNull(resources);
ResourceItem item = getOnlyItem(resources, ResourceType.STRING, "app_name");
assertEquals("en", item.getSource().getQualifiers());
assertEquals("en", item.getConfiguration().getLocaleQualifier().getLanguage());
//noinspection ConstantConditions
assertEquals("Animations Demo", item.getResourceValue(false).getValue());
long generation = resources.getModificationCount();
WriteCommandAction.runWriteCommandAction(null, new Runnable() {
@Override
public void run() {
try {
// Move file from one location to another
file1.move(this, file2.getParent());
} catch (IOException e) {
fail(e.toString());
}
}
});
assertTrue(generation < resources.getModificationCount());
item = getOnlyItem(resources, ResourceType.STRING, "app_name");
assertEquals("no", item.getSource().getQualifiers());
assertEquals("no", item.getConfiguration().getLocaleQualifier().getLanguage());
//noinspection ConstantConditions
assertEquals("Animations Demo", item.getResourceValue(false).getValue());
}
Aggregations