提交 4198f3cb 编写于 作者: J jlahoda

8131019: jshell tool: access javadoc from tool

Summary: Adding internal support to resolve {@inheritDoc} and format javadoc to plain text for use by jdk.jshell and jdk.scripting.nashorn.shell, enhancing Shift-<tab> documentation in JShell with ability to show javadoc.
Reviewed-by: jjg, rfield
上级 50ac6548
......@@ -297,17 +297,21 @@ public enum Entity {
rsaquo(8250),
euro(8364);
int code;
public final int code;
private Entity(int code) {
this.code = code;
}
static boolean isValid(String name) {
public static boolean isValid(String name) {
return names.containsKey(name);
}
static boolean isValid(int code) {
public static Entity get(String name) {
return names.get(name);
}
public static boolean isValid(int code) {
// allow numeric codes for standard ANSI characters
return codes.containsKey(code) || ( 32 <= code && code < 2127);
}
......
#
# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
CAP_TypeParameters=Type Parameters:
CAP_Parameters=Parameters:
CAP_Returns=Returns:
CAP_Thrown_Exceptions=Thrown Exceptions:
......@@ -65,6 +65,9 @@ module jdk.compiler {
jdk.jdeps,
jdk.javadoc,
jdk.jshell;
exports jdk.internal.shellsupport.doc to
jdk.jshell,
jdk.scripting.nashorn.shell;
uses javax.annotation.processing.Processor;
uses com.sun.source.util.Plugin;
......
......@@ -25,6 +25,7 @@
package jdk.internal.jshell.tool;
import jdk.jshell.SourceCodeAnalysis.Documentation;
import jdk.jshell.SourceCodeAnalysis.QualifiedNames;
import jdk.jshell.SourceCodeAnalysis.Suggestion;
......@@ -34,27 +35,30 @@ import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.function.Function;
import java.util.prefs.BackingStoreException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.shellsupport.doc.JavadocFormatter;
import jdk.internal.jline.NoInterruptUnixTerminal;
import jdk.internal.jline.Terminal;
import jdk.internal.jline.TerminalFactory;
import jdk.internal.jline.TerminalSupport;
import jdk.internal.jline.WindowsTerminal;
import jdk.internal.jline.console.ConsoleReader;
import jdk.internal.jline.console.CursorBuffer;
import jdk.internal.jline.console.KeyMap;
import jdk.internal.jline.console.UserInterruptException;
import jdk.internal.jline.console.completer.Completer;
......@@ -259,22 +263,118 @@ class ConsoleIOContext extends IOContext {
"\u001BO3P" //Alt-F1 (Linux)
};
private String lastDocumentationBuffer;
private int lastDocumentationCursor = (-1);
private void documentation(JShellTool repl) {
String buffer = in.getCursorBuffer().buffer.toString();
int cursor = in.getCursorBuffer().cursor;
String doc;
boolean firstInvocation = !buffer.equals(lastDocumentationBuffer) || cursor != lastDocumentationCursor;
lastDocumentationBuffer = buffer;
lastDocumentationCursor = cursor;
List<String> doc;
String seeMore;
Terminal term = in.getTerminal();
if (prefix.isEmpty() && buffer.trim().startsWith("/")) {
doc = repl.commandDocumentation(buffer, cursor);
doc = Arrays.asList(repl.commandDocumentation(buffer, cursor, firstInvocation));
seeMore = "jshell.console.see.help";
} else {
doc = repl.analysis.documentation(prefix + buffer, cursor + prefix.length());
JavadocFormatter formatter = new JavadocFormatter(term.getWidth(),
term.isAnsiSupported());
Function<Documentation, String> convertor;
if (firstInvocation) {
convertor = d -> d.signature();
} else {
convertor = d -> formatter.formatJavadoc(d.signature(),
d.javadoc() != null ? d.javadoc()
: repl.messageFormat("jshell.console.no.javadoc"));
}
doc = repl.analysis.documentation(prefix + buffer, cursor + prefix.length(), !firstInvocation)
.stream()
.map(convertor)
.collect(Collectors.toList());
seeMore = "jshell.console.see.javadoc";
}
try {
if (doc != null) {
in.println();
in.println(doc);
in.redrawLine();
in.flush();
if (doc != null && !doc.isEmpty()) {
if (firstInvocation) {
in.println();
in.println(doc.stream().collect(Collectors.joining("\n")));
in.println(repl.messageFormat(seeMore));
in.redrawLine();
in.flush();
} else {
in.println();
int height = term.getHeight();
String lastNote = "";
PRINT_DOC: for (Iterator<String> docIt = doc.iterator(); docIt.hasNext(); ) {
String currentDoc = docIt.next();
String[] lines = currentDoc.split("\n");
int firstLine = 0;
PRINT_PAGE: while (true) {
int toPrint = height - 1;
while (toPrint > 0 && firstLine < lines.length) {
in.println(lines[firstLine++]);
toPrint--;
}
if (firstLine >= lines.length) {
break;
}
lastNote = repl.getResourceString("jshell.console.see.next.page");
in.print(lastNote + ConsoleReader.RESET_LINE);
in.flush();
while (true) {
int r = in.readCharacter();
switch (r) {
case ' ': continue PRINT_PAGE;
case 'q':
case 3:
break PRINT_DOC;
default:
in.beep();
break;
}
}
}
if (docIt.hasNext()) {
lastNote = repl.getResourceString("jshell.console.see.next.javadoc");
in.print(lastNote + ConsoleReader.RESET_LINE);
in.flush();
while (true) {
int r = in.readCharacter();
switch (r) {
case ' ': continue PRINT_DOC;
case 'q':
case 3:
break PRINT_DOC;
default:
in.beep();
break;
}
}
}
}
//clear the "press space" line:
in.getCursorBuffer().buffer.replace(0, buffer.length(), lastNote);
in.getCursorBuffer().cursor = 0;
in.killLine();
in.getCursorBuffer().buffer.append(buffer);
in.getCursorBuffer().cursor = cursor;
in.redrawLine();
in.flush();
}
} else {
in.beep();
}
......
......@@ -1308,7 +1308,7 @@ public class JShellTool implements MessageHandler {
return commandCompletions.completionSuggestions(code, cursor, anchor);
}
public String commandDocumentation(String code, int cursor) {
public String commandDocumentation(String code, int cursor, boolean shortDescription) {
code = code.substring(0, cursor);
int space = code.indexOf(' ');
......@@ -1316,7 +1316,7 @@ public class JShellTool implements MessageHandler {
String cmd = code.substring(0, space);
Command command = commands.get(cmd);
if (command != null) {
return getResourceString(command.helpKey + ".summary");
return getResourceString(command.helpKey + (shortDescription ? ".summary" : ""));
}
}
......
......@@ -145,6 +145,11 @@ jshell.err.the.snippet.cannot.be.used.with.this.command = This command does not
jshell.err.retained.mode.failure = Failure in retained modes (modes cleared) -- {0} {1}
jshell.console.see.more = <press tab to see more>
jshell.console.see.javadoc = <press shift-tab again to see javadoc>
jshell.console.see.help = <press shift-tab again to see detailed help>
jshell.console.see.next.page = -- Press space for next page, Q to quit. --
jshell.console.see.next.javadoc = -- Press space for next javadoc, Q to quit. --
jshell.console.no.javadoc = <no javadoc found>
jshell.console.do.nothing = Do nothing
jshell.console.choice = Choice: \
......
......@@ -506,6 +506,9 @@ public class JShell implements AutoCloseable {
if (!closed) {
closeDown();
executionControl().close();
if (sourceCodeAnalysis != null) {
sourceCodeAnalysis.close();
}
}
}
......
......@@ -63,12 +63,16 @@ public abstract class SourceCodeAnalysis {
public abstract List<Suggestion> completionSuggestions(String input, int cursor, int[] anchor);
/**
* Compute a description/help string for the given user's input.
* Compute documentation for the given user's input. Multiple {@code Documentation} objects may
* be returned when multiple elements match the user's input (like for overloaded methods).
* @param input the snippet the user wrote so far
* @param cursor the current position of the cursors in the given {@code input} text
* @return description/help string for the given user's input
* @param computeJavadoc true if the javadoc for the given input should be computed in
* addition to the signature
* @return the documentations for the given user's input, if multiple elements match the input,
* multiple {@code Documentation} objects are returned.
*/
public abstract String documentation(String input, int cursor);
public abstract List<Documentation> documentation(String input, int cursor, boolean computeJavadoc);
/**
* Infer the type of the given expression. The expression spans from the beginning of {@code code}
......@@ -265,6 +269,26 @@ public abstract class SourceCodeAnalysis {
boolean matchesType();
}
/**
* A documentation for a candidate for continuation of the given user's input.
*/
public interface Documentation {
/**
* The signature of the given element.
*
* @return the signature
*/
String signature();
/**
* The javadoc of the given element.
*
* @return the javadoc, or null if not found or not requested
*/
String javadoc();
}
/**
* List of possible qualified names.
*/
......
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8131019
* @summary Test JavadocHelper
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/jdk.internal.shellsupport.doc
* @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
* @run testng JavadocHelperTest
*/
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.Function;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import javax.lang.model.element.Element;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
import jdk.internal.shellsupport.doc.JavadocHelper;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@Test
public class JavadocHelperTest {
public void testJavadoc() throws Exception {
doTestJavadoc("",
t -> t.getElements().getTypeElement("test.Super"),
"Top level. ");
doTestJavadoc("",
t -> getFirstMethod(t, "test.Super"),
" javadoc1A\n" +
"\n" +
" @param p1 param1A\n" +
" @param p2 param2A\n" +
" @param p3 param3A\n" +
" @throws IllegalStateException exc1A\n" +
" @throws IllegalArgumentException exc2A\n" +
" @throws IllegalAccessException exc3A\n" +
" @return valueA\n");
}
private Element getFirstMethod(JavacTask task, String typeName) {
return ElementFilter.methodsIn(task.getElements().getTypeElement(typeName).getEnclosedElements()).get(0);
}
private Function<JavacTask, Element> getSubTest = t -> getFirstMethod(t, "test.Sub");
public void testInheritNoJavadoc() throws Exception {
doTestJavadoc("",
getSubTest,
" javadoc1A\n" +
"\n" +
" @param p1 param1A\n" +
" @param p2 param2A\n" +
" @param p3 param3A\n" +
" @throws IllegalStateException exc1A\n" +
" @throws IllegalArgumentException exc2A\n" +
" @throws IllegalAccessException exc3A\n" +
" @return valueA\n");
}
public void testInheritFull() throws Exception {
doTestJavadoc(" /**\n" +
" * Prefix {@inheritDoc} suffix.\n" +
" *\n" +
" * @param p1 prefix {@inheritDoc} suffix\n" +
" * @param p2 prefix {@inheritDoc} suffix\n" +
" * @param p3 prefix {@inheritDoc} suffix\n" +
" * @throws IllegalStateException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalArgumentException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalAccessException prefix {@inheritDoc} suffix\n" +
" * @return prefix {@inheritDoc} suffix\n" +
" */\n",
getSubTest,
" Prefix javadoc1 suffix.\n" +
"\n" +
" @param p1 prefix param1 suffix\n" +
" @param p2 prefix param2 suffix\n" +
" @param p3 prefix param3 suffix\n" +
" @throws IllegalStateException prefix exc1 suffix\n" +
" @throws IllegalArgumentException prefix exc2 suffix\n" +
" @throws IllegalAccessException prefix exc3 suffix\n" +
" @return prefix value suffix\n");
}
public void testInheritMissingParam() throws Exception {
doTestJavadoc(" /**\n" +
" * Prefix {@inheritDoc} suffix.\n" +
" *\n" +
" * @param p1 prefix {@inheritDoc} suffix\n" +
" * @param p3 prefix {@inheritDoc} suffix\n" +
" * @throws IllegalStateException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalArgumentException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalAccessException prefix {@inheritDoc} suffix\n" +
" * @return prefix {@inheritDoc} suffix\n" +
" */\n",
getSubTest,
" Prefix javadoc1 suffix.\n" +
"\n" +
" @param p1 prefix param1 suffix\n" +
"@param p2 param2\n" +
" @param p3 prefix param3 suffix\n" +
" @throws IllegalStateException prefix exc1 suffix\n" +
" @throws IllegalArgumentException prefix exc2 suffix\n" +
" @throws IllegalAccessException prefix exc3 suffix\n" +
" @return prefix value suffix\n");
}
public void testInheritMissingFirstParam() throws Exception {
doTestJavadoc(" /**\n" +
" * Prefix {@inheritDoc} suffix.\n" +
" *\n" +
" * @param p2 prefix {@inheritDoc} suffix\n" +
" * @param p3 prefix {@inheritDoc} suffix\n" +
" * @throws IllegalStateException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalArgumentException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalAccessException prefix {@inheritDoc} suffix\n" +
" * @return prefix {@inheritDoc} suffix\n" +
" */\n",
getSubTest,
" Prefix javadoc1 suffix.\n" +
"@param p1 param1\n" +
"\n" +
" @param p2 prefix param2 suffix\n" +
" @param p3 prefix param3 suffix\n" +
" @throws IllegalStateException prefix exc1 suffix\n" +
" @throws IllegalArgumentException prefix exc2 suffix\n" +
" @throws IllegalAccessException prefix exc3 suffix\n" +
" @return prefix value suffix\n");
}
public void testInheritMissingThrows() throws Exception {
doTestJavadoc(" /**\n" +
" * Prefix {@inheritDoc} suffix.\n" +
" *\n" +
" * @param p1 prefix {@inheritDoc} suffix\n" +
" * @param p2 prefix {@inheritDoc} suffix\n" +
" * @param p3 prefix {@inheritDoc} suffix\n" +
" * @throws IllegalStateException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalAccessException prefix {@inheritDoc} suffix\n" +
" * @return prefix {@inheritDoc} suffix\n" +
" */\n",
getSubTest,
" Prefix javadoc1 suffix.\n" +
"\n" +
" @param p1 prefix param1 suffix\n" +
" @param p2 prefix param2 suffix\n" +
" @param p3 prefix param3 suffix\n" +
" @throws IllegalStateException prefix exc1 suffix\n" +
"@throws java.lang.IllegalArgumentException exc2\n" +
" @throws IllegalAccessException prefix exc3 suffix\n" +
" @return prefix value suffix\n");
}
public void testInheritMissingReturn() throws Exception {
doTestJavadoc(" /**\n" +
" * Prefix {@inheritDoc} suffix.\n" +
" *\n" +
" * @param p1 prefix {@inheritDoc} suffix\n" +
" * @param p2 prefix {@inheritDoc} suffix\n" +
" * @param p3 prefix {@inheritDoc} suffix\n" +
" * @throws IllegalStateException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalArgumentException prefix {@inheritDoc} suffix\n" +
" * @throws IllegalAccessException prefix {@inheritDoc} suffix\n" +
" */\n",
getSubTest,
" Prefix javadoc1 suffix.\n" +
"\n" +
" @param p1 prefix param1 suffix\n" +
" @param p2 prefix param2 suffix\n" +
" @param p3 prefix param3 suffix\n" +
" @throws IllegalStateException prefix exc1 suffix\n" +
" @throws IllegalArgumentException prefix exc2 suffix\n" +
" @throws IllegalAccessException prefix exc3 suffix\n" +
"@return value\n");
}
private void doTestJavadoc(String origJavadoc, Function<JavacTask, Element> getElement, String expectedJavadoc) throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
String subClass =
"package test;\n" +
"public class Sub extends Super {\n" +
origJavadoc +
" public String test(int p1, int p2, int p3) throws IllegalStateException, IllegalArgumentException, IllegalAccessException { return null;} \n" +
"}\n";
String superClass =
"package test;\n" +
"/**Top level." +
" */\n" +
"public class Super {\n" +
" /**\n" +
" * javadoc1A\n" +
" *\n" +
" * @param p1 param1A\n" +
" * @param p2 param2A\n" +
" * @param p3 param3A\n" +
" * @throws IllegalStateException exc1A\n" +
" * @throws IllegalArgumentException exc2A\n" +
" * @throws IllegalAccessException exc3A\n" +
" * @return valueA\n" +
" */\n" +
" public String test(int p1, int p2, int p3) throws IllegalStateException, IllegalArgumentException, IllegalAccessException { return null;} \n" +
"}\n";
Path srcZip = Paths.get("src.zip");
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(srcZip))) {
out.putNextEntry(new JarEntry("test/Sub.java"));
out.write(subClass.getBytes());
out.putNextEntry(new JarEntry("test/Super.java"));
out.write(superClass.getBytes());
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
DiagnosticListener<? super JavaFileObject> noErrors = d -> {
if (d.getKind() == Kind.ERROR) {
throw new AssertionError(d.getMessage(null));
}
};
assertTrue(compiler.getTask(null, null, noErrors, Arrays.asList("-d", "."), null, Arrays.asList(new JFOImpl("Super", superClass), new JFOImpl("Sub", subClass))).call());
try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
fm.setLocationFromPaths(StandardLocation.CLASS_PATH, Arrays.asList(Paths.get(".").toAbsolutePath()));
JavacTask task = (JavacTask) compiler.getTask(null, fm, noErrors, null, null, null);
Element el = getElement.apply(task);
try (JavadocHelper helper = JavadocHelper.create(task, Arrays.asList(srcZip))) {
String javadoc = helper.getResolvedDocComment(el);
assertEquals(javadoc, expectedJavadoc);
}
}
}
private static final class JFOImpl extends SimpleJavaFileObject {
private final String code;
public JFOImpl(String name, String code) throws URISyntaxException {
super(new URI("mem:///" + name + ".java"), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return code;
}
}
}
......@@ -23,12 +23,12 @@
/*
* @test
* @bug 8131025 8141092 8153761 8145263
* @bug 8131025 8141092 8153761 8145263 8131019
* @summary Test Completion and Documentation
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.jdeps/com.sun.tools.javap
* @library /tools/lib
* @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
* @build KullaTesting TestingInputStream Compiler
* @run testng CompletionSuggestionTest
......@@ -305,26 +305,26 @@ public class CompletionSuggestionTest extends KullaTesting {
public void testDocumentation() throws Exception {
dontReadParameterNamesFromClassFile();
assertDocumentation("System.getProperty(|",
assertSignature("System.getProperty(|",
"String System.getProperty(String key)",
"String System.getProperty(String key, String def)");
assertEval("char[] chars = null;");
assertDocumentation("new String(chars, |",
assertSignature("new String(chars, |",
"String(char[], int, int)");
assertDocumentation("String.format(|",
assertSignature("String.format(|",
"String String.format(String, Object...)",
"String String.format(java.util.Locale, String, Object...)");
assertDocumentation("\"\".getBytes(\"\"|", "void String.getBytes(int, int, byte[], int)",
assertSignature("\"\".getBytes(\"\"|", "void String.getBytes(int, int, byte[], int)",
"byte[] String.getBytes(String) throws java.io.UnsupportedEncodingException",
"byte[] String.getBytes(java.nio.charset.Charset)");
assertDocumentation("\"\".getBytes(\"\" |", "void String.getBytes(int, int, byte[], int)",
assertSignature("\"\".getBytes(\"\" |", "void String.getBytes(int, int, byte[], int)",
"byte[] String.getBytes(String) throws java.io.UnsupportedEncodingException",
"byte[] String.getBytes(java.nio.charset.Charset)");
}
public void testMethodsWithNoArguments() throws Exception {
dontReadParameterNamesFromClassFile();
assertDocumentation("System.out.println(|",
assertSignature("System.out.println(|",
"void java.io.PrintStream.println()",
"void java.io.PrintStream.println(boolean)",
"void java.io.PrintStream.println(char)",
......@@ -339,6 +339,7 @@ public class CompletionSuggestionTest extends KullaTesting {
public void testErroneous() {
assertCompletion("Undefined.|");
assertSignature("does.not.exist|");
}
public void testClinit() {
......@@ -474,59 +475,63 @@ public class CompletionSuggestionTest extends KullaTesting {
public void testDocumentationOfUserDefinedMethods() {
assertEval("void f() {}");
assertDocumentation("f(|", "void f()");
assertSignature("f(|", "void f()");
assertEval("void f(int i) {}");
assertDocumentation("f(|", "void f()", "void f(int i)");
assertSignature("f(|", "void f()", "void f(int i)");
assertEval("<T> void f(T... ts) {}", DiagCheck.DIAG_WARNING, DiagCheck.DIAG_OK);
assertDocumentation("f(|", "void f()", "void f(int i)", "void <T>f(T... ts)");
assertSignature("f(|", "void f()", "void f(int i)", "void <T>f(T... ts)");
assertEval("class A {}");
assertEval("void f(A a) {}");
assertDocumentation("f(|", "void f()", "void f(int i)", "void <T>f(T... ts)", "void f(A a)");
assertSignature("f(|", "void f()", "void f(int i)", "void <T>f(T... ts)", "void f(A a)");
}
public void testClass() {
assertSignature("String|", "java.lang.String");
}
public void testDocumentationOfUserDefinedConstructors() {
Snippet a = classKey(assertEval("class A {}"));
assertDocumentation("new A(|", "A()");
assertSignature("new A(|", "A()");
Snippet a2 = classKey(assertEval("class A { A() {} A(int i) {}}",
ste(MAIN_SNIPPET, VALID, VALID, true, null),
ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
assertDocumentation("new A(|", "A()", "A(int i)");
assertSignature("new A(|", "A()", "A(int i)");
assertEval("class A<T> { A(T a) {} A(int i) {} <U> A(T t, U u) {}}",
ste(MAIN_SNIPPET, VALID, VALID, true, null),
ste(a2, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
assertDocumentation("new A(|", "A<T>(T a)", "A<T>(int i)", "<U> A<T>(T t, U u)");
assertSignature("new A(|", "A<T>(T a)", "A<T>(int i)", "<U> A<T>(T t, U u)");
}
public void testDocumentationOfOverriddenMethods() throws Exception {
dontReadParameterNamesFromClassFile();
assertDocumentation("\"\".wait(|",
assertSignature("\"\".wait(|",
"void Object.wait(long) throws InterruptedException",
"void Object.wait(long, int) throws InterruptedException",
"void Object.wait() throws InterruptedException");
assertEval("class Base {void method() {}}");
Snippet e = classKey(assertEval("class Extend extends Base {}"));
assertDocumentation("new Extend().method(|", "void Base.method()");
assertSignature("new Extend().method(|", "void Base.method()");
assertEval("class Extend extends Base {void method() {}}",
ste(MAIN_SNIPPET, VALID, VALID, true, null),
ste(e, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
assertDocumentation("new Extend().method(|", "void Extend.method()");
assertSignature("new Extend().method(|", "void Extend.method()");
}
public void testDocumentationOfInvisibleMethods() {
assertDocumentation("Object.wait(|", "");
assertDocumentation("\"\".indexOfSupplementary(|", "");
assertSignature("Object.wait(|");
assertSignature("\"\".indexOfSupplementary(|");
Snippet a = classKey(assertEval("class A {void method() {}}"));
assertDocumentation("A.method(|", "");
assertSignature("A.method(|");
assertEval("class A {private void method() {}}",
ste(MAIN_SNIPPET, VALID, VALID, true, null),
ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
assertDocumentation("new A().method(|", "");
assertSignature("new A().method(|");
}
public void testDocumentationOfInvisibleConstructors() {
assertDocumentation("new Compiler(|", "");
assertSignature("new Compiler(|");
assertEval("class A { private A() {} }");
assertDocumentation("new A(|", "");
assertSignature("new A(|");
}
public void testDocumentationWithBoxing() {
......@@ -535,13 +540,13 @@ public class CompletionSuggestionTest extends KullaTesting {
assertEval("Object object = null;");
assertEval("void method(int n, Object o) { }");
assertEval("void method(Object n, int o) { }");
assertDocumentation("method(primitive,|",
assertSignature("method(primitive,|",
"void method(int n, Object o)",
"void method(Object n, int o)");
assertDocumentation("method(boxed,|",
assertSignature("method(boxed,|",
"void method(int n, Object o)",
"void method(Object n, int o)");
assertDocumentation("method(object,|",
assertSignature("method(object,|",
"void method(Object n, int o)");
}
......@@ -567,7 +572,7 @@ public class CompletionSuggestionTest extends KullaTesting {
void assertDoc(String generics, String expectedGenerics) {
assertEval(evalFormatter.apply(generics, count));
assertDocumentation(codeFacotry.apply(count), docFormatter.apply(expectedGenerics, count));
assertSignature(codeFacotry.apply(count), docFormatter.apply(expectedGenerics, count));
count++;
}
}
......
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8131019
* @summary Test Javadoc
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.jshell
* @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
* @build KullaTesting TestingInputStream Compiler
* @run testng JavadocTest
*/
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.testng.annotations.Test;
@Test
public class JavadocTest extends KullaTesting {
private final Compiler compiler = new Compiler();
public void testJavadoc() {
prepareZip();
assertJavadoc("test.Clazz|", "test.Clazz\n" +
"Top level. ");
assertEval("test.Clazz clz = null;");
assertJavadoc("clz.test(|", "String test.Clazz.test(int p) throws IllegalStateException\n" +
" javadoc1A\n" +
"\n" +
" @param p param\n" +
" @throws IllegalStateException exc\n" +
" @return value\n");
//undefined handling:
assertJavadoc("clz.undef|");
}
private void prepareZip() {
String clazz =
"package test;\n" +
"/**Top level." +
" */\n" +
"public class Clazz {\n" +
" /**\n" +
" * javadoc1A\n" +
" *\n" +
" * @param p param\n" +
" * @throws IllegalStateException exc\n" +
" * @return value\n" +
" */\n" +
" public String test(int p) throws IllegalStateException { return null;}\n" +
"}\n";
Path srcZip = Paths.get("src.zip");
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(srcZip))) {
out.putNextEntry(new JarEntry("test/Clazz.java"));
out.write(clazz.getBytes());
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
compiler.compile(clazz);
try {
Field availableSources = getAnalysis().getClass().getDeclaredField("availableSources");
availableSources.setAccessible(true);
availableSources.set(getAnalysis(), Arrays.asList(srcZip));
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
addToClasspath(compiler.getClassDir());
}
}
......@@ -72,11 +72,14 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import jdk.jshell.Diag;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static jdk.jshell.Snippet.Status.*;
import static org.testng.Assert.*;
import static jdk.jshell.Snippet.SubKind.METHOD_SUBKIND;
import jdk.jshell.SourceCodeAnalysis.Documentation;
public class KullaTesting {
......@@ -946,12 +949,24 @@ public class KullaTesting {
}
}
public void assertDocumentation(String code, String... expected) {
public void assertSignature(String code, String... expected) {
int cursor = code.indexOf('|');
code = code.replace("|", "");
assertTrue(cursor > -1, "'|' expected, but not found in: " + code);
List<Documentation> documentation = getAnalysis().documentation(code, cursor, false);
Set<String> docSet = documentation.stream().map(doc -> doc.signature()).collect(Collectors.toSet());
Set<String> expectedSet = Stream.of(expected).collect(Collectors.toSet());
assertEquals(docSet, expectedSet, "Input: " + code);
}
public void assertJavadoc(String code, String... expected) {
int cursor = code.indexOf('|');
code = code.replace("|", "");
assertTrue(cursor > -1, "'|' expected, but not found in: " + code);
String documentation = getAnalysis().documentation(code, cursor);
Set<String> docSet = Stream.of(documentation.split("\r?\n")).collect(Collectors.toSet());
List<Documentation> documentation = getAnalysis().documentation(code, cursor, true);
Set<String> docSet = documentation.stream()
.map(doc -> doc.signature() + "\n" + doc.javadoc())
.collect(Collectors.toSet());
Set<String> expectedSet = Stream.of(expected).collect(Collectors.toSet());
assertEquals(docSet, expectedSet, "Input: " + code);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册