blob: fdee2c9dd01731514adb7544e8e2a48e4611c5a5 [file] [log] [blame]
/*
* 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;
}
}