aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2011-01-29 09:15:43 -0800
committerAndroid Code Review <code-review@android.com>2011-01-29 09:15:43 -0800
commit42564fd81c33f560b469eb28dec587b6adddf298 (patch)
tree320b729dbfc49f5b25534b8fa61f43faab6ee4f8 /eclipse
parent0f717400dbf7c45652d7553f67b4eee85e093101 (diff)
parent6c9e1ff78b5d7613f4cff379373d69fda19812e6 (diff)
downloadsdk-42564fd81c33f560b469eb28dec587b6adddf298.zip
sdk-42564fd81c33f560b469eb28dec587b6adddf298.tar.gz
sdk-42564fd81c33f560b469eb28dec587b6adddf298.tar.bz2
Merge "Add support for multiple hyperlink targets"
Diffstat (limited to 'eclipse')
-rw-r--r--eclipse/dictionary.txt1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java620
2 files changed, 262 insertions, 359 deletions
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt
index 0ce2caa..83fcce5 100644
--- a/eclipse/dictionary.txt
+++ b/eclipse/dictionary.txt
@@ -134,6 +134,7 @@ params
pings
placeholder
plugin
+popup
pre
precompiler
pref
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
index c6ca95e..54567f3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
@@ -26,16 +26,13 @@ import static com.android.ide.eclipse.adt.AndroidConstants.FN_RESOURCE_BASE;
import static com.android.ide.eclipse.adt.AndroidConstants.FN_RESOURCE_CLASS;
import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ROOT_ELEMENT;
-import static com.android.sdklib.SdkConstants.FD_VALUES;
import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_NAME;
import static com.android.sdklib.xml.AndroidManifest.ATTRIBUTE_PACKAGE;
import static com.android.sdklib.xml.AndroidManifest.NODE_ACTIVITY;
import static com.android.sdklib.xml.AndroidManifest.NODE_SERVICE;
import com.android.annotations.VisibleForTesting;
-import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AndroidConstants;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
@@ -49,10 +46,12 @@ import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.io.IFileWrapper;
+import com.android.ide.eclipse.adt.io.IFolderWrapper;
import com.android.resources.ResourceType;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.SdkConstants;
import com.android.sdklib.io.FileWrapper;
+import com.android.sdklib.io.IAbstractFile;
+import com.android.sdklib.io.IAbstractFolder;
import com.android.util.Pair;
import org.apache.xerces.parsers.DOMParser;
@@ -64,6 +63,7 @@ import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XNIException;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
@@ -105,7 +105,6 @@ import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.encoding.util.Logger;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
@@ -127,10 +126,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -314,21 +312,6 @@ public class Hyperlinks {
}
/**
- * Is this a resource that resides in a file whose name is determined by the
- * resource name?
- */
- private static boolean isFileResource(ResourceType type) {
- ResourceFolderType[] folderTypes = FolderTypeRelationship.getRelatedFolders(type);
- for (ResourceFolderType folderType : folderTypes) {
- if (folderType != ResourceFolderType.VALUES) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
* Computes the actual exact location to jump to for a given XML context.
*
* @param context the XML context to be opened
@@ -349,73 +332,8 @@ public class Hyperlinks {
} else if (isClassElement(context) || isClassAttribute(context)) {
return openJavaClass(project, getClassFqcn(context));
} else {
- Attr attribute = context.getAttribute();
- return openResourceUrl(project, attribute.getValue());
- }
- }
-
- /**
- * Opens a given resource url (such as @layout/foo or @string/bar).
- *
- * @param project the project to search in
- * @param url the resource url
- * @return true if the request was handled successfully
- */
- private static boolean openResourceUrl(IProject project, String url) {
- if (url.startsWith("@android")) {
- return openAndroidResource(project, url);
- }
-
- Pair<ResourceType,String> resource = parseResource(url);
- if (resource != null) {
- ResourceType type = resource.getFirst();
- if (type != null) {
- String name = resource.getSecond();
- IResource member = null;
- IRegion region = null;
-
- // Is this something found in a values/ folder?
- if (isValueResource(type)) {
- Pair<IFile,IRegion> def = findValueDefinition(project, type, name);
- if (def != null) {
- member = def.getFirst();
- region = def.getSecond();
- }
- }
-
- // Id's are handled specially because they are typically defined
- // inline (though they -can- be defined in the values folder above as well,
- // in which case we will prefer that definition)
- if (member == null && type == ResourceType.ID) {
- Pair<IFile,IRegion> def = findIdDefinition(project, name);
- if (def != null) {
- member = def.getFirst();
- region = def.getSecond();
- }
- }
-
- // Is this something found in a file identified by the name?
- // (Some URLs can be both -- for example, a color can be both
- // listed in an xml files in values/ as well as under /res/color/).
- if (member == null && isFileResource(type)) {
- // It's a single file resource, like @layout/foo; open
- // layout/foo.xml
- member = findNonValueFile(project, type, name);
- }
-
- try {
- if (member != null && member instanceof IFile) {
- IFile file = (IFile) member;
- openFile(file, region);
-
- return true;
- }
- } catch (PartInitException pie) {
- Logger.log(Logger.WARNING_DEBUG, pie.getMessage(), pie);
- }
- }
+ return false;
}
- return false;
}
/** Opens the given file and shows the given (optional) region */
@@ -439,7 +357,6 @@ public class Hyperlinks {
IWorkbenchPage page = sourceEditor.getEditorSite().getPage();
IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
IPath workspacePath = workspace.getLocation();
- IEditorSite editorSite = sourceEditor.getEditorSite();
if (workspacePath.isPrefixOf(filePath)) {
IPath relativePath = filePath.makeRelativeTo(workspacePath);
IResource file = workspace.findMember(relativePath);
@@ -483,166 +400,14 @@ public class Hyperlinks {
}
// Failed: display message to the user
- String message = String.format("Could not find resource %1$s", filePath);
- IStatusLineManager status = editorSite.getActionBars().getStatusLineManager();
- status.setErrorMessage(message);
- }
-
- /**
- * Opens a framework resource's declaration
- *
- * @param project project to look up the framework for
- * @param url the resource url, such as @android:string/ok, to open the declaration
- * for
- * @return true if the url was successfully opened.
- */
- private static boolean openAndroidResource(IProject project, String url) {
- Pair<ResourceType,String> parsedUrl = parseResource(url);
- if (parsedUrl == null) {
- return false;
- }
-
- ResourceType type = parsedUrl.getFirst();
- String name = parsedUrl.getSecond();
-
- // Attempt to open files, such as layouts and drawables in @android?
- if (isFileResource(type)) {
- ProjectResources frameworkResources = getResources(project, true /* framework */);
- if (frameworkResources == null) {
- return false;
- }
- Map<ResourceType, Map<String, ResourceValue>> configuredResources =
- frameworkResources.getConfiguredResources(new FolderConfiguration());
-
- Set<String> seen = new HashSet<String>();
- seen.add(url);
-
- // Loop over map lookup since one lookup may yield another attribute to
- // be looked up, e.g. @android:drawable/alert_dark_frame will yield
- // @drawable/popup_full_dark, which when looked up will finally yield
- // the XML path we are looking for
- while (true) {
- Map<String, ResourceValue> typeMap = configuredResources.get(type);
- if (typeMap != null) {
- ResourceValue value = typeMap.get(name);
- if (value != null) {
- String valueStr = value.getValue();
- if (valueStr.startsWith("?")) { //$NON-NLS-1$
- // FIXME: It's a reference. We should resolve this properly.
- return false;
- } else if (valueStr.startsWith("@")) { //$NON-NLS-1$
- // Refers to a different resource; resolve it iteratively
- if (seen.contains(valueStr)) {
- return false;
- }
- seen.add(valueStr);
- parsedUrl = parseResource(valueStr);
- type = parsedUrl.getFirst();
- name = parsedUrl.getSecond();
- // Continue to iterate map lookup
- } else {
- // valueStr may not be a path... if it's not, don't try
- // to look it up. (For example, it may return the resolved
- // string value of @android:string/cancel => "Cancel").
- if (new File(valueStr).exists()) {
- Path path = new Path(valueStr);
- openPath(path, null, -1);
- return true;
- }
- break;
- }
- } else {
- break;
- }
- } else {
- break;
- }
- }
-
- // Attempt to find files via ProjectResources.getSourceFiles(); this
- // is done after the above search since this search won't resolve references
- FolderConfiguration configuration = getConfiguration();
- List<ResourceFile> sourceFiles = frameworkResources.getSourceFiles(type, name,
- configuration);
- for (ResourceFile file : sourceFiles) {
- String location = file.getFile().getOsLocation();
- if (new File(location).exists()) {
- Path path = new Path(location);
- openPath(path, null, -1);
- return true;
- }
- }
-
- } else if (isValueResource(type)) {
- FolderConfiguration configuration = getConfiguration();
- Pair<File, Integer> match = findFrameworkValueByConfig(project, type, name,
- configuration);
- if (match == null && configuration != null) {
- match = findFrameworkValueByConfig(project, type, name, null);
- }
-
- if (match != null) {
- Path path = new Path(match.getFirst().getPath());
- openPath(path, null, match.getSecond());
- return true;
- }
- }
-
- return false;
- }
-
- /** Return the set of matching source files for the given resource type and name */
- private static List<ResourceFile> getResourceFiles(IProject project,
- ResourceType type, String name, boolean framework,
- FolderConfiguration configuration) {
- ProjectResources resources = getResources(project, framework);
- if (resources == null) {
- return null;
- }
- List<ResourceFile> sourceFiles = resources.getSourceFiles(type, name, configuration);
- if (sourceFiles != null) {
- if (sourceFiles.size() > 1 && configuration == null) {
- // Sort all the files in the base values/ folder first, followed by
- // everything else
- List<ResourceFile> first = new ArrayList<ResourceFile>();
- List<ResourceFile> second = new ArrayList<ResourceFile>();
- for (ResourceFile file : sourceFiles) {
- if (FD_VALUES.equals(file.getFolder().getFolder().getName())) {
- // Found match in value
- first.add(file);
- } else {
- second.add(file);
- }
- }
- first.addAll(second);
- sourceFiles = first;
- }
- }
-
- return sourceFiles;
+ displayError(String.format("Could not find resource %1$s", filePath));
}
- /** Searches for the given resource for a specific configuration (which may be null) */
- private static Pair<File, Integer> findFrameworkValueByConfig(IProject project,
- ResourceType type, String name, FolderConfiguration configuration) {
- List<ResourceFile> sourceFiles = getResourceFiles(project, type, name, true /* framework*/,
- configuration);
- if (sourceFiles != null) {
- for (ResourceFile resourceFile : sourceFiles) {
- if (resourceFile.getFile() instanceof FileWrapper) {
- File file = ((FileWrapper) resourceFile.getFile());
- if (file.getName().endsWith(EXT_XML)) {
- // Must have an XML extension
- Pair<File, Integer> match = findValueInXml(type, name, file);
- if (match != null) {
- return match;
- }
- }
- }
- }
- }
-
- return null;
+ private static void displayError(String message) {
+ // Failed: display message to the user
+ IEditorSite editorSite = getEditor().getEditorSite();
+ IStatusLineManager status = editorSite.getActionBars().getStatusLineManager();
+ status.setErrorMessage(message);
}
/**
@@ -677,41 +442,6 @@ public class Hyperlinks {
return false;
}
- /** Looks up the project member of the given type and the given name */
- private static IResource findNonValueFile(IProject project, ResourceType type, String name) {
- ProjectResources resources = getResources(project, false /* not framework */);
- if (resources == null) {
- return null;
- }
- FolderConfiguration configuration = getConfiguration();
- if (configuration != null) {
- IResource file = findFileByConfig(type, name, resources, configuration);
- if (file != null) {
- return file;
- }
- }
-
- return findFileByConfig(type, name, resources, null);
- }
-
- /**
- * Find a file for a given named resource, associated with a given folder
- * configuration
- */
- private static IResource findFileByConfig(ResourceType type, String name,
- ProjectResources resources, FolderConfiguration configuration) {
- List<ResourceFile> sourceFiles = resources.getSourceFiles(type, name, configuration);
- if (sourceFiles != null) {
- for (ResourceFile resourceFile : sourceFiles) {
- if (resourceFile.getFile() instanceof IFileWrapper) {
- return ((IFileWrapper) resourceFile.getFile()).getIFile();
- }
- }
- }
-
- return null;
- }
-
/**
* Returns the current configuration, if the associated UI editor has been initialized
* and has an associated configuration
@@ -742,9 +472,12 @@ public class Hyperlinks {
IFile file = ((FileEditorInput) editorInput).getFile();
IProject project = file.getProject();
ProjectResources pr = ResourceManager.getInstance().getProjectResources(project);
- ResourceFolder resFolder = pr.getResourceFolder((IFolder) file.getParent());
- if (resFolder != null) {
- return resFolder.getConfiguration();
+ IContainer parent = file.getParent();
+ if (parent instanceof IFolder) {
+ ResourceFolder resFolder = pr.getResourceFolder((IFolder) parent);
+ if (resFolder != null) {
+ return resFolder.getConfiguration();
+ }
}
}
}
@@ -805,78 +538,66 @@ public class Hyperlinks {
}
}
- // We're currently only searching in the base layout folder.
- // The next step is to add global resource reference tracking (which we already
- // need to detect unused resources etc) and in that case we can quickly offer
- // multiple links, one to each definition.
- String folderPath = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP
- + SdkConstants.FD_LAYOUT;
-
- IFolder f = project.getFolder(folderPath);
- if (f.exists()) {
- try {
- // Check XML files in values/
- for (IResource resource : f.members()) {
- if (resource.exists() && !resource.isDerived() && resource instanceof IFile) {
- IFile file = (IFile) resource;
- // Must have an XML extension
- if (EXT_XML.equals(file.getFileExtension())) {
- Pair<IFile, IRegion> target = findIdInXml(id, file);
+ // Look in the configuration folder: Search compatible configurations
+ ProjectResources resources = getResources(project, false /* isFramework */);
+ FolderConfiguration configuration = getConfiguration();
+ if (configuration != null) { // Not the case when searching from Java files for example
+ List<ResourceFolder> folders = resources.getFolders(ResourceFolderType.LAYOUT);
+ if (folders != null) {
+ for (ResourceFolder folder : folders) {
+ if (folder.getConfiguration().isMatchFor(configuration)) {
+ IAbstractFolder wrapper = folder.getFolder();
+ if (wrapper instanceof IFolderWrapper) {
+ IFolder iFolder = ((IFolderWrapper) wrapper).getIFolder();
+ Pair<IFile, IRegion> target = findIdInFolder(iFolder, id);
if (target != null) {
return target;
}
}
}
}
- } catch (CoreException e) {
- AdtPlugin.log(e, ""); //$NON-NLS-1$
+ return null;
}
}
- return null;
- }
-
- /**
- * Searches for a resource of a "multi-file" type (like @string) where the value can
- * be found in any file within the folder containing resource of that type (in the
- * case of @string, "values", and in the case of @color, "colors", etc).
- */
- private static Pair<IFile, IRegion> findValueDefinition(IProject project, ResourceType type,
- String name) {
- // Search within the files in the values folder and find the value which defines
- // the given resource. To be efficient, we will only parse XML files that contain
- // a string match of the given token name.
- FolderConfiguration configuration = getConfiguration();
- Pair<IFile, IRegion> target = findValueByConfig(project, type, name, configuration);
- if (target != null) {
- return target;
- }
-
- if (configuration != null) {
- // Try searching without configuration too; more potential matches
- return findValueByConfig(project, type, name, configuration);
+ // Ugh. Search ALL layout files in the project!
+ List<ResourceFolder> folders = resources.getFolders(ResourceFolderType.LAYOUT);
+ if (folders != null) {
+ for (ResourceFolder folder : folders) {
+ IAbstractFolder wrapper = folder.getFolder();
+ if (wrapper instanceof IFolderWrapper) {
+ IFolder iFolder = ((IFolderWrapper) wrapper).getIFolder();
+ Pair<IFile, IRegion> target = findIdInFolder(iFolder, id);
+ if (target != null) {
+ return target;
+ }
+ }
+ }
}
return null;
}
- /** Searches for the given resource for a specific configuration (which may be null) */
- private static Pair<IFile, IRegion> findValueByConfig(IProject project,
- ResourceType type, String name, FolderConfiguration configuration) {
- List<ResourceFile> sourceFiles = getResourceFiles(project, type, name,
- false /* not framework*/, configuration);
- if (sourceFiles != null) {
- for (ResourceFile resourceFile : sourceFiles) {
- if (resourceFile.getFile() instanceof IFileWrapper) {
- IFile file = ((IFileWrapper) resourceFile.getFile()).getIFile();
+ /**
+ * Finds a definition of an id attribute in a particular layout folder.
+ */
+ private static Pair<IFile, IRegion> findIdInFolder(IContainer f, String id) {
+ try {
+ // Check XML files in values/
+ for (IResource resource : f.members()) {
+ if (resource.exists() && !resource.isDerived() && resource instanceof IFile) {
+ IFile file = (IFile) resource;
+ // Must have an XML extension
if (EXT_XML.equals(file.getFileExtension())) {
- Pair<IFile, IRegion> target = findValueInXml(type, name, file);
+ Pair<IFile, IRegion> target = findIdInXml(id, file);
if (target != null) {
return target;
}
}
}
}
+ } catch (CoreException e) {
+ AdtPlugin.log(e, ""); //$NON-NLS-1$
}
return null;
@@ -1110,6 +831,95 @@ public class Hyperlinks {
return null;
}
+ /**
+ * Computes hyperlinks to resource definitions for resource urls (e.g. {@code
+ * @android:string/ok} or {@code @layout/foo}. May create multiple links.
+ */
+ private static IHyperlink[] getResourceLinks(IRegion range, String url) {
+ List<IHyperlink> links = new ArrayList<IHyperlink>();
+ IProject project = Hyperlinks.getProject();
+ FolderConfiguration configuration = getConfiguration();
+
+ Pair<ResourceType,String> resource = parseResource(url);
+ if (resource == null || resource.getFirst() == null) {
+ return null;
+ }
+ ResourceType type = resource.getFirst();
+ String name = resource.getSecond();
+
+ boolean isFramework = url.startsWith("@android"); //$NON-NLS-1$
+
+ ProjectResources resources = getResources(project, isFramework);
+ if (resources == null) {
+ return null;
+ }
+ List<ResourceFile> sourceFiles = resources.getSourceFiles(type, name,
+ null /*configuration*/);
+ ResourceFile best = null;
+ if (configuration != null && sourceFiles != null && sourceFiles.size() > 0) {
+ List<ResourceFile> bestFiles = resources.getSourceFiles(type, name, configuration);
+ if (bestFiles != null && bestFiles.size() > 0) {
+ best = bestFiles.get(0);
+ }
+ }
+ if (sourceFiles != null) {
+ List<ResourceFile> matches = new ArrayList<ResourceFile>();
+ for (ResourceFile resourceFile : sourceFiles) {
+ matches.add(resourceFile);
+ }
+
+ if (matches.size() > 0) {
+ final ResourceFile fBest = best;
+ Collections.sort(matches, new Comparator<ResourceFile>() {
+ public int compare(ResourceFile rf1, ResourceFile rf2) {
+ // Sort best item to the front
+ if (rf1 == fBest) {
+ return -1;
+ } else if (rf2 == fBest) {
+ return 1;
+ } else {
+ return getFileName(rf1).compareTo(getFileName(rf2));
+ }
+ }
+ });
+
+ // Is this something found in a values/ folder?
+ boolean valueResource = isValueResource(type);
+ //boolean fileResource = isFileResource(type);
+
+ for (ResourceFile file : matches) {
+ String folderName = file.getFolder().getFolder().getName();
+ String label = String.format("Open Declaration in %1$s/%2$s",
+ folderName, getFileName(file));
+
+ // Only search for resource type within the file if it's an
+ // XML file and it is a value resource
+ ResourceLink link = new ResourceLink(label, range, file,
+ valueResource ? type : null, name);
+ links.add(link);
+ }
+ }
+ }
+
+ // Id's are handled specially because they are typically defined
+ // inline (though they -can- be defined in the values folder above as
+ // well, in which case we will prefer that definition)
+ if (!isFramework && type == ResourceType.ID && links.size() == 0) {
+ // Must compute these lazily...
+ links.add(new ResourceLink("Open XML Declaration", range, null, type, name));
+ }
+
+ if (links.size() > 0) {
+ return links.toArray(new IHyperlink[links.size()]);
+ } else {
+ return null;
+ }
+ }
+
+ private static String getFileName(ResourceFile file) {
+ return file.getFile().getName();
+ }
+
/** Detector for finding Android references in XML files */
public static class XmlResolver extends AbstractHyperlinkDetector {
@@ -1135,6 +945,15 @@ public class Hyperlinks {
isLinkable = true;
// Strip out quotes
range = new Region(range.getOffset() + 1, range.getLength() - 2);
+
+ Attr attribute = context.getAttribute();
+ if (attribute != null && attribute.getValue().startsWith("@")) { //$NON-NLS-1$
+ // Instantly create links for resources since we can use the existing
+ // resolved maps for this and offer multiple choices for the user
+
+ String url = attribute.getValue();
+ return getResourceLinks(range, url);
+ }
}
} else if (type == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
if (isAttributeNameLink(context)) {
@@ -1147,7 +966,7 @@ public class Hyperlinks {
}
if (isLinkable) {
- IHyperlink hyperlink = new DeferredResolutionLink(null, context, range);
+ IHyperlink hyperlink = new DeferredResolutionLink(context, range);
if (hyperlink != null) {
return new IHyperlink[] {
hyperlink
@@ -1237,9 +1056,7 @@ public class Hyperlinks {
}
String elementName = element.getElementName();
String url = '@' + typeName + '/' + elementName;
- return new IHyperlink[] {
- new DeferredResolutionLink(url, null, wordRegion)
- };
+ return getResourceLinks(wordRegion, url);
}
}
@@ -1293,14 +1110,11 @@ public class Hyperlinks {
* until it is asked to open the hyperlink
*/
private static class DeferredResolutionLink implements IHyperlink {
- private String mResourceUrl;
private XmlContext mXmlContext;
private IRegion mRegion;
- public DeferredResolutionLink(String resourceName,
- XmlContext xmlContext, IRegion mRegion) {
+ public DeferredResolutionLink(XmlContext xmlContext, IRegion mRegion) {
super();
- this.mResourceUrl = resourceName;
this.mXmlContext = xmlContext;
this.mRegion = mRegion;
}
@@ -1319,24 +1133,112 @@ public class Hyperlinks {
public void open() {
// Lazily compute the location to open
- IProject project = Hyperlinks.getProject();
- if (mResourceUrl != null) {
- if (!openResourceUrl(project, mResourceUrl)) {
- // Failed: display message to the user
- String message = String.format("Could not open %1$s", mResourceUrl);
- IEditorSite editorSite = Hyperlinks.getEditor().getEditorSite();
- IStatusLineManager status = editorSite.getActionBars().getStatusLineManager();
- status.setErrorMessage(message);
+ if (mXmlContext != null && !Hyperlinks.open(mXmlContext)) {
+ // Failed: display message to the user
+ displayError("Could not open link");
+ }
+ }
+ }
+
+ /**
+ * Hyperlink implementation which provides a link for a resource; the actual file name
+ * is known, but the value location within XML files is deferred until the link is
+ * actually opened.
+ */
+ private static class ResourceLink implements IHyperlink {
+ private final String mLinkText;
+ private final IRegion mLinkRegion;
+ private final ResourceType mType;
+ private final String mName;
+ private final ResourceFile mFile;
+
+ /**
+ * Constructs a new {@link ResourceLink}.
+ *
+ * @param linkText the description of the link to be shown in a popup when there
+ * is more than one match
+ * @param linkRegion the region corresponding to the link source highlight
+ * @param file the target resource file containing the link definition
+ * @param type the type of resource being linked to
+ * @param name the name of the resource being linked to
+ */
+ public ResourceLink(String linkText, IRegion linkRegion, ResourceFile file,
+ ResourceType type, String name) {
+ super();
+ mLinkText = linkText;
+ mLinkRegion = linkRegion;
+ mType = type;
+ mName = name;
+ mFile = file;
+ }
+
+ public IRegion getHyperlinkRegion() {
+ return mLinkRegion;
+ }
+
+ public String getHyperlinkText() {
+ // return "Open XML Declaration";
+ return mLinkText;
+ }
+
+ public String getTypeLabel() {
+ return null;
+ }
+
+ public void open() {
+ // We have to defer computation of ids until the link is clicked since we
+ // don't have a fast map lookup for these
+ if (mFile == null && mType == ResourceType.ID) {
+ // Id's are handled specially because they are typically defined
+ // inline (though they -can- be defined in the values folder above as well,
+ // in which case we will prefer that definition)
+ IProject project = getProject();
+ Pair<IFile,IRegion> def = findIdDefinition(project, mName);
+ if (def != null) {
+ try {
+ openFile(def.getFirst(), def.getSecond());
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, null);
+ }
+ return;
}
+
+ displayError(String.format("Could not find id %1$s", mName));
return;
}
- if (!Hyperlinks.open(mXmlContext)) {
- // Failed: display message to the user
- String message = String.format("Could not open link");
- IEditorSite editorSite = getEditor().getEditorSite();
- IStatusLineManager status = editorSite.getActionBars().getStatusLineManager();
- status.setErrorMessage(message);
+ IAbstractFile wrappedFile = mFile.getFile();
+ if (wrappedFile instanceof IFileWrapper) {
+ IFile file = ((IFileWrapper) wrappedFile).getIFile();
+ try {
+ // Lazily search for the target?
+ IRegion region = null;
+ if (mType != null && mName != null && EXT_XML.equals(file.getFileExtension())) {
+ Pair<IFile, IRegion> target = findValueInXml(mType, mName, file);
+ if (target != null) {
+ region = target.getSecond();
+ }
+ }
+ openFile(file, region);
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, null);
+ }
+ } else if (wrappedFile instanceof FileWrapper) {
+ File file = ((FileWrapper) wrappedFile);
+ IPath path = new Path(file.getAbsolutePath());
+ int offset = 0;
+ // Lazily search for the target?
+ if (mType != null && mName != null && EXT_XML.equals(path.getFileExtension())) {
+ if (file.exists()) {
+ Pair<File, Integer> target = findValueInXml(mType, mName, file);
+ if (target != null && target.getSecond() != null) {
+ offset = target.getSecond();
+ }
+ }
+ }
+ openPath(path, null, offset);
+ } else {
+ throw new IllegalArgumentException("Invalid link parameters");
}
}
}