| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.tools.idea.rendering; |
| |
| import com.android.annotations.NonNull; |
| import com.android.ide.common.rendering.api.RenderResources; |
| import com.android.ide.common.rendering.api.ResourceValue; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.xml.XmlFile; |
| import com.intellij.psi.xml.XmlTag; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| |
| import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX; |
| import static com.android.SdkConstants.TOOLS_URI; |
| |
| /** |
| * A reference to a particular file in the project |
| */ |
| public class IncludeReference { |
| @SuppressWarnings("ConstantConditions") |
| public static final IncludeReference NONE = new IncludeReference(null, null, null); |
| |
| /** Tools namespace attribute for declaring a surrounding layout to be used */ |
| public static final String ATTR_RENDER_IN = "showIn"; |
| |
| /** |
| * The source file of the reference (included from) |
| */ |
| @NotNull |
| private final VirtualFile myFromFile; |
| |
| /** |
| * The destination file of the reference (the file being included) |
| */ |
| @Nullable |
| private final VirtualFile myToFile; |
| |
| /** |
| * The module containing the file |
| */ |
| @NotNull |
| private final Module myModule; |
| |
| /** |
| * Creates a new include reference |
| */ |
| private IncludeReference(@NonNull Module module, @NonNull VirtualFile fromFile, @Nullable VirtualFile toFile) { |
| myModule = module; |
| myFromFile = fromFile; |
| myToFile = toFile; |
| } |
| |
| /** |
| * Creates a new include reference |
| */ |
| public static IncludeReference create(@NonNull Module module, @NonNull VirtualFile fromFile, @Nullable VirtualFile toFile) { |
| return new IncludeReference(module, fromFile, toFile); |
| } |
| |
| /** |
| * Returns the associated module |
| * @return the module |
| */ |
| @NotNull |
| public Module getModule() { |
| return myModule; |
| } |
| |
| /** |
| * Returns the file for the include reference |
| * @return the file |
| */ |
| @NotNull |
| public VirtualFile getFromFile() { |
| return myFromFile; |
| } |
| |
| /** |
| * Returns the path for the include reference |
| * @return the path |
| */ |
| @NotNull |
| public File getFromPath() { |
| return VfsUtilCore.virtualToIoFile(myFromFile); |
| } |
| |
| /** |
| * Returns the destination file for the include reference |
| * @return the destination file |
| */ |
| @Nullable |
| public VirtualFile getToFile() { |
| return myToFile; |
| } |
| |
| /** |
| * Returns the destination path for the include reference |
| * @return the destination path, if known |
| */ |
| @Nullable |
| public File getToPath() { |
| return myToFile != null ? VfsUtilCore.virtualToIoFile(myToFile) : null; |
| } |
| |
| /** |
| * Returns a description of this reference, suitable to be shown to the user |
| * |
| * @return a display name for the reference |
| */ |
| @NotNull |
| public String getFromDisplayName() { |
| // The ID is deliberately kept in a pretty user-readable format but we could |
| // consider prepending layout/ on ids that don't have it (to make the display |
| // more uniform) or ripping out all layout[-constraint] prefixes out and |
| // instead prepending @ etc. |
| if (myToFile != null && myToFile.getParent() != null && myToFile.getParent().equals(myFromFile.getParent())) { |
| return myFromFile.getName(); |
| } |
| |
| return myFromFile.getParent().getName() + '/' + myFromFile.getName(); |
| } |
| |
| /** |
| * Returns the resource name of this layout |
| * |
| * @return the resource name |
| */ |
| @NotNull |
| public String getFromResourceName() { |
| return ResourceHelper.getResourceName(myFromFile); |
| } |
| |
| /** |
| * Returns the resource URL for this layout, such as {@code @layout/foo}. |
| * |
| * @return the resource URL |
| */ |
| @NotNull |
| public String getFromResourceUrl() { |
| return LAYOUT_RESOURCE_PREFIX + getFromResourceName(); |
| } |
| |
| @Nullable |
| public static String getIncludingLayout(@NotNull final XmlFile file) { |
| if (!ApplicationManager.getApplication().isReadAccessAllowed()) { |
| return ApplicationManager.getApplication().runReadAction(new Computable<String>() { |
| @Nullable |
| @Override |
| public String compute() { |
| return getIncludingLayout(file); |
| } |
| }); |
| } |
| XmlTag rootTag = file.getRootTag(); |
| if (rootTag != null && rootTag.isValid()) { |
| return rootTag.getAttributeValue(ATTR_RENDER_IN, TOOLS_URI); |
| } |
| |
| return null; |
| } |
| |
| public static void setIncludingLayout(@NotNull Project project, @NotNull XmlFile xmlFile, @Nullable String layout) { |
| XmlTag tag = xmlFile.getRootTag(); |
| if (tag != null) { |
| SetAttributeFix fix = new SetAttributeFix(project, tag, ATTR_RENDER_IN, TOOLS_URI, layout); |
| fix.execute(); |
| } |
| } |
| |
| /** Returns an {link IncludeReference} specified for the given file, or {@link #NONE} if no include should be performed from the |
| * given file */ |
| @NotNull |
| public static IncludeReference get(@NotNull final Module module, @NotNull final XmlFile file, @NotNull final RenderResources resolver) { |
| if (!ApplicationManager.getApplication().isReadAccessAllowed()) { |
| return ApplicationManager.getApplication().runReadAction(new Computable<IncludeReference>() { |
| @NotNull |
| @Override |
| public IncludeReference compute() { |
| return get(module, file, resolver); |
| } |
| }); |
| } |
| |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| XmlTag rootTag = file.getRootTag(); |
| if (rootTag != null) { |
| String layout = rootTag.getAttributeValue(ATTR_RENDER_IN, TOOLS_URI); |
| if (layout != null) { |
| ResourceValue resValue = resolver.findResValue(layout, false); |
| if (resValue != null) { |
| // TODO: Do some sort of picking based on best configuration!! |
| // I should make sure I also get a configuration that is compatible with |
| // my target include! I could stash it in the include reference! |
| File path = ResourceHelper.resolveLayout(resolver, resValue); |
| if (path != null) { |
| VirtualFile source = LocalFileSystem.getInstance().findFileByIoFile(path); |
| if (source != null) { |
| VirtualFile target = file.getVirtualFile(); |
| return create(module, source, target); |
| } |
| } |
| } |
| } |
| } |
| |
| return NONE; |
| } |
| } |