# HG changeset patch
# Parent 8261224fd6078bd4a922685f57e8bae5f8995c0c
diff --git a/ant.grammar/nbproject/project.xml b/ant.grammar/nbproject/project.xml
--- a/ant.grammar/nbproject/project.xml
+++ b/ant.grammar/nbproject/project.xml
@@ -100,7 +100,7 @@
2
- 1.9.0
+ 1.28.0
diff --git a/ant.grammar/src/org/netbeans/modules/ant/grammar/AntGrammar.java b/ant.grammar/src/org/netbeans/modules/ant/grammar/AntGrammar.java
--- a/ant.grammar/src/org/netbeans/modules/ant/grammar/AntGrammar.java
+++ b/ant.grammar/src/org/netbeans/modules/ant/grammar/AntGrammar.java
@@ -45,6 +45,7 @@
package org.netbeans.modules.ant.grammar;
import java.io.IOException;
+import java.net.URI;
import java.net.URL;
import java.text.Collator;
import java.util.ArrayList;
@@ -66,6 +67,7 @@
import org.netbeans.modules.xml.api.model.GrammarResult;
import org.netbeans.modules.xml.api.model.HintContext;
import org.netbeans.modules.xml.spi.dom.AbstractNode;
+import org.netbeans.modules.xml.api.model.DescriptionSource;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.URLMapper;
import org.openide.util.Enumerations;
@@ -395,24 +397,22 @@
case PROJECT:
case TARGET:
list.add(new MyElement(element) {
- @Override public String getDescription() {
+ private URL manpage;
+
+ {
ClassLoader cl = Lookup.getDefault().lookup(ClassLoader.class);
- URL manpage = cl.getResource("org/apache/tools/ant/module/docs/ant-docs/Tasks/" + element + ".html");
+ manpage = cl.getResource("org/apache/tools/ant/module/docs/ant-docs/Tasks/" + element + ".html");
if (manpage == null) {
manpage = cl.getResource("org/apache/tools/ant/module/docs/ant-docs/Types/" + element + ".html");
}
- if (manpage != null) {
- FileObject f = URLMapper.findFileObject(manpage);
- if (f != null) {
- try {
- return new String(f.asBytes(), FileEncodingQuery.getEncoding(f));
- } catch (IOException x) {
- LOG.log(Level.INFO, "Could not load " + manpage, x);
- }
- }
- }
- return null;
}
+
+ @Override
+ public URL getContentURL() {
+ return manpage;
+ }
+
+
});
break;
default:
@@ -827,7 +827,7 @@
}
- private static class MyElement extends AbstractResultNode implements Element {
+ private static class MyElement extends AbstractResultNode implements Element, DescriptionSource {
private String name;
@@ -850,7 +850,21 @@
public @Override String toString() {
return name;
}
+
+ @Override
+ public DescriptionSource resolveLink(String link) {
+ return null;
+ }
+
+ @Override
+ public boolean isExternal() {
+ return false;
+ }
+ @Override
+ public URL getContentURL() {
+ return null;
+ }
}
private static class MyAttr extends AbstractResultNode implements Attr {
diff --git a/xml.core/nbproject/project.properties b/xml.core/nbproject/project.properties
--- a/xml.core/nbproject/project.properties
+++ b/xml.core/nbproject/project.properties
@@ -42,7 +42,7 @@
javac.compilerargs=-Xlint -Xlint:-serial
javac.source=1.6
-spec.version.base=1.27.0
+spec.version.base=1.28.0
is.autoload=true
javadoc.packages=\
diff --git a/xml.core/nbproject/project.xml b/xml.core/nbproject/project.xml
--- a/xml.core/nbproject/project.xml
+++ b/xml.core/nbproject/project.xml
@@ -50,6 +50,15 @@
org.netbeans.modules.xml.core
+ org.netbeans.api.annotations.common
+
+
+
+ 1
+ 1.11
+
+
+
org.netbeans.api.xml
diff --git a/xml.core/src/org/netbeans/modules/xml/api/model/DescriptionSource.java b/xml.core/src/org/netbeans/modules/xml/api/model/DescriptionSource.java
new file mode 100644
--- /dev/null
+++ b/xml.core/src/org/netbeans/modules/xml/api/model/DescriptionSource.java
@@ -0,0 +1,106 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.xml.api.model;
+
+import java.net.URI;
+import java.net.URL;
+import org.netbeans.api.annotations.common.CheckForNull;
+
+/**
+ * Optional mixin interface, which can be implemented together with the
+ * {@link GrammarResult} interface on the returned completion item.
+ * It allows to resolve links and provide description contents.
+ *
+ * Implementation may return {@code null} from {@link #getDescription} to
+ * indicate that the system should load contents from the URL supplied by {@link #getContentURL()}.
+ *
+ * The implementation may choose to provide the content URL to open within the IDE
+ * or in an external browser. Any links will be resolved relatively to that URL.
+ * For special cases, the implementation may resolve a link to another instance
+ * of DescriptionSource by implementing {@link #resolveLink}.
+ *
+ * If both {@link #getDescription} and {@link #getContentURL} return null, there's
+ * no description at all for the {@link GrammarResult}.
+ *
+ * @author sdedic
+ * @since 1.28
+ */
+public interface DescriptionSource {
+ /**
+ * Provides text of the description. If the method returns null, the
+ * IDE will try to load contents of the {@link #getContentURL()} and use that
+ * as a description.
+ *
+ * @return Description contents or {@code null}, if contents should be loaded
+ * from {@link #getContentURL}.
+ */
+ @CheckForNull
+ public String getDescription();
+
+ /**
+ * True, if the description can be opened by an external browser, following the
+ * {@link #getContentURL}.
+ *
+ * @return true, if the content URL can be used outside of the IDE
+ */
+ public boolean isExternal();
+
+ /**
+ * Returns URL for the content, so it can be retrieved. If this URL is provided,
+ * any link not resolved by {@link #resolveLink} will be treated
+ * as relative to this content URL.
+ *
+ * @return URL of the description content
+ */
+ @CheckForNull
+ public URL getContentURL();
+
+ /**
+ * Resolves a link in the documentation to another DescriptionSource. Can
+ * return null, if the link cannot be resolved.
+ *
+ * @param link link found in the text
+ * @return resolved description, or {@code null}
+ */
+ @CheckForNull
+ public DescriptionSource resolveLink(String link);
+}
diff --git a/xml.core/src/org/netbeans/modules/xml/api/model/GrammarResult.java b/xml.core/src/org/netbeans/modules/xml/api/model/GrammarResult.java
--- a/xml.core/src/org/netbeans/modules/xml/api/model/GrammarResult.java
+++ b/xml.core/src/org/netbeans/modules/xml/api/model/GrammarResult.java
@@ -46,6 +46,7 @@
import org.w3c.dom.Node;
import javax.swing.Icon;
+import org.netbeans.api.annotations.common.CheckForNull;
/**
* It represents additonal properties of a result option.
@@ -77,9 +78,28 @@
String getDisplayName();
/**
+ * Returns contents of a description, text suitable for displaying as a tooltip
+ * that simplifies decision. {@code null} may be returned if no description is available.
+ * The decription is interpreted as HTML markup. If the markup contains relative
+ * links or special URIs, implement also {@link DescriptionSource} to resolve
+ * those links.
+ *
+ * If {@link DescriptionSource} is implemented on the same object, this method
+ * may return null to indicate the content should be loaded by the infrastructure
+ * from the URL returned by {@link DescriptionSource#getContentURL()}. If both
+ * {@code getDescription()} and {@code DescriptionSource.getContentURL()} return
+ * null, no description is displayed in the tooltip.
+ *
+ * Implementors may prefer implementing the {@code DescriptionSource} and
+ * loading from the {@link DescriptionSource#getContentURL()} if the
+ * description resides in a separate file included in the JAR or in the XML layer.
+ *
* @return provide additional information simplifing decision
- * (suitable for tooltip) or null
+ * (suitable for tooltip) or {@code null}.
+ *
+ * @since 1.28 - DescriptionSource extension
*/
+ @CheckForNull
String getDescription();
/**
diff --git a/xml.text/nbproject/project.xml b/xml.text/nbproject/project.xml
--- a/xml.text/nbproject/project.xml
+++ b/xml.text/nbproject/project.xml
@@ -184,12 +184,21 @@
+ org.netbeans.modules.queries
+
+
+
+ 1
+ 1.25
+
+
+
org.netbeans.modules.xml.core
2
- 1.14
+ 1.28
diff --git a/xml.text/src/org/netbeans/modules/xml/text/completion/XMLResultItem.java b/xml.text/src/org/netbeans/modules/xml/text/completion/XMLResultItem.java
--- a/xml.text/src/org/netbeans/modules/xml/text/completion/XMLResultItem.java
+++ b/xml.text/src/org/netbeans/modules/xml/text/completion/XMLResultItem.java
@@ -49,7 +49,11 @@
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.net.MalformedURLException;
import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.text.*;
import javax.swing.Icon;
@@ -57,13 +61,18 @@
import org.netbeans.editor.*;
import javax.swing.JLabel;
import org.netbeans.api.editor.completion.Completion;
+import org.netbeans.api.queries.FileEncodingQuery;
import org.netbeans.modules.xml.api.model.GrammarResult;
+import org.netbeans.modules.xml.api.model.DescriptionSource;
import org.netbeans.modules.xml.text.api.XMLDefaultTokenContext;
import org.netbeans.modules.xml.text.syntax.XMLSyntaxSupport;
import org.netbeans.spi.editor.completion.CompletionDocumentation;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.netbeans.spi.editor.completion.CompletionResultSet;
import org.netbeans.spi.editor.completion.CompletionTask;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
/**
* This class carries result information required by NetBeans Editor module.
@@ -72,6 +81,7 @@
* @author Sandeep Randhawa
*/
class XMLResultItem implements CompletionItem {
+ private static final Logger LOG = Logger.getLogger(XMLResultItem.class.getName());
// text to be diplayed to user
public final String displayText;
@@ -304,25 +314,160 @@
protected CompletionTask doCreateDocumentationTask(final GrammarResult res) {
return new CompletionTask() {
public void query(CompletionResultSet resultSet) {
- if (res != null && res.getDescription() != null) {
- resultSet.setDocumentation(new Docum(res.getDescription()));
-
+ CompletionDocumentation cd = create();
+ if (cd != null) {
+ resultSet.setDocumentation(cd);
}
resultSet.finish();
}
public void refresh(CompletionResultSet resultSet) {
- if (res != null && res.getDescription() != null) {
- resultSet.setDocumentation(new Docum(res.getDescription()));
- }
- resultSet.finish();
+ query(resultSet);
}
public void cancel() {}
+
+
+ private CompletionDocumentation create() {
+ String doc;
+
+ doc = res.getDescription();
+ if (!(res instanceof DescriptionSource)) {
+ if (doc == null) {
+ return null;
+ }
+ return new Docum(doc);
+ } else {
+ DescriptionSource ds = (DescriptionSource)res;
+ if (doc == null && ds.getContentURL() == null) {
+ return null;
+ }
+ return new ExtDocum(ds, doc);
+ }
+ }
};
}
+
+ /**
+ * Extended documentation, based on the {@link DescriptionSource} SPI.
+ */
+ private static class ExtDocum extends URLDocum implements CompletionDocumentation {
+ private DescriptionSource src;
+ private String doc;
+
+ ExtDocum(DescriptionSource src, String doc) {
+ super(src.getContentURL(), src.isExternal());
+ this.src = src;
+ this.doc = doc;
+ }
+
+ @Override
+ public String getText() {
+ if (doc == null) {
+ doc = src.getDescription();
+ if (doc == null) {
+ doc = super.getText();
+ }
+ }
+ return doc;
+ }
+
+ @Override
+ public CompletionDocumentation resolveLink(String link) {
+ try {
+ DescriptionSource target = src.resolveLink(link);
+ if (target != null) {
+ return new ExtDocum(target, null);
+ }
+
+ URL base = src.getContentURL();
+ if (base == null) {
+ // sorry, cannot resolve.
+ return null;
+ }
+
+ URL targetURL = new URL(base, link);
+
+ // leave the VM as soon as possible. This hack uses URLMappers
+ // to find out whether URL (converted to FO and back) can be
+ // represented outside the VM
+ boolean external = true;
+ FileObject f = URLMapper.findFileObject(targetURL);
+ if (f != null) {
+ external = URLMapper.findURL(f, URLMapper.EXTERNAL) != null;
+ }
+ return new URLDocum(targetURL, external);
+ } catch (MalformedURLException ex) {
+ Exceptions.printStackTrace(ex);
+ return null;
+ }
+ }
+
+ @Override
+ public Action getGotoSourceAction() {
+ return null;
+ }
+ }
+
+ /**
+ * Pure URL documentation item. Resolves links, if original URL was able
+ * to open externally, the derived URLs do it as well.
+ */
+ private static class URLDocum implements CompletionDocumentation {
+ URL content;
+ boolean external;
+
+ URLDocum(URL content, boolean external) {
+ this.content = content;
+ this.external = external;
+ }
+
+ URLDocum(boolean external) {
+ this.external = external;
+ }
+
+ @Override
+ public Action getGotoSourceAction() {
+ return null;
+ }
+
+ @Override
+ public String getText() {
+ if (content == null) {
+ return null;
+ }
+ FileObject f = URLMapper.findFileObject(content);
+ if (f != null) {
+ try {
+ return new String(f.asBytes(), FileEncodingQuery.getEncoding(f));
+ } catch (IOException x) {
+ LOG.log(Level.INFO, "Could not load " + content, x);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public URL getURL() {
+ return external ? content : null;
+ }
+
+ @Override
+ public CompletionDocumentation resolveLink(String link) {
+ if (content == null) {
+ return null;
+ }
+ try {
+ return new URLDocum(new URL(content, link), external);
+ } catch (MalformedURLException ex) {
+ Exceptions.printStackTrace(ex);
+ return null;
+ }
+ }
+
+ }
private static class Docum implements CompletionDocumentation {
private String doc;
-
+
private Docum(String doc) {
this.doc = doc;
}
diff --git a/xml.text/test/unit/src/org/netbeans/modules/xml/text/completion/ElementResultItemDocumentationTest.java b/xml.text/test/unit/src/org/netbeans/modules/xml/text/completion/ElementResultItemDocumentationTest.java
new file mode 100644
--- /dev/null
+++ b/xml.text/test/unit/src/org/netbeans/modules/xml/text/completion/ElementResultItemDocumentationTest.java
@@ -0,0 +1,382 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.xml.text.completion;
+
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import javax.swing.Icon;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.junit.NbTestSuite;
+
+import org.netbeans.modules.editor.completion.CompletionImpl;
+import org.netbeans.modules.editor.completion.CompletionResultSetImpl;
+import org.netbeans.modules.xml.api.EncodingUtil;
+import org.netbeans.modules.xml.api.model.DescriptionSource;
+import org.netbeans.modules.xml.api.model.GrammarResult;
+import org.netbeans.modules.xml.spi.dom.AbstractNode;
+import org.netbeans.spi.editor.completion.CompletionDocumentation;
+import org.netbeans.spi.editor.completion.CompletionProvider;
+import org.netbeans.spi.editor.completion.CompletionResultSet;
+import org.netbeans.spi.editor.completion.CompletionTask;
+
+/**
+ * Checks behaviour of {@link GrammarResult} support in the ElementResultItem.
+ * plain CompletionDocumentation must work unchanged. If mixin interface is implemented,
+ * explicit values (content, resolved links) must take precedence.
+ *
+ * If contentURL is provided, links must be resolved and content loaded by the
+ * ElementResultItem support even though the supplied CompletionDocumentation itself does not
+ * provide content/links.
+ *
+ * @author sdedic
+ */
+public class ElementResultItemDocumentationTest extends NbTestCase {
+
+ public static NbTestSuite suite() {
+ NbTestSuite suite = new NbTestSuite();
+ suite.addTestSuite(ElementResultItemDocumentationTest.class);
+ return suite;
+ }
+
+ public ElementResultItemDocumentationTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private CompletionResultSetImpl rsImpl;
+
+
+ private CompletionResultSet resultSetFor(CompletionTask task, int type) throws Exception {
+ rsImpl = createCompletionResultImpl(task, type);
+ return rsImpl.getResultSet();
+ }
+
+ private CompletionResultSetImpl createCompletionResultImpl(CompletionTask task, int type) throws Exception {
+ Class clazz = CompletionResultSetImpl.class;
+ Constructor ctor = clazz.getDeclaredConstructor(CompletionImpl.class, Object.class, CompletionTask.class, Integer.TYPE);
+ ctor.setAccessible(true);
+ return (CompletionResultSetImpl)ctor.newInstance(CompletionImpl.get(), "", task, type);
+ }
+
+ /**
+ * Check the previou state, result items without the mixin interface.
+ * Should return the description 'as is'
+ *
+ * @throws Exception
+ */
+ private static final String PLAIN_DESCRIPTION_TEXT = "Plain description";
+
+ public void testPlainContent() throws Exception {
+ MockGrammarResult r = new MockGrammarResult();
+
+ r.setDescription(PLAIN_DESCRIPTION_TEXT);
+ ElementResultItem item = new ElementResultItem(0, r);
+ CompletionTask task = item.doCreateDocumentationTask(r);
+
+ CompletionResultSet rs = resultSetFor(task, CompletionProvider.DOCUMENTATION_QUERY_TYPE);
+ task.query(rs);
+
+ assertTrue(rs.isFinished());
+
+ assertEquals(PLAIN_DESCRIPTION_TEXT, rsImpl.getDocumentation().getText());
+
+ // should return null from the url.
+ assertNull(rsImpl.getDocumentation().getURL());
+
+ // should not be able to resolve links
+ assertNull(rsImpl.getDocumentation().resolveLink("link"));
+ }
+
+ private URL createResourceName(String n) {
+ return ElementResultItemDocumentationTest.class.getResource(n);
+ }
+
+ private CompletionDocumentation createDocResourceResultSet(MockUrlGrammarResult r) throws Exception {
+ if (r == null) {
+ r = new MockUrlGrammarResult();
+ r.setContentURL(createResourceName("res/docResource.html"));
+ r.setExternal(true);
+ }
+
+ ElementResultItem item = new ElementResultItem(0, r);
+ CompletionTask task = item.doCreateDocumentationTask(r);
+ CompletionResultSet rs = resultSetFor(task, CompletionProvider.DOCUMENTATION_QUERY_TYPE);
+ task.query(rs);
+
+ assertTrue(rs.isFinished());
+
+ return rsImpl.getDocumentation();
+ }
+
+ /**
+ * Checks that custom contents overrides the one supplied by XMLResultItem
+ *
+ * @throws Exception
+ */
+ public void testCustomContent() throws Exception {
+ URL res = EncodingUtil.class.getResource("/org/netbeans/modules/xml/core/resources/Bundle.properties");
+ MockUrlGrammarResult test = new MockUrlGrammarResult();
+ test.setContentURL(res);
+ test.setExternal(false);
+ test.setDescription(PLAIN_DESCRIPTION_TEXT);
+
+ CompletionDocumentation doc = createDocResourceResultSet(test);
+ assertNull(doc.getURL());
+ assertEquals("Invalid content", PLAIN_DESCRIPTION_TEXT, doc.getText());
+ }
+
+ /**
+ * Checks documentation bundled in a JAR - not openable by external browser.
+ * Need a resource within a JAR
+ *
+ * @throws Exception
+ */
+ public void testInternalUrlDocumentation() throws Exception {
+ URL res = EncodingUtil.class.getResource("/org/netbeans/modules/xml/core/resources/Bundle.properties");
+ MockUrlGrammarResult test = new MockUrlGrammarResult();
+ test.setContentURL(res);
+ test.setExternal(false);
+
+ CompletionDocumentation doc = createDocResourceResultSet(test);
+ assertNull(doc.getURL());
+ assertTrue(doc.getText().contains("OpenIDE-Module-Name=XML Core"));
+
+ // check that relative links still resolve to internal doc
+ CompletionDocumentation internal = doc.resolveLink("mf-layer.xml");
+ assertNotNull("Relative links must be resolvable", internal);
+ assertNull("Relative links cannot be opened in extbrowser", doc.getURL());
+ assertTrue("Content must be accessible",
+ internal.getText().contains("org-netbeans-modules-xml-dtd-grammar-DTDGrammarQueryProvider.instance"));
+
+ // check that absolute links resolve to external doc
+ CompletionDocumentation external = doc.resolveLink("http://www.netbeans.org");
+ assertNotNull("Absolute links must be resolvable", external);
+ assertEquals("Absolute links must have URL", external.getURL(), new URL("http://www.netbeans.org"));
+ }
+
+ /**
+ * Checks that file-based documentation reverts to external as soon as possible
+ * @throws Exception
+ */
+ public void testFileDocumentation() throws Exception {
+ URL res = EncodingUtil.class.getResource("/org/netbeans/modules/xml/core/resources/Bundle.properties");
+ MockUrlGrammarResult test = new MockUrlGrammarResult();
+ test.setContentURL(res);
+ test.setExternal(false);
+
+ CompletionDocumentation doc = createDocResourceResultSet(test);
+ assertNull(doc.getURL());
+ assertTrue("Invalid content", doc.getText().contains("OpenIDE-Module-Name=XML Core"));
+
+ // check that resolve of file-based URL turns the doc to external:
+
+ URL url = createResourceName("res/docResource.html");
+ CompletionDocumentation file = doc.resolveLink(url.toString());
+ assertNotNull(file);
+ assertEquals("URL must be openable in browser", url, file.getURL());
+ assertTrue("Invalid content of the linked doc", file.getText().startsWith("This is an URL resource with relative link and
+absolute link
diff --git a/xml.text/test/unit/src/org/netbeans/modules/xml/text/completion/res/relativeLink1.html b/xml.text/test/unit/src/org/netbeans/modules/xml/text/completion/res/relativeLink1.html
new file mode 100644
--- /dev/null
+++ b/xml.text/test/unit/src/org/netbeans/modules/xml/text/completion/res/relativeLink1.html
@@ -0,0 +1,1 @@
+Resource referenced by relative link
\ No newline at end of file