From 2b9f07885147caab17c5fc3acb916b10a3c6273d Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Thu, 24 May 2012 13:38:57 -0700 Subject: Make Go To Declaration work for theme references Also fix a couple of NPEs. Change-Id: Iab4544d3538d370dd76d96e3a97d49555b7c6359 --- .../eclipse/adt/internal/editors/Hyperlinks.java | 71 ++++++++++++++-------- .../internal/editors/layout/gle2/OutlinePage.java | 3 +- .../adt/internal/editors/HyperlinksTest.java | 8 ++- .../testdata/navigation1-expected-navigate15.txt | 7 +++ .../layout/refactoring/testdata/navigation1.xml | 1 + 5 files changed, 63 insertions(+), 27 deletions(-) create mode 100644 eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java index 6e035af..c63ed18 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/Hyperlinks.java @@ -16,14 +16,15 @@ package com.android.ide.eclipse.adt.internal.editors; -import static com.android.util.XmlUtils.ANDROID_URI; import static com.android.ide.common.layout.LayoutConstants.ATTR_CLASS; import static com.android.ide.common.layout.LayoutConstants.ATTR_NAME; import static com.android.ide.common.layout.LayoutConstants.ATTR_ON_CLICK; import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX; import static com.android.ide.common.layout.LayoutConstants.VIEW; import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF; +import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_THEME_REF; import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF; +import static com.android.ide.common.resources.ResourceResolver.PREFIX_THEME_REF; import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG; import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML; import static com.android.ide.eclipse.adt.AdtConstants.FN_RESOURCE_BASE; @@ -38,6 +39,10 @@ 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 static com.android.tools.lint.detector.api.LintConstants.ANDROID_STYLE_RESOURCE_PREFIX; +import static com.android.tools.lint.detector.api.LintConstants.NEW_ID_RESOURCE_PREFIX; +import static com.android.tools.lint.detector.api.LintConstants.STYLE_RESOURCE_PREFIX; +import static com.android.util.XmlUtils.ANDROID_URI; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -210,14 +215,14 @@ public class Hyperlinks { } String value = attribute.getValue(); - if (value.startsWith("@+")) { //$NON-NLS-1$ + if (value.startsWith(NEW_ID_RESOURCE_PREFIX)) { // It's a value -declaration-, nowhere else to jump // (though we could consider jumping to the R-file; would that // be helpful?) return false; } - Pair resource = ResourceHelper.parseResource(value); + Pair resource = parseResource(value); if (resource != null) { ResourceType type = resource.getFirst(); if (type != null) { @@ -1026,23 +1031,28 @@ public class Hyperlinks { if (type == ResourceType.ID) { // Ids are recorded in tags instead of tags targetTag = "item"; //$NON-NLS-1$ - } else if (type == ResourceType.ATTR) { + } + + Pair result = findTag(name, file, parser, document, targetTag); + if (result == null && type == ResourceType.ATTR) { // Attributes seem to be defined in tags targetTag = "public"; //$NON-NLS-1$ + result = findTag(name, file, parser, document, targetTag); } - Element root = document.getDocumentElement(); - if (root.getTagName().equals(ROOT_ELEMENT)) { - NodeList children = root.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) child; - if (element.getTagName().equals(targetTag)) { - String elementName = element.getAttribute(NAME_ATTR); - if (elementName.equals(name)) { + return result; + } - return Pair.of(file, parser.getOffset(element)); - } + private static Pair findTag(String name, File file, OffsetTrackingParser parser, + Document document, String targetTag) { + NodeList children = document.getElementsByTagName(targetTag); + for (int i = 0, n = children.getLength(); i < n; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) child; + if (element.getTagName().equals(targetTag)) { + String elementName = element.getAttribute(NAME_ATTR); + if (elementName.equals(name)) { + return Pair.of(file, parser.getOffset(element)); } } } @@ -1067,15 +1077,17 @@ public class Hyperlinks { } } - Pair resource = ResourceHelper.parseResource(url); + Pair resource = parseResource(url); if (resource == null) { - String androidStyle = "@android:style/"; //$NON-NLS-1$ + String androidStyle = ANDROID_STYLE_RESOURCE_PREFIX; if (url.startsWith(PREFIX_ANDROID_RESOURCE_REF)) { url = androidStyle + url.substring(PREFIX_ANDROID_RESOURCE_REF.length()); + } else if (url.startsWith(PREFIX_ANDROID_THEME_REF)) { + url = androidStyle + url.substring(PREFIX_ANDROID_THEME_REF.length()); } else if (url.startsWith(ANDROID_PKG + ':')) { url = androidStyle + url.substring(ANDROID_PKG.length() + 1); } else { - url = "@style/" + url; //$NON-NLS-1$ + url = STYLE_RESOURCE_PREFIX + url; } } return getResourceLinks(range, url); @@ -1087,6 +1099,16 @@ public class Hyperlinks { return getResourceLinks(range, url, project, configuration); } + /** Parse a resource reference or a theme reference and return the individual parts */ + private static Pair parseResource(String url) { + if (url.startsWith(PREFIX_THEME_REF)) { + url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length()); + return ResourceHelper.parseResource(url); + } + + return ResourceHelper.parseResource(url); + } + /** * Computes hyperlinks to resource definitions for resource urls (e.g. * {@code @android:string/ok} or {@code @layout/foo}. May create multiple links. @@ -1101,14 +1123,15 @@ public class Hyperlinks { @NonNull IProject project, @Nullable FolderConfiguration configuration) { List links = new ArrayList(); - Pair resource = ResourceHelper.parseResource(url); + Pair 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$ + boolean isFramework = url.startsWith(PREFIX_ANDROID_RESOURCE_REF) + || url.startsWith(PREFIX_ANDROID_THEME_REF); if (project == null) { // Local reference *within* a framework isFramework = true; @@ -1217,10 +1240,10 @@ public class Hyperlinks { return getStyleLinks(context, range, attribute.getValue()); } if (attribute != null - && attribute.getValue().startsWith(PREFIX_RESOURCE_REF)) { + && (attribute.getValue().startsWith(PREFIX_RESOURCE_REF) + || attribute.getValue().startsWith(PREFIX_THEME_REF))) { // 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); } @@ -1253,7 +1276,7 @@ public class Hyperlinks { int offset = caretOffset; while (offset > lineStart) { char c = document.getChar(offset); - if (c == '@') { + if (c == '@' || c == '?') { urlStart = offset; break; } else if (!isValidResourceUrlChar(c)) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java index 3524970..9261aff 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java @@ -736,7 +736,8 @@ public class OutlinePage extends ContentOutlinePage // Temporary diagnostics code when developing GridLayout if (GridLayoutRule.sDebugGridLayout) { String namespace; - if (e.getParentNode().getNodeName().equals(GRID_LAYOUT)) { + if (e.getParentNode() != null + && e.getParentNode().getNodeName().equals(GRID_LAYOUT)) { namespace = ANDROID_URI; } else { IProject project = mGraphicalEditorPart.getProject(); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java index 0d42357..a54376d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java @@ -19,8 +19,6 @@ import static com.android.sdklib.SdkConstants.FD_SOURCES; import com.android.ide.common.resources.ResourceFile; import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.Hyperlinks; import com.android.ide.eclipse.adt.internal.editors.Hyperlinks.ResourceLink; import com.android.ide.eclipse.adt.internal.editors.Hyperlinks.XmlResolver; import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest; @@ -180,6 +178,12 @@ public class HyperlinksTest extends AdtProjectTest { "class=\"com.and^roid.eclipse.tests.TestFragment\""); } + public void testNavigate15() throws Exception { + // Check navigating to a theme resource + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "?android:attr/alert^DialogStyle"); + } + // Left to test: // onClick handling // class attributes diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt new file mode 100644 index 0000000..e36c5f3 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1-expected-navigate15.txt @@ -0,0 +1,7 @@ +Go To Declaration in navigation1.xml for ?android:attr/alert^DialogStyle: +Open Declaration in values/attrs.xml : [?android:attr/alertDialogStyle] + data/res/values/attrs.xml + + +After open, the selected text is: + ^ diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml index 9c175fc..e7ac4bc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/navigation1.xml @@ -13,4 +13,5 @@ + -- cgit v1.1