Search in sources :

Example 31 with ResourceItem

use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.

the class StringsWriteUtils method getStringResourceItem.

@Nullable
private static ResourceItem getStringResourceItem(@NotNull AndroidFacet facet, @NotNull String key, @Nullable Locale locale) {
    LocalResourceRepository repository = facet.getModuleResources(true);
    // Ensure that items *just* created are processed by the resource repository
    repository.sync();
    List<ResourceItem> items = repository.getResourceItem(ResourceType.STRING, key);
    if (items == null) {
        return null;
    }
    for (ResourceItem item : items) {
        FolderConfiguration config = item.getConfiguration();
        LocaleQualifier qualifier = config == null ? null : config.getLocaleQualifier();
        if (qualifier == null) {
            if (locale == null) {
                return item;
            } else {
                continue;
            }
        }
        Locale l = Locale.create(qualifier);
        if (l.equals(locale)) {
            return item;
        }
    }
    return null;
}
Also used : Locale(com.android.tools.idea.rendering.Locale) LocalResourceRepository(com.android.tools.idea.res.LocalResourceRepository) FolderConfiguration(com.android.ide.common.resources.configuration.FolderConfiguration) ResourceItem(com.android.ide.common.res2.ResourceItem) LocaleQualifier(com.android.ide.common.resources.configuration.LocaleQualifier) Nullable(org.jetbrains.annotations.Nullable)

Example 32 with ResourceItem

use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.

the class StringsWriteUtils method createItem.

/**
   * Creates a string resource in the specified locale.
   *
   * @return the resource item that was created, null if it wasn't created or could not be read back
   */
@Nullable
public static ResourceItem createItem(@NotNull final AndroidFacet facet, @NotNull VirtualFile resFolder, @Nullable final Locale locale, @NotNull final String name, @NotNull final String value, final boolean translatable) {
    Project project = facet.getModule().getProject();
    XmlFile resourceFile = getStringResourceFile(project, resFolder, locale);
    if (resourceFile == null) {
        return null;
    }
    final XmlTag root = resourceFile.getRootTag();
    if (root == null) {
        return null;
    }
    new WriteCommandAction.Simple(project, "Creating string " + name, resourceFile) {

        @Override
        public void run() {
            XmlTag child = root.createChildTag(ResourceType.STRING.getName(), root.getNamespace(), escapeResourceStringAsXml(value), false);
            child.setAttribute(SdkConstants.ATTR_NAME, name);
            // XmlTagImpl handles a null value by deleting the attribute, which is our desired behavior
            //noinspection ConstantConditions
            child.setAttribute(SdkConstants.ATTR_TRANSLATABLE, translatable ? null : SdkConstants.VALUE_FALSE);
            root.addSubTag(child, false);
        }
    }.execute();
    if (ApplicationManager.getApplication().isReadAccessAllowed()) {
        return getStringResourceItem(facet, name, locale);
    } else {
        return ApplicationManager.getApplication().runReadAction(new Computable<ResourceItem>() {

            @Override
            public ResourceItem compute() {
                return getStringResourceItem(facet, name, locale);
            }
        });
    }
}
Also used : WriteCommandAction(com.intellij.openapi.command.WriteCommandAction) Project(com.intellij.openapi.project.Project) XmlFile(com.intellij.psi.xml.XmlFile) ResourceItem(com.android.ide.common.res2.ResourceItem) XmlTag(com.intellij.psi.xml.XmlTag) Nullable(org.jetbrains.annotations.Nullable)

Example 33 with ResourceItem

use of com.android.ide.common.res2.ResourceItem in project android by JetBrains.

the class StringsWriteUtils method setAttributeForItems.

/**
   * Sets the value of an attribute for resource items.  If SdkConstants.ATTR_NAME is set to null or "", the items are deleted.
   *
   * @param attribute The attribute whose value we wish to change
   * @param value     The desired attribute value
   * @param items     The resource items
   * @return True if the value was successfully set, false otherwise
   */
public static boolean setAttributeForItems(@NotNull Project project, @NotNull final String attribute, @Nullable final String value, @NotNull List<ResourceItem> items) {
    if (items.isEmpty()) {
        return false;
    }
    final List<XmlTag> tags = Lists.newArrayListWithExpectedSize(items.size());
    final Set<PsiFile> files = Sets.newHashSetWithExpectedSize(items.size());
    for (ResourceItem item : items) {
        XmlTag tag = LocalResourceRepository.getItemTag(project, item);
        if (tag == null) {
            return false;
        }
        tags.add(tag);
        files.add(tag.getContainingFile());
    }
    final boolean deleteTag = attribute.equals(SdkConstants.ATTR_NAME) && (value == null || value.isEmpty());
    new WriteCommandAction.Simple(project, "Setting attribute " + attribute, files.toArray(new PsiFile[files.size()])) {

        @Override
        public void run() {
            for (XmlTag tag : tags) {
                if (deleteTag) {
                    tag.delete();
                } else {
                    // XmlTagImpl handles a null value by deleting the attribute, which is our desired behavior
                    //noinspection ConstantConditions
                    tag.setAttribute(attribute, value);
                }
            }
        }
    }.execute();
    return true;
}
Also used : WriteCommandAction(com.intellij.openapi.command.WriteCommandAction) ResourceItem(com.android.ide.common.res2.ResourceItem) XmlTag(com.intellij.psi.xml.XmlTag)

Example 34 with ResourceItem

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());
        }
    }
}
Also used : FindUsagesHandler(com.intellij.find.findUsages.FindUsagesHandler) HtmlBuilder(com.android.utils.HtmlBuilder) ResourceType(com.android.resources.ResourceType) AppResourceRepository(com.android.tools.idea.res.AppResourceRepository) AndroidFacet(org.jetbrains.android.facet.AndroidFacet) LightElement(com.intellij.psi.impl.light.LightElement) Project(com.intellij.openapi.project.Project) ResourceItem(com.android.ide.common.res2.ResourceItem)

Example 35 with ResourceItem

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;
        }
    }
}
Also used : ResourceFile(com.android.ide.common.res2.ResourceFile) AndroidLibrary(com.android.builder.model.AndroidLibrary) ResourceItem(com.android.ide.common.res2.ResourceItem) VirtualFile(com.intellij.openapi.vfs.VirtualFile) ResourceFile(com.android.ide.common.res2.ResourceFile) File(java.io.File)

Aggregations

ResourceItem (com.android.ide.common.res2.ResourceItem)83 VirtualFile (com.intellij.openapi.vfs.VirtualFile)44 PsiFile (com.intellij.psi.PsiFile)35 Document (com.intellij.openapi.editor.Document)26 PsiDocumentManager (com.intellij.psi.PsiDocumentManager)26 ResourceValue (com.android.ide.common.rendering.api.ResourceValue)11 FolderConfiguration (com.android.ide.common.resources.configuration.FolderConfiguration)11 XmlTag (com.intellij.psi.xml.XmlTag)11 NotNull (org.jetbrains.annotations.NotNull)11 IOException (java.io.IOException)10 AbstractResourceRepository (com.android.ide.common.res2.AbstractResourceRepository)8 AndroidFacet (org.jetbrains.android.facet.AndroidFacet)8 StyleResourceValue (com.android.ide.common.rendering.api.StyleResourceValue)7 ResourceFile (com.android.ide.common.res2.ResourceFile)7 ResourceType (com.android.resources.ResourceType)7 WriteCommandAction (com.intellij.openapi.command.WriteCommandAction)7 Nullable (org.jetbrains.annotations.Nullable)7 LocalResourceRepository (com.android.tools.idea.res.LocalResourceRepository)6 Project (com.intellij.openapi.project.Project)6 File (java.io.File)6