Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKalyan Prasad Tatavarthi2020-08-27 01:54:54 -0400
committerKalyan Prasad Tatavarthi2020-09-03 02:54:08 -0400
commit1fc8eb207b22bcf0985d573bf20a60a2d712c535 (patch)
treea04e6140ba612cc7ba3cf7e0157a37e532b6e3fe
parent2fb065742918e5309bd9f028ae64eb69651b05a1 (diff)
downloadeclipse.jdt.core-1fc8eb207b22bcf0985d573bf20a60a2d712c535.tar.gz
eclipse.jdt.core-1fc8eb207b22bcf0985d573bf20a60a2d712c535.tar.xz
eclipse.jdt.core-1fc8eb207b22bcf0985d573bf20a60a2d712c535.zip
Bug 566057 - [15] [javadoc] [DOM] Dom AST support for module in @see,
@link and @linkplain javadoc tags. Change-Id: I0a3018bceadcc96cfc539be66b0ff2d11ee49ad4 Signed-off-by: Kalyan Prasad Tatavarthi <kalyan_prasad@in.ibm.com>
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java46
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java126
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterJavadocTest_15.java1430
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java5
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/RunConverterTests.java1
-rw-r--r--org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath6
-rw-r--r--org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.project17
-rw-r--r--org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/javadoc/test0001/X.java7
-rw-r--r--org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/module-info.java4
-rw-r--r--org.eclipse.jdt.core/.settings/.api_filters8
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionJavadocParser.java13
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java25
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocModuleReference.java17
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java18
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java30
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java44
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTMatcher.java24
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTNode.java8
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTVisitor.java34
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java19
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java97
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleQualifiedName.java256
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java11
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java5
24 files changed, 2227 insertions, 24 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
index d6b9f9b2aa..106399b8fe 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
@@ -3792,6 +3792,52 @@ protected void runNegativeTest(
// javac options
javacTestOptions /* javac test options */);
}
+//runNegativeTest(
+//// test directory preparation
+//new String[] { /* test files */
+//},
+//null /* no test files */,
+//// compiler results
+//"----------\n" + /* expected compiler log */
+//// javac options
+//JavacTestOptions.SKIP /* skip javac tests */);
+//JavacTestOptions.DEFAULT /* default javac test options */);
+//javacTestOptions /* javac test options */);
+protected void runNegativeTest(
+// test directory preparation
+String[] testFiles,
+String[] dependentFiles,
+// compiler results
+String expectedCompilerLog,
+// javac options
+JavacTestOptions javacTestOptions) {
+ runTest(
+ // test directory preparation
+ true /* flush output directory */,
+ testFiles /* test files */,
+ dependentFiles,
+ // compiler options
+ null /* no class libraries */,
+ false,
+ null /* no custom options */,
+ false /* do not perform statements recovery */,
+ null /* no custom requestor */,
+ // compiler results
+ true /* expecting compiler errors */,
+ expectedCompilerLog /* expected compiler log */,
+ // runtime options
+ null,
+ false /* do not force execution */,
+ null /* no vm arguments */,
+ // runtime results
+ null /* do not check output string */,
+ null /* do not check error string */,
+ null,
+ null,
+ // javac options
+ javacTestOptions /* javac test options */,
+ Charset.defaultCharset());
+}
// runNegativeTest(
// // test directory preparation
// true /* flush output directory */,
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java
index 518420bfaf..2095669d8f 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java
@@ -230,5 +230,131 @@ public void test003() {
new String[] {"module-info.java", moduleInfo }, "" );
}
+public void test004() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+
+ String moduleInfo = "" +
+ "/**\n" +
+ " */\n" +
+ "module mod.one { \n" +
+ " exports p;\n" +
+ "}";
+ String I1 = "" +
+ "package p;\n" +
+ "/**\n" +
+ " * interface I1\n" +
+ " * {@linkplain mod.one/}\n" +
+ " */\n" +
+ "interface I1 {\n" +
+ " /**\n" +
+ " * Method foo\n" +
+ " * @return int\n" +
+ " */\n" +
+ " public int foo();\n" +
+ "}";
+
+ String P1 = "" +
+ "package p;\n" +
+ "/**\n" +
+ " * class P1\n" +
+ " * {@linkplain mod.one/p.P1#foo()}\n" +
+ " */\n" +
+ "public class P1 implements I1 {\n" +
+ " @Override\n" +
+ " public int foo() { return 0; }\n" +
+ "}";
+
+ this.runConformTest(new String[] {"p/I1.java", I1 ,"p/P1.java" , P1 } ,
+ new String[] {"module-info.java", moduleInfo }, "" );
+}
+
+public void test005() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+
+ String moduleInfo = "" +
+ "/**\n" +
+ " */\n" +
+ "module mod.one { \n" +
+ " exports p;\n" +
+ "}";
+ String I1 = "" +
+ "package p;\n" +
+ "/**\n" +
+ " * interface I1\n" +
+ " * {@linkplain mod.one/}\n" +
+ " */\n" +
+ "interface I1 {\n" +
+ " /**\n" +
+ " * Method foo\n" +
+ " * @return int\n" +
+ " */\n" +
+ " public int foo();\n" +
+ "}";
+
+ String P1 = "" +
+ "package p;\n" +
+ "/**\n" +
+ " * class P1\n" +
+ " * {@linkplain mod.one/p.P1#abc}\n" +
+ " */\n" +
+ "public class P1 implements I1 {\n" +
+ " public int abc;\n" +
+ " @Override\n" +
+ " public int foo() { return 0; }\n" +
+ "}";
+
+ this.runConformTest(new String[] {"p/I1.java", I1 ,"p/P1.java" , P1 } ,
+ new String[] {"module-info.java", moduleInfo }, "" );
+}
+
+public void test006() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+
+ String moduleInfo = "" +
+ "/**\n" +
+ " */\n" +
+ "module mod.one { \n" +
+ " exports p;\n" +
+ "}";
+ String I1 = "" +
+ "package p;\n" +
+ "/**\n" +
+ " * interface I1\n" +
+ " * {@linkplain mod.one/}\n" +
+ " */\n" +
+ "interface I1 {\n" +
+ " /**\n" +
+ " * Method foo\n" +
+ " * @return int\n" +
+ " */\n" +
+ " public int foo();\n" +
+ "}";
+
+ String P1 = "" +
+ "package p;\n" +
+ "/**\n" +
+ " * class P1\n" +
+ " * {@linkplain mod.one/p.P1#abd}\n" +
+ " */\n" +
+ "public class P1 implements I1 {\n" +
+ " public int abc;\n" +
+ " @Override\n" +
+ " public int foo() { return 0; }\n" +
+ "}";
+ String errorMsg = "" +
+ "----------\n" +
+ "1. ERROR in p\\P1.java (at line 4)\n" +
+ " * {@linkplain mod.one/p.P1#abd}\n" +
+ " ^^^\n" +
+ "Javadoc: abd cannot be resolved or is not a field\n" +
+ "----------\n";
+
+ this.runNegativeTest(new String[] {"p/I1.java", I1 ,"p/P1.java" , P1 } ,
+ new String[] {"module-info.java", moduleInfo }, errorMsg,
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
+}
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterJavadocTest_15.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterJavadocTest_15.java
new file mode 100644
index 0000000000..4e3e5ecee6
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterJavadocTest_15.java
@@ -0,0 +1,1430 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.dom;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.Comment;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.MemberRef;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodRef;
+import org.eclipse.jdt.core.dom.MethodRefParameter;
+import org.eclipse.jdt.core.dom.ModuleQualifiedName;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.TextElement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.IModuleBinding;
+import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
+
+/**
+ * Class to test DOM/AST nodes built for Javadoc comments.
+ *
+ * Most of tests are 'automatic'. It means that to add a new tests, you only need to
+ * create one or several CUs and put them in org.eclipse.jdt.core.model.tests/workspace/Converter/src/javadoc/testXXX
+ * folder and add the corresponding test in this class:
+ * <pre>
+ * public void testXXX() throws JavaModelException {
+ * verifyComments("testXXX");
+ * }
+ * </pre>
+ *
+ * Note that when a test fails, the easiest way to debug it is to open
+ * a runtime workbench, create a project 'Converter', delete the default 'src' source folder
+ * and replace it by a linked source to the 'src' folder of org.eclipse.jdt.core.model.tests/workspace/Converter/src
+ * in your workspace.
+ *
+ * Then open the CU on which the test fails in a ASTView and verify the offset/length
+ * of the offending node located at the positions displayed in the console when the test failed...
+ *
+ * Since 3.4, the failing test also provides the comparison between the source of the comment
+ * and the string get from the built DOM/AST nodes in the comment (see {@link ASTConverterJavadocFlattener})
+ * but this may be not enough to see precisely the origin of the problem.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class ASTConverterJavadocTest_15 extends ConverterTestSetup {
+
+ // Flag to know whether Converter directory should be copied from org.eclipse.jdt.core.tests.model project
+ static protected boolean COPY_DIR = true;
+
+ // Test counters
+ protected static int[] TEST_COUNTERS = { 0, 0, 0, 0 };
+ // Unicode tests
+ protected static boolean UNICODE = false;
+ // Unix tests
+ final boolean unix;
+ static final String UNIX_SUPPORT = System.getProperty("unix");
+ // Doc Comment support
+ static final String DOC_COMMENT_SUPPORT = System.getProperty("doc.support");
+ final String docCommentSupport;
+
+ // List of comments read from source of test
+ private static final int LINE_COMMENT = 100;
+ private static final int BLOCK_COMMENT =200;
+ private static final int DOC_COMMENT = 300;
+ List comments = new ArrayList();
+ private String chars;
+ // List of tags contained in each comment read from test source.
+ List allTags = new ArrayList();
+ // tags inhibiting inline tags
+ static final String TAG_CODE = "code";
+ static final String TAG_LITERAL = "literal";
+ // Current compilation unit
+ protected ICompilationUnit sourceUnit;
+ // Test package binding
+ protected boolean resolveBinding = true;
+ protected boolean packageBinding = true;
+ // AST Level
+ protected int astLevel = AST.JLS15;
+ protected int savedLevel;
+ // Debug
+ protected String prefix = "";
+ protected boolean debug = false;
+ protected StringBuffer problems;
+ protected String compilerOption = JavaCore.IGNORE;
+ protected List failures;
+ protected boolean stopOnFailure = true;
+ Map savedOptions = null;
+ protected ICompilationUnit moduleUnit;
+
+
+
+ /**
+ * @param name
+ * @param support
+ */
+ public ASTConverterJavadocTest_15(String name, String support, String unix) {
+ super(name);
+ this.docCommentSupport = support;
+ this.unix = "true".equals(unix);
+ }
+ /**
+ * @param name
+ */
+ public ASTConverterJavadocTest_15(String name) {
+ this(name.substring(0, name.indexOf(" - ")),
+ name.substring(name.indexOf(" - Doc ") + 7, name.lastIndexOf("abled") + 5),
+ name.indexOf(" - Unix") != -1 ? "true" : "false");
+ }
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#getName()
+ */
+ public String getName() {
+ String strUnix = this.unix ? " - Unix" : "";
+ return super.getName()+" - Doc "+this.docCommentSupport+strUnix;
+ }
+
+ public static Test suite() {
+ TestSuite suite = new Suite(ASTConverterJavadocTest_15.class.getName());
+// String param = System.getProperty("unicode");
+// if ("true".equals(param)) {
+// unicode = true;
+// }
+// String param = System.getProperty("unix");
+// if ("true".equals(param)) {
+// unix = true;
+// }
+ if (DOC_COMMENT_SUPPORT == null) {
+ buildSuite(suite, JavaCore.ENABLED);
+ buildSuite(suite, JavaCore.DISABLED);
+ } else {
+ String support = DOC_COMMENT_SUPPORT==null ? JavaCore.DISABLED : (DOC_COMMENT_SUPPORT.equals(JavaCore.DISABLED)?JavaCore.DISABLED:JavaCore.ENABLED);
+ buildSuite(suite, support);
+ }
+ return suite;
+
+// Run test cases subset
+// COPY_DIR = false;
+// System.err.println("WARNING: only subset of tests will be executed!!!");
+// suite.addTest(new ASTConverterJavadocTest("testBug165525"));
+// return suite;
+ }
+
+ public static void buildSuite(TestSuite suite, String support) {
+ Class c = ASTConverterJavadocTest_15.class;
+ Method[] methods = c.getMethods();
+ for (int i = 0, max = methods.length; i < max; i++) {
+ if (methods[i].getName().startsWith("test")) { //$NON-NLS-1$
+ suite.addTest(new ASTConverterJavadocTest_15(methods[i].getName(), support, UNIX_SUPPORT));
+ }
+ }
+ // when unix support not specified, also run using unix format
+ if (UNIX_SUPPORT == null && JavaCore.ENABLED.equals(support)) {
+ for (int i = 0, max = methods.length; i < max; i++) {
+ if (methods[i].getName().startsWith("test")) { //$NON-NLS-1$
+ suite.addTest(new ASTConverterJavadocTest_15(methods[i].getName(), support, "true"));
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.tests.model.AbstractJavaModelTests#copyDirectory(java.io.File, java.io.File)
+ */
+ @Override
+ protected void copyDirectory(File sourceDir, File targetDir) throws IOException {
+ if (COPY_DIR) {
+ super.copyDirectory(sourceDir, targetDir);
+ } else {
+ targetDir.mkdirs();
+ File sourceFile = new File(sourceDir, ".project");
+ File targetFile = new File(targetDir, ".project");
+ targetFile.createNewFile();
+ copy(sourceFile, targetFile);
+ sourceFile = new File(sourceDir, ".classpath");
+ targetFile = new File(targetDir, ".classpath");
+ targetFile.createNewFile();
+ copy(sourceFile, targetFile);
+ }
+ }
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ TEST_COUNTERS[0]++;
+ this.failures = new ArrayList();
+ this.problems = new StringBuffer();
+ this.workingCopies = null;
+ this.savedLevel = this.astLevel;
+ }
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ int size = this.failures.size();
+ String title = size+" positions/bindings were incorrect in "+getName();
+ if (size == 0) {
+ TEST_COUNTERS[1]++;
+ } else if (this.problems.length() > 0) {
+ if (this.debug) {
+ System.out.println("Compilation warnings/errors occured:");
+ System.out.println(this.problems.toString());
+ }
+ TEST_COUNTERS[2]++;
+ } else {
+ TEST_COUNTERS[3]++;
+ System.out.println(title+":");
+ for (int i=0; i<size; i++) {
+ System.out.println(" - "+this.failures.get(i));
+ }
+ }
+// if (!stopOnFailure) {
+ assertTrue(title, size==0 || this.problems.length() > 0);
+// }
+ super.tearDown();
+
+ // Restore saved ast level
+ this.astLevel = this.savedLevel;
+ }
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#tearDown()
+ */
+ @Override
+ public void tearDownSuite() throws Exception {
+ // put default options on project
+ if (this.currentProject != null && this.savedOptions != null) {
+ this.currentProject.setOptions(this.savedOptions);
+ }
+ super.tearDownSuite();
+ if (TEST_COUNTERS[0] != TEST_COUNTERS[1]) {
+ NumberFormat intFormat = NumberFormat.getInstance();
+ intFormat.setMinimumIntegerDigits(3);
+ intFormat.setMaximumIntegerDigits(3);
+ System.out.println("=====================================");
+ System.out.println(intFormat.format(TEST_COUNTERS[0])+" tests have been executed:");
+ System.out.println(" - "+intFormat.format(TEST_COUNTERS[1])+" tests have been actually executed.");
+ System.out.println(" - "+intFormat.format(TEST_COUNTERS[2])+" tests were skipped due to compilation errors.");
+ System.out.println(" - "+intFormat.format(TEST_COUNTERS[3])+" tests failed.");
+ }
+ }
+
+ @Override
+ public ASTNode runConversion(char[] source, String unitName, IJavaProject project) {
+ ASTParser parser = ASTParser.newParser(this.astLevel);
+ parser.setSource(source);
+ parser.setUnitName(unitName);
+ parser.setProject(project);
+ parser.setResolveBindings(this.resolveBinding);
+ return parser.createAST(null);
+ }
+
+ @Override
+ public ASTNode runConversion(char[] source, String unitName, IJavaProject project, Map options) {
+ if (project == null) {
+ ASTParser parser = ASTParser.newParser(this.astLevel);
+ parser.setSource(source);
+ parser.setUnitName(unitName);
+ parser.setCompilerOptions(options);
+ parser.setResolveBindings(this.resolveBinding);
+ return parser.createAST(null);
+ }
+ return runConversion(source, unitName, project);
+ }
+
+ private char getNextChar(char[] source, int idx) {
+ // get next char
+ char ch = source[idx];
+ int charLength = 1;
+ int pos = idx;
+ this.chars = null;
+ if (ch == '\\' && source[idx+1] == 'u') {
+ //-------------unicode traitement ------------
+ int c1, c2, c3, c4;
+ charLength++;
+ while (source[idx+charLength] == 'u') charLength++;
+ if (((c1 = ScannerHelper.getHexadecimalValue(source[idx+charLength++])) > 15 || c1 < 0)
+ || ((c2 = ScannerHelper.getHexadecimalValue(source[idx+charLength++])) > 15 || c2 < 0)
+ || ((c3 = ScannerHelper.getHexadecimalValue(source[idx+charLength++])) > 15 || c3 < 0)
+ || ((c4 = ScannerHelper.getHexadecimalValue(source[idx+charLength++])) > 15 || c4 < 0)) {
+ return ch;
+ }
+ ch = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
+ this.chars = new String(source, pos, charLength);
+ }
+ return ch;
+ }
+ /*
+ * Convert Javadoc source to match Javadoc.toString().
+ * Store converted comments and their corresponding tags respectively
+ * in comments and allTags fields
+ */
+ protected void setSourceComment(char[] source) throws ArrayIndexOutOfBoundsException {
+ this.comments = new ArrayList();
+ this.allTags = new ArrayList();
+ StringBuffer buffer = null;
+ int comment = 0;
+ boolean end = false, lineStarted = false;
+ String tag = null;
+ List tags = new ArrayList();
+ int length = source.length;
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345
+ // when parsing tags such as @code and @literal,
+ // any tag should be discarded and considered as plain text until
+ // properly closed with closing brace
+ boolean considerTagAsPlainText = false;
+ int openingBraces = 0;
+ char previousChar=0, currentChar=0;
+ for (int i=0; i<length;) {
+ previousChar = currentChar;
+ // get next char
+ currentChar = getNextChar(source, i);
+ i += (this.chars==null) ? 1 : this.chars.length();
+
+ switch (comment) {
+ case 0:
+ switch (currentChar) {
+ case '/':
+ comment = 1; // first char for comments...
+ buffer = new StringBuffer();
+ if (this.chars == null) buffer.append(currentChar);
+ else buffer.append(this.chars);
+ break;
+ case '\'':
+ while (i<length) {
+ // get next char
+ currentChar = getNextChar(source, i);
+ i += (this.chars==null) ? 1 : this.chars.length();
+ if (currentChar == '\\') {
+ // get next char
+ currentChar = getNextChar(source, i);
+ i += (this.chars==null) ? 1 : this.chars.length();
+ } else {
+ if (currentChar == '\'') {
+ break;
+ }
+ }
+ }
+ break;
+ case '"':
+ while (i<length) {
+ // get next char
+ currentChar = getNextChar(source, i);
+ i += (this.chars==null) ? 1 : this.chars.length();
+ if (currentChar == '\\') {
+ // get next char
+ currentChar = getNextChar(source, i);
+ i += (this.chars==null) ? 1 : this.chars.length();
+ } else {
+ if (currentChar == '"') {
+ // get next char
+ currentChar = getNextChar(source, i);
+ if (currentChar == '"') {
+ i += (this.chars==null) ? 1 : this.chars.length();
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ break;
+ case 1: // first '/' has been found...
+ switch (currentChar) {
+ case '/':
+ if (this.chars == null) buffer.append(currentChar);
+ else buffer.append(this.chars);
+ comment = LINE_COMMENT;
+ break;
+ case '*':
+ if (this.chars == null) buffer.append(currentChar);
+ else buffer.append(this.chars);
+ comment = 2; // next step
+ break;
+ default:
+ comment = 0;
+ break;
+ }
+ break;
+ case 2: // '/*' has been found...
+ if (currentChar == '*') {
+ comment = 3; // next step...
+ } else {
+ comment = BLOCK_COMMENT;
+ }
+ if (this.chars == null) buffer.append(currentChar);
+ else buffer.append(this.chars);
+ break;
+ case 3: // '/**' has bee found, verify that's not an empty block comment
+ if (currentChar == '/') { // empty block comment
+ if (this.chars == null) buffer.append(currentChar);
+ else buffer.append(this.chars);
+ this.comments.add(buffer.toString());
+ this.allTags.add(new ArrayList());
+ comment = 0;
+ break;
+ }
+ comment = DOC_COMMENT;
+ // $FALL-THROUGH$ - do not break, directly go to next case...
+ case DOC_COMMENT:
+ if (tag != null) { // a tag name is currently scanned
+ if (currentChar >= 'a' && currentChar <= 'z') {
+ tag += currentChar;
+ } else {
+ if (tag.equalsIgnoreCase(TAG_LITERAL) || tag.equalsIgnoreCase(TAG_CODE)) considerTagAsPlainText = true;
+ tags.add(tag);
+ tag = null;
+ }
+ }
+ // Some characters are special in javadoc comments
+ switch (currentChar) {
+ case '@':
+ if (!lineStarted) {
+ tag = "";
+ lineStarted = true;
+ } else if (previousChar == '{') {
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345
+ if (considerTagAsPlainText) {
+ openingBraces++;
+ } else {
+ tag = "";
+ lineStarted = true;
+ }
+ }
+ break;
+ case '\r':
+ case '\n':
+ lineStarted = false;
+ break;
+ case '*':
+ break;
+ case '}':
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345
+ if (considerTagAsPlainText) {
+ if (openingBraces > 0) {
+ openingBraces--;
+ } else {
+ considerTagAsPlainText = false;
+ }
+ }
+ break;
+ default:
+ if (!Character.isWhitespace(currentChar)) {
+ lineStarted = true;
+ }
+ }
+ // $FALL-THROUGH$ - common treatment for block and javadoc comments
+ case BLOCK_COMMENT:
+ if (this.chars == null) buffer.append(currentChar);
+ else buffer.append(this.chars);
+ if (end && currentChar == '/') {
+ comment = 0;
+ lineStarted = false;
+ this.comments.add(buffer.toString());
+ this.allTags.add(tags);
+ tags = new ArrayList();
+ }
+ end = currentChar == '*';
+ break;
+ case LINE_COMMENT:
+ if (currentChar == '\r' || currentChar == '\n') {
+ /*
+ if (currentChar == '\r' && source[i+1] == '\n') {
+ buffer.append(source[++i]);
+ }
+ */
+ comment = 0;
+ this.comments.add(buffer.toString());
+ this.allTags.add(tags);
+ } else {
+ if (this.chars == null) buffer.append(currentChar);
+ else buffer.append(this.chars);
+ }
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+
+ /*
+ * Convert Javadoc source to match Javadoc.toString().
+ * Store converted comments and their corresponding tags respectively
+ * in comments and allTags fields
+ */
+ char[] getUnicodeSource(char[] source) {
+ int length = source.length;
+ int unicodeLength = length*6;
+ char[] unicodeSource = new char[unicodeLength];
+ int u=0;
+ for (int i=0; i<length; i++) {
+ // get next char
+ if (source[i] == '\\' && source[i+1] == 'u') {
+ //-------------unicode traitement ------------
+ int c1, c2, c3, c4;
+ unicodeSource[u++] = source[i];
+ unicodeSource[u++] = source[++i];
+ if (((c1 = ScannerHelper.getHexadecimalValue(source[i+1])) > 15 || c1 < 0)
+ || ((c2 = ScannerHelper.getHexadecimalValue(source[i+2])) > 15 || c2 < 0)
+ || ((c3 = ScannerHelper.getHexadecimalValue(source[i+3])) > 15 || c3 < 0)
+ || ((c4 = ScannerHelper.getHexadecimalValue(source[i+4])) > 15 || c4 < 0)) {
+ throw new RuntimeException("Invalid unicode in source at "+i);
+ }
+ for (int j=0; j<4; j++) unicodeSource[u++] = source[++i];
+ } else {
+ unicodeSource[u++] = '\\';
+ unicodeSource[u++] = 'u';
+ unicodeSource[u++] = '0';
+ unicodeSource[u++] = '0';
+ int val = source[i]/16;
+ unicodeSource[u++] = (char) (val<10 ? val+ 0x30 : val-10+0x61);
+ val = source[i]%16;
+ unicodeSource[u++] = (char) (val<10 ? val+ 0x30 : val-10+0x61);
+ }
+ }
+ // Return one well sized array
+ if (u != unicodeLength) {
+ char[] result = new char[u];
+ System.arraycopy(unicodeSource, 0, result, 0, u);
+ return result;
+ }
+ return unicodeSource;
+ }
+
+ /*
+ * Convert Javadoc source to match Javadoc.toString().
+ * Store converted comments and their corresponding tags respectively
+ * in comments and allTags fields
+ */
+ char[] getUnixSource(char[] source) {
+ int length = source.length;
+ int unixLength = length;
+ char[] unixSource = new char[unixLength];
+ int u=0;
+ for (int i=0; i<length; i++) {
+ // get next char
+ if (source[i] == '\r' && source[i+1] == '\n') {
+ i++;
+ }
+ unixSource[u++] = source[i];
+ }
+ // Return one well sized array
+ if (u != unixLength) {
+ char[] result = new char[u];
+ System.arraycopy(unixSource, 0, result, 0, u);
+ return result;
+ }
+ return unixSource;
+ }
+
+ /*
+ * Return all tags number for a given Javadoc
+ */
+ int allTags(Javadoc docComment) {
+ int all = 0;
+ // Count main tags
+ Iterator tags = docComment.tags().listIterator();
+ while (tags.hasNext()) {
+ TagElement tagElement = (TagElement) tags.next();
+ if (tagElement.getTagName() != null) {
+ all++;
+ }
+ Iterator fragments = tagElement.fragments().listIterator();
+ while (fragments.hasNext()) {
+ ASTNode node = (ASTNode) fragments.next();
+ if (node.getNodeType() == ASTNode.TAG_ELEMENT) {
+ all++;
+ }
+ }
+ }
+ return all;
+ }
+
+ /*
+ * Add a failure to the list. Use only one method as it easier to put breakpoint to
+ * debug failure when it occurs...
+ */
+ private void addFailure(String msg) {
+ this.failures.add(msg);
+ }
+
+ /*
+ * Put the failure message in list instead of throwing exception immediately.
+ * This allow to store several failures per test...
+ * @see tearDown method which finally throws the execption to signal that test fails.
+ */
+ protected void assumeTrue(String msg, boolean cond) {
+ if (!cond) {
+ addFailure(msg);
+ if (this.stopOnFailure) assertTrue(msg, cond);
+ }
+ }
+
+ /*
+ * Put the failure message in list instead of throwing exception immediately.
+ * This allow to store several failures per test...
+ * @see tearDown method which finally throws the execption to signal that test fails.
+ */
+ protected void assumeNull(String msg, Object obj) {
+ if (obj != null) {
+ addFailure(msg);
+ if (this.stopOnFailure) assertNull(msg, obj);
+ }
+ }
+
+ /*
+ * Put the failure message in list instead of throwing exception immediately.
+ * This allow to store several failures per test...
+ * @see tearDown method which finally throws the execption to signal that test fails.
+ */
+ protected void assumeNotNull(String msg, Object obj) {
+ if (obj == null) {
+ addFailure(msg);
+ if (this.stopOnFailure) assertNotNull(msg, obj);
+ }
+ }
+
+ /*
+ * Put the failure message in list instead of throwing exception immediately.
+ * This allow to store several failures per test...
+ * @see tearDown method which finally throws the execption to signal that test fails.
+ */
+ protected void assumeEquals(String msg, int expected, int actual) {
+ if (expected != actual) {
+ addFailure(msg+", expected="+expected+" actual="+actual);
+ if (this.stopOnFailure) assertEquals(msg, expected, actual);
+ }
+ }
+
+ /*
+ * Put the failure message in list instead of throwing exception immediately.
+ * This allow to store several failures per test...
+ * @see tearDown method which finally throws the execption to signal that test fails.
+ */
+ protected void assumeEquals(String msg, Object expected, Object actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ addFailure(msg+", expected:<"+expected+"> actual:<"+actual+'>');
+ if (this.stopOnFailure) assertEquals(msg, expected, actual);
+ }
+
+ /*
+ * Verify positions of tags in source
+ */
+ private void verifyPositions(Javadoc docComment, char[] source) {
+ boolean stop = this.stopOnFailure;
+ this.stopOnFailure = false;
+ // Verify javadoc start and end position
+ int start = docComment.getStartPosition();
+ int end = start+docComment.getLength()-1;
+ assumeTrue(this.prefix+"Misplaced javadoc start at <"+start+">: "+docComment, source[start++] == '/' && source[start++] == '*' && source[start++] == '*');
+ // Get first meaningful character
+ int tagStart = start;
+ // Verify tags
+ Iterator tags = docComment.tags().listIterator();
+ while (tags.hasNext()) {
+ while (source[tagStart] == '*' || Character.isWhitespace(source[tagStart])) {
+ tagStart++; // purge non-stored characters
+ }
+ TagElement tagElement = (TagElement) tags.next();
+ int teStart = tagElement.getStartPosition();
+ assumeEquals(this.prefix+"Wrong start position <"+teStart+"> for tag element: "+tagElement, tagStart, teStart);
+ verifyPositions(tagElement, source);
+ tagStart += tagElement.getLength();
+ }
+ while (source[tagStart] == '*' || Character.isWhitespace(source[tagStart])) {
+ tagStart++; // purge non-stored characters
+ }
+ assumeTrue(this.prefix+"Misplaced javadoc end at <"+tagStart+'>', source[tagStart-1] == '*' && source[tagStart] == '/');
+ assumeEquals(this.prefix+"Wrong javadoc length at <"+end+">: ", tagStart, end);
+ this.stopOnFailure = stop;
+ if (stop && this.failures.size() > 0) {
+ String expected = new String(source, docComment.getStartPosition(), docComment.getLength());
+ ASTConverterJavadocFlattener flattener = new ASTConverterJavadocFlattener(expected);
+ docComment.accept(flattener);
+ assertEquals("Unexpected errors while verifying javadoc comment positions!", expected, flattener.getResult());
+ }
+ }
+
+ /**
+ * Verify positions of fragments in source
+ * @deprecated using deprecated code
+ */
+ private void verifyPositions(TagElement tagElement, char[] source) {
+ String text = null;
+ // Verify tag name
+ String tagName = tagElement.getTagName();
+ int tagStart = tagElement.getStartPosition();
+ if (tagElement.isNested()) {
+ assumeEquals(this.prefix+"Wrong start position <"+tagStart+"> for "+tagElement, '{', source[tagStart++]);
+ }
+ if (tagName != null) {
+ text= new String(source, tagStart, tagName.length());
+ assumeEquals(this.prefix+"Misplaced tag name at <"+tagStart+">: ", tagName, text);
+ tagStart += tagName.length();
+ }
+ // Verify each fragment
+ ASTNode previousFragment = null;
+ Iterator elements = tagElement.fragments().listIterator();
+ while (elements.hasNext()) {
+ ASTNode fragment = (ASTNode) elements.next();
+ if (fragment.getNodeType() == ASTNode.TEXT_ELEMENT) {
+ if (previousFragment == null && TagElement.TAG_PARAM.equals(tagName) && ((TextElement)fragment).getText().equals("<")) { // special case here for @param <E> syntax
+ int start = tagStart;
+ // verify '<'
+ while (source[start] == ' ' || Character.isWhitespace(source[start])) {
+ start++; // purge white characters
+ }
+ text = new String(source, start, fragment.getLength());
+ assumeEquals(this.prefix+"Misplaced text element at <"+fragment.getStartPosition()+">: ", text, ((TextElement) fragment).getText());
+ start += fragment.getLength();
+ // verify simple name
+ assumeTrue(this.prefix+"Unexpected fragment end for "+tagElement, elements.hasNext());
+ fragment = (ASTNode) elements.next();
+ while (source[start] == ' ' || Character.isWhitespace(source[start])) {
+ start++; // purge white characters
+ }
+ assumeEquals(this.prefix+"Unexpected node type for tag element "+tagElement, ASTNode.SIMPLE_NAME, fragment.getNodeType());
+ Name name = (Name) fragment;
+ verifyNamePositions(start, name, source);
+ start += fragment.getLength();
+ // verify simple name
+ assumeTrue(this.prefix+"Unexpected fragment end for "+tagElement, elements.hasNext());
+ fragment = (ASTNode) elements.next();
+ while (source[start] == ' ' || Character.isWhitespace(source[start])) {
+ start++; // purge white characters
+ }
+ text = new String(source, start, fragment.getLength());
+ assumeEquals(this.prefix+"Misplaced text element at <"+fragment.getStartPosition()+">: ", text, ((TextElement) fragment).getText());
+ start += fragment.getLength();
+ // reset fragment as simple name to avoid issue with next text element
+ fragment = name;
+ tagStart += (start- tagStart) - name.getLength();
+ } else {
+ if (previousFragment == null) {
+ if (tagName != null && (source[tagStart] == '\r' || source[tagStart] == '\n')) {
+ while (source[tagStart] == '*' || Character.isWhitespace(source[tagStart])) {
+ tagStart++; // purge non-stored characters
+ }
+ }
+ } else {
+ if (previousFragment.getNodeType() == ASTNode.TEXT_ELEMENT) {
+ assumeTrue(this.prefix+"Wrong length at <"+previousFragment.getStartPosition()+"> for text element "+previousFragment, (source[tagStart] == '\r' /* && source[tagStart+1] == '\n' */ || source[tagStart] == '\n'));
+ while (source[tagStart] == '*' || Character.isWhitespace(source[tagStart])) {
+ tagStart++; // purge non-stored characters
+ }
+ } else if (TagElement.TAG_PARAM.equals(tagName) && previousFragment.getNodeType() == ASTNode.SIMPLE_NAME && ((TextElement)fragment).getText().equals(">")) {
+ while (source[tagStart] == ' ' || Character.isWhitespace(source[tagStart])) {
+ tagStart++; // purge white characters
+ }
+ } else {
+ int start = tagStart;
+ boolean newLine = false;
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ if (source[tagStart] == '\r' || source[tagStart] == '\n') {
+ newLine = true;
+ }
+ }
+ if (newLine) tagStart = start;
+ }
+ }
+ text = new String(source, tagStart, fragment.getLength());
+ assumeEquals(this.prefix+"Misplaced text element at <"+fragment.getStartPosition()+">: ", text, ((TextElement) fragment).getText());
+ }
+ } else {
+ while (source[tagStart] == '*' || Character.isWhitespace(source[tagStart])) {
+ tagStart++; // purge non-stored characters
+ }
+ if (fragment.getNodeType() == ASTNode.SIMPLE_NAME || fragment.getNodeType() == ASTNode.QUALIFIED_NAME) {
+ verifyNamePositions(tagStart, (Name) fragment, source);
+ } else if (fragment.getNodeType() == ASTNode.TAG_ELEMENT) {
+ TagElement inlineTag = (TagElement) fragment;
+ assumeEquals(this.prefix+"Tag element <"+inlineTag+"> has wrong start position", tagStart, inlineTag.getStartPosition());
+ verifyPositions(inlineTag, source);
+ } else if (fragment.getNodeType() == ASTNode.MEMBER_REF) {
+ MemberRef memberRef = (MemberRef) fragment;
+ // Store start position
+ int start = tagStart;
+ // Verify qualifier position
+ Name qualifier = memberRef.getQualifier();
+ if (qualifier != null) {
+ verifyNamePositions(start, qualifier, source);
+ start += qualifier.getLength();
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+ }
+ // Verify member separator position
+ assumeEquals(this.prefix+"Misplaced # separator at <"+start+"> for member ref "+memberRef, '#', source[start]);
+ start++;
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+ // Verify member name position
+ Name name = memberRef.getName();
+ text = new String(source, start, name.getLength());
+ assumeEquals(this.prefix+"Misplaced member ref at <"+start+">: ", text, name.toString());
+ verifyNamePositions(start, name, source);
+ } else if (fragment.getNodeType() == ASTNode.METHOD_REF) {
+ MethodRef methodRef = (MethodRef) fragment;
+ // Store start position
+ int start = tagStart;
+ // Verify qualifier position
+ Name qualifier = methodRef.getQualifier();
+ if (qualifier != null) {
+ verifyNamePositions(start, qualifier, source);
+ start += qualifier.getLength();
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+ }
+ // Verify member separator position
+ assumeEquals(this.prefix+"Misplaced # separator at <"+start+"> for method ref: "+methodRef, '#', source[start]);
+ start++;
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+ // Verify member name position
+ Name name = methodRef.getName();
+ int nameLength = name.getLength();
+ text = new String(source, start, nameLength);
+ if (!text.equals(name.toString())) { // may have qualified constructor reference for inner classes
+ if (methodRef.getQualifier().isQualifiedName()) {
+ text = new String(source, start, methodRef.getQualifier().getLength());
+ assumeEquals(this.prefix+"Misplaced method ref name at <"+start+">: ", text, methodRef.getQualifier().toString());
+ while (source[start] != '.' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+ start++;
+ } else {
+ while (source[start] != '.' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+ start++;
+ text = new String(source, start, nameLength);
+ assumeEquals(this.prefix+"Misplaced method ref name at <"+start+">: ", text, name.toString());
+ }
+ }
+ verifyNamePositions(start, name, source);
+ start += nameLength;
+ // Verify arguments starting open parenthesis
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+// assumeEquals(prefix+"Misplaced ( at <"+start+"> for method ref: "+methodRef, '(', source[start]);
+ if (source[start] == '(') { // now method reference may have no parenthesis...
+ start++;
+ // Verify parameters
+ Iterator parameters = methodRef.parameters().listIterator();
+ while (parameters.hasNext()) {
+ MethodRefParameter param = (MethodRefParameter) parameters.next();
+ boolean lastParam = !parameters.hasNext();
+ // Verify parameter type positions
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++; // purge non-stored characters
+ }
+ Type type = param.getType();
+ if (type.isSimpleType()) {
+ verifyNamePositions(start, ((SimpleType)type).getName(), source);
+ } else if (type.isPrimitiveType()) {
+ text = new String(source, start, type.getLength());
+ assumeEquals(this.prefix+"Misplaced method ref parameter type at <"+start+"> for method ref: "+methodRef, text, type.toString());
+ } else if (type.isArrayType()) {
+ Type elementType = ((ArrayType) param.getType()).getElementType();
+ if (elementType.isSimpleType()) {
+ verifyNamePositions(start, ((SimpleType)elementType).getName(), source);
+ } else if (elementType.isPrimitiveType()) {
+ text = new String(source, start, elementType.getLength());
+ assumeEquals(this.prefix+"Misplaced method ref parameter type at <"+start+"> for method ref: "+methodRef, text, elementType.toString());
+ }
+ }
+ start += type.getLength();
+ // if last param then perhaps a varargs
+ while (Character.isWhitespace(source[start])) { // do NOT accept '*' in parameter declaration
+ start++; // purge non-stored characters
+ }
+ if (lastParam && this.astLevel != AST.JLS2 && param.isVarargs()) {
+ for (int p=0;p<3;p++) {
+ assumeTrue(this.prefix+"Missing ellipsis for vararg method ref parameter at <"+start+"> for method ref: "+methodRef, source[start++]=='.');
+ }
+ }
+ // Verify parameter name positions
+ while (Character.isWhitespace(source[start])) { // do NOT accept '*' in parameter declaration
+ start++; // purge non-stored characters
+ }
+ name = param.getName();
+ if (name != null) {
+ text = new String(source, start, name.getLength());
+ assumeEquals(this.prefix+"Misplaced method ref parameter name at <"+start+"> for method ref: "+methodRef, text, name.toString());
+ start += name.getLength();
+ }
+ // Verify end parameter declaration
+ while (source[start] == '*' || Character.isWhitespace(source[start])) {
+ start++;
+ }
+ assumeTrue(this.prefix+"Misplaced parameter end at <"+start+"> for method ref: "+methodRef, source[start] == ',' || source[start] == ')');
+ start++;
+ if (source[start] == ')') {
+ break;
+ }
+ }
+ }
+ }
+ }
+ tagStart += fragment.getLength();
+ previousFragment = fragment;
+ }
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345
+ if (!(TAG_CODE.equalsIgnoreCase(tagName) || !TAG_LITERAL.equalsIgnoreCase(tagName)) && tagElement.isNested()) {
+ assumeEquals(this.prefix+"Wrong end character at <"+tagStart+"> for "+tagElement, '}', source[tagStart++]);
+ }
+ }
+
+ /*
+ * Verify each name component positions.
+ */
+ private void verifyNamePositions(int nameStart, Name name, char[] source) {
+ if (name.isQualifiedName()) {
+ QualifiedName qualified = (QualifiedName) name;
+ int start = qualified.getName().getStartPosition();
+ String str = new String(source, start, qualified.getName().getLength());
+ assumeEquals(this.prefix+"Misplaced or wrong name for qualified name: "+name, str, qualified.getName().toString());
+ verifyNamePositions(nameStart, ((QualifiedName) name).getQualifier(), source);
+ }
+ String str = new String(source, nameStart, name.getLength());
+ if (str.indexOf('\n') < 0) { // cannot compare if text contains new line
+ assumeEquals(this.prefix+"Misplaced name for qualified name: ", str, name.toString());
+ } else if (this.debug) {
+ System.out.println(this.prefix+"Name contains new line for qualified name: "+name);
+ }
+ }
+
+ /*
+ * Verify that bindings of Javadoc comment structure are resolved or not.
+ * For expected unresolved binding, verify that following text starts with 'Unknown'
+ */
+ private void verifyBindings(Javadoc docComment) {
+ boolean stop = this.stopOnFailure;
+// stopOnFailure = false;
+ // Verify tags
+ Iterator tags = docComment.tags().listIterator();
+ while (tags.hasNext()) {
+ verifyBindings((TagElement) tags.next());
+ }
+ this.stopOnFailure = stop;
+ assertTrue(!stop || this.failures.size()==0);
+ }
+
+ /*
+ * Verify that bindings of Javadoc tag structure are resolved or not.
+ * For expected unresolved binding, verify that following text starts with 'Unknown'
+ */
+ private void verifyBindings(TagElement tagElement) {
+ // Verify each fragment
+ Iterator elements = tagElement.fragments().listIterator();
+ IBinding previousBinding = null;
+ ASTNode previousFragment = null;
+ boolean resolvedBinding = false;
+ while (elements.hasNext()) {
+ ASTNode fragment = (ASTNode) elements.next();
+ if (fragment.getNodeType() == ASTNode.TEXT_ELEMENT) {
+ TextElement text = (TextElement) fragment;
+ if (resolvedBinding) {
+ if (previousBinding == null) {
+ assumeTrue(this.prefix+"Reference '"+previousFragment+"' should be bound!", text.getText().trim().indexOf("Unknown")>=0);
+ } else {
+ assumeTrue(this.prefix+"Unknown reference '"+previousFragment+"' should NOT be bound!", text.getText().trim().indexOf("Unknown")<0);
+ }
+ }
+ previousBinding = null;
+ resolvedBinding = false;
+ } else if (fragment.getNodeType() == ASTNode.TAG_ELEMENT) {
+ verifyBindings((TagElement) fragment);
+ previousBinding = null;
+ resolvedBinding = false;
+ } else {
+ resolvedBinding = true;
+ if (fragment.getNodeType() == ASTNode.SIMPLE_NAME) {
+ previousBinding = ((Name)fragment).resolveBinding();
+ } else if (fragment.getNodeType() == ASTNode.QUALIFIED_NAME) {
+ QualifiedName name = (QualifiedName) fragment;
+ previousBinding = name.resolveBinding();
+ verifyNameBindings(name);
+ } else if (fragment.getNodeType() == ASTNode.MODULE_QUALIFIED_NAME) {
+ ModuleQualifiedName name = (ModuleQualifiedName) fragment;
+ Name typeName = name.getName();
+ if (typeName != null) {
+ if (typeName.getNodeType() == ASTNode.SIMPLE_NAME) {
+ previousBinding = ((Name)fragment).resolveBinding();
+ } else if (typeName.getNodeType() == ASTNode.QUALIFIED_NAME) {
+ QualifiedName qname = (QualifiedName) typeName;
+ previousBinding = qname.resolveBinding();
+ verifyNameBindings(qname);
+ }
+ }
+ Name mName = name.getModuleQualifier();
+ if (mName.getNodeType() == ASTNode.SIMPLE_NAME) {
+ previousBinding = ((Name)fragment).resolveBinding();
+ } else if (mName.getNodeType() == ASTNode.QUALIFIED_NAME) {
+ QualifiedName qname = (QualifiedName) mName;
+ previousBinding = qname.resolveBinding();
+ }
+ assumeNotNull(this.prefix+""+name+" binding was not foundfound in "+fragment, previousBinding);
+ assumeTrue(this.prefix+""+name+" binding is not module binding "+fragment, previousBinding instanceof IModuleBinding);
+ } else if (fragment.getNodeType() == ASTNode.MEMBER_REF) {
+ MemberRef memberRef = (MemberRef) fragment;
+ previousBinding = memberRef.resolveBinding();
+ if (previousBinding != null) {
+ SimpleName name = memberRef.getName();
+ assumeNotNull(this.prefix+""+name+" binding was not foundfound in "+fragment, name.resolveBinding());
+ verifyNameBindings(memberRef.getQualifier());
+ }
+ } else if (fragment.getNodeType() == ASTNode.METHOD_REF) {
+ MethodRef methodRef = (MethodRef) fragment;
+ previousBinding = methodRef.resolveBinding();
+ if (previousBinding != null) {
+ SimpleName methodName = methodRef.getName();
+ IBinding methNameBinding = methodName.resolveBinding();
+ Name methodQualifier = methodRef.getQualifier();
+ // TODO (frederic) Replace the two following lines by commented block when bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=62650 will be fixed
+ assumeNotNull(this.prefix+""+methodName+" binding was not found in "+fragment, methNameBinding);
+ verifyNameBindings(methodQualifier);
+ /*
+ if (methodQualifier == null) {
+ if (methNameBinding == null) {
+ char firstChar = methodName.getIdentifier().charAt(0);
+ if (Character.isUpperCase(firstChar)) {
+ // assume that selector starting with uppercase is for constructor => signal that binding is null
+ System.out.println(prefix+"Binding for selector of '"+methodRef+"' is null.");
+ }
+ } else {
+ if (methNameBinding.getName().equals(methodName.getIdentifier())) { // binding is not null only for constructor
+ assumeNotNull(prefix+""+methodName+" binding was not found!",methNameBinding);
+ } else {
+ assumeNull(prefix+""+methodName+" binding should be null!", methNameBinding);
+ }
+ }
+ } else {
+ SimpleName methodSimpleType = null;
+ if (methodQualifier.isQualifiedName()) {
+ methodSimpleType = ((QualifiedName)methodQualifier).getName();
+ } else {
+ methodSimpleType = (SimpleName) methodQualifier;
+ }
+ if (methodSimpleType.getIdentifier().equals(methodName.getIdentifier())) { // binding is not null only for constructor
+ assumeNotNull(prefix+""+methodName+" binding was not found!",methNameBinding);
+ } else {
+ assumeNull(prefix+""+methodName+" binding should be null!", methNameBinding);
+ }
+ verifyNameBindings(methodRef.getQualifier());
+ }
+ */
+ Iterator parameters = methodRef.parameters().listIterator();
+ while (parameters.hasNext()) {
+ MethodRefParameter param = (MethodRefParameter) parameters.next();
+ Type type = param.getType();
+ assumeNotNull(this.prefix+""+type+" binding was not found in "+fragment, type.resolveBinding());
+ if (type.isSimpleType()) {
+ verifyNameBindings(((SimpleType)type).getName());
+ } else if (type.isArrayType()) {
+ Type elementType = ((ArrayType) param.getType()).getElementType();
+ assumeNotNull(this.prefix+""+elementType+" binding was not found in "+fragment, elementType.resolveBinding());
+ if (elementType.isSimpleType()) {
+ verifyNameBindings(((SimpleType)elementType).getName());
+ }
+ }
+ // Do not verify parameter name as no binding is expected for them
+ }
+ }
+ }
+ }
+ previousFragment = fragment;
+ }
+ assumeTrue(this.prefix+"Reference '"+(previousFragment==null?tagElement:previousFragment)+"' should be bound!", (!resolvedBinding || previousBinding != null));
+ }
+
+ /*
+ * Verify each name component binding.
+ */
+ private void verifyNameBindings(Name name) {
+ if (name != null) {
+ IBinding binding = name.resolveBinding();
+ if (name.toString().indexOf("Unknown") > 0) {
+ assumeNull(this.prefix+name+" binding should be null!", binding);
+ } else {
+ assumeNotNull(this.prefix+name+" binding was not found!", binding);
+ }
+ SimpleName simpleName = null;
+ int index = 0;
+ while (name.isQualifiedName()) {
+ simpleName = ((QualifiedName) name).getName();
+ binding = simpleName.resolveBinding();
+ if (simpleName.getIdentifier().equalsIgnoreCase("Unknown")) {
+ assumeNull(this.prefix+simpleName+" binding should be null!", binding);
+ } else {
+ assumeNotNull(this.prefix+simpleName+" binding was not found!", binding);
+ }
+ if (index > 0 && this.packageBinding) {
+ assumeEquals(this.prefix+"Wrong binding type!", IBinding.PACKAGE, binding.getKind());
+ }
+ index++;
+ name = ((QualifiedName) name).getQualifier();
+ binding = name.resolveBinding();
+ if (name.toString().indexOf("Unknown") > 0) {
+ assumeNull(this.prefix+name+" binding should be null!", binding);
+ } else {
+ assumeNotNull(this.prefix+name+" binding was not found!", binding);
+ }
+ if (this.packageBinding) {
+ assumeEquals(this.prefix+"Wrong binding type!", IBinding.PACKAGE, binding.getKind());
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#setUp()
+ */
+ protected void verifyComments(String test) throws JavaModelException {
+ ICompilationUnit[] units = getCompilationUnits("Converter" , "src", "javadoc."+test); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ for (int i=0; i<units.length; i++) {
+ verifyComments(units[i]);
+ }
+ }
+
+ /*
+ * Verify the comments of a compilation unit.
+ */
+ protected void verifyWorkingCopiesComments() throws JavaModelException {
+ assumeNotNull("No working copies to verify!", this.workingCopies);
+ int length = this.workingCopies.length;
+ assumeTrue("We need to have at least one working copy to verify!", length>0);
+ for (int i=0; i<length; i++) {
+ verifyComments(this.workingCopies[i]);
+ }
+ }
+
+ /*
+ * Verify the comments of a compilation unit.
+ */
+ protected CompilationUnit verifyComments(ICompilationUnit unit) throws JavaModelException {
+ // Get test file
+ this.sourceUnit = unit;
+ this.prefix = unit.getElementName()+": ";
+
+ // Get current project
+ String sourceStr = this.sourceUnit.getSource();
+ if (this.savedOptions != null && !this.sourceUnit.getJavaProject().getElementName().equals(this.currentProject.getElementName())) {
+ this.currentProject.setOptions(this.savedOptions);
+ this.savedOptions = null;
+ }
+ this.currentProject = this.sourceUnit.getJavaProject();
+ if (this.savedOptions == null) this.savedOptions = this.currentProject.getOptions(false);
+
+ // set up java project options
+ this.currentProject.setOption(JavaCore.COMPILER_PB_INVALID_JAVADOC, this.compilerOption);
+ this.currentProject.setOption(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS, this.compilerOption);
+ this.currentProject.setOption(JavaCore.COMPILER_PB_MISSING_JAVADOC_COMMENTS, this.compilerOption);
+ this.currentProject.setOption(JavaCore.COMPILER_PB_METHOD_WITH_CONSTRUCTOR_NAME, JavaCore.IGNORE);
+ this.currentProject.setOption(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, this.docCommentSupport);
+
+ // Verify source regardings converted comments
+ char[] source = sourceStr.toCharArray();
+ String fileName = unit.getPath().toString();
+ try {
+ return verifyComments(fileName, source);
+ }
+ catch (RuntimeException ex) {
+ TEST_COUNTERS[3]++;
+ throw ex;
+ }
+ }
+
+ protected CompilationUnit verifyComments(String fileName, char[] source) {
+ return verifyComments(fileName, source, null);
+ }
+
+ protected CompilationUnit verifyComments(String fileName, char[] source, Map options) {
+
+ // Verify comments either in unicode or not
+ char[] testedSource = source;
+ if (UNICODE) {
+ testedSource = getUnicodeSource(source);
+ }
+
+ // Verify comments either in unicode or not
+ else if (this.unix) {
+ testedSource = getUnixSource(source);
+ }
+
+ // Get comments infos from test file
+ setSourceComment(testedSource);
+
+ // Create DOM AST nodes hierarchy
+ List unitComments = null;
+ String sourceLevel = null;
+ String complianceLevel = null;
+ if (this.currentProject != null) {
+ complianceLevel = this.currentProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
+ sourceLevel = this.currentProject.getOption(JavaCore.COMPILER_SOURCE, true);
+ this.currentProject.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_15);
+ this.currentProject.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_15);
+ }
+ CompilationUnit compilUnit = (CompilationUnit) runConversion(testedSource, fileName, this.currentProject, options);
+ if (this.compilerOption.equals(JavaCore.ERROR)) {
+ assumeEquals(this.prefix+"Unexpected problems", 0, compilUnit.getProblems().length); //$NON-NLS-1$
+ } else if (this.compilerOption.equals(JavaCore.WARNING)) {
+ IProblem[] problemsList = compilUnit.getProblems();
+ int length = problemsList.length;
+ if (length > 0) {
+ this.problems.append(" - "+this.prefix+length+" problems:"); //$NON-NLS-1$
+ for (int i = 0; i < problemsList.length; i++) {
+ this.problems.append(" + ");
+ this.problems.append(problemsList[i]);
+ this.problems.append("\n");
+ }
+ }
+ }
+ unitComments = compilUnit.getCommentList();
+ assumeNotNull(this.prefix+"Unexpected problems", unitComments);
+
+ // Basic comments verification
+ int size = unitComments.size();
+ assumeEquals(this.prefix+"Wrong number of comments!", this.comments.size(), size);
+
+ // Verify comments positions and bindings
+ for (int i=0; i<size; i++) {
+ Comment comment = (Comment) unitComments.get(i);
+ List tags = (List) this.allTags.get(i);
+ // Verify flattened content
+ String stringComment = (String) this.comments.get(i);
+// ASTConverterJavadocFlattener printer = new ASTConverterJavadocFlattener(stringComment);
+// comment.accept(printer);
+ String text = new String(testedSource, comment.getStartPosition(), comment.getLength());
+ assumeEquals(this.prefix+"Flattened comment does NOT match source!", stringComment, text);
+ // Verify javdoc tags positions and bindings
+ if (comment.isDocComment()) {
+ Javadoc docComment = (Javadoc)comment;
+ if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
+ assumeEquals(this.prefix+"Invalid tags number in javadoc:\n"+docComment+"\n", tags.size(), allTags(docComment));
+ verifyPositions(docComment, testedSource);
+ if (this.resolveBinding) {
+ verifyBindings(docComment);
+ }
+ } else {
+ assumeEquals("Javadoc should be flat!", 0, docComment.tags().size());
+ }
+ }
+ }
+
+ /* Verify each javadoc: not implemented yet
+ Iterator types = compilUnit.types().listIterator();
+ while (types.hasNext()) {
+ TypeDeclaration typeDeclaration = (TypeDeclaration) types.next();
+ verifyJavadoc(typeDeclaration.getJavadoc());
+ }
+ */
+
+ if (sourceLevel != null) {
+ this.currentProject.setOption(JavaCore.COMPILER_COMPLIANCE, complianceLevel);
+ this.currentProject.setOption(JavaCore.COMPILER_SOURCE, sourceLevel);
+ }
+ // Return compilation unit for possible further verifications
+ return compilUnit;
+ }
+
+ public void testJavadoc1() throws JavaModelException {
+ this.moduleUnit = getWorkingCopy("/Converter_15_1/src/module-info.java",
+ "module test1.one.two {\r\n" +
+ "}");
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy("/Converter_15_1/src/javadoc/X.java",
+ "package javadoc;\n" +
+ "public class X {\n" +
+ " /** \n" +
+ " * @see test1.one.two/ \n" +
+ " * {@link test1.one.two/} \n" +
+ " * {@linkplain test1.one.two/} \n" +
+ " */ \n" +
+ " public static void foo(Object object) {\n" +
+ " }\n" +
+ "}\n"
+ );
+ CompilationUnit compilUnit = verifyComments(this.workingCopies[0]);
+ if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
+ // Get comments
+ List unitComments = compilUnit.getCommentList();
+ int size = unitComments.size();
+ assertEquals("Wrong number of comments", 1, size);
+ Javadoc[] javadocs = new Javadoc[size];
+ Iterator iterator = unitComments.iterator();
+ for (int i=0; i<size; i++) {
+ Comment comment = (Comment) iterator.next();
+ assertEquals("Expect javadoc for comment: "+comment, ASTNode.JAVADOC, comment.getNodeType());
+ javadocs[i] = (Javadoc) comment;
+ }
+
+ // Verify member type declaration start
+ ASTNode node = getASTNode(compilUnit, 0, 0);
+ assertEquals("Expected method declaration for node: "+node, ASTNode.METHOD_DECLARATION, node.getNodeType());
+ MethodDeclaration methodDeclaration = (MethodDeclaration) node;
+ int javadocStart = javadocs[0].getStartPosition();
+ assertEquals("Invalid start position for MethodDeclaration: "+methodDeclaration, methodDeclaration.getStartPosition(), javadocStart);
+ }
+ }
+
+ public void testJavadoc3() throws JavaModelException {
+ this.moduleUnit = getWorkingCopy("/Converter_15_1/src/module-info.java",
+ "module test1.one.two {\r\n" +
+ "}");
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy("/Converter_15_1/src/javadoc/X.java",
+ "package javadoc;\n" +
+ "public class X {\n" +
+ " /** \n" +
+ " * @see test1.one.two/javadoc.X#foo \n" +
+ " */ \n" +
+ " public static void foo(Object object) {\n" +
+ " }\n" +
+ "}\n"
+ );
+ CompilationUnit compilUnit = verifyComments(this.workingCopies[0]);
+ if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
+ // Get comments
+ List unitComments = compilUnit.getCommentList();
+ int size = unitComments.size();
+ assertEquals("Wrong number of comments", 1, size);
+ Javadoc[] javadocs = new Javadoc[size];
+ Iterator iterator = unitComments.iterator();
+ for (int i=0; i<size; i++) {
+ Comment comment = (Comment) iterator.next();
+ assertEquals("Expect javadoc for comment: "+comment, ASTNode.JAVADOC, comment.getNodeType());
+ javadocs[i] = (Javadoc) comment;
+ }
+
+ // Verify member type declaration start
+ ASTNode node = getASTNode(compilUnit, 0, 0);
+ assertEquals("Expected method declaration for node: "+node, ASTNode.METHOD_DECLARATION, node.getNodeType());
+ MethodDeclaration methodDeclaration = (MethodDeclaration) node;
+ int javadocStart = javadocs[0].getStartPosition();
+ assertEquals("Invalid start position for MethodDeclaration: "+methodDeclaration, methodDeclaration.getStartPosition(), javadocStart);
+ }
+ }
+
+ public void testJavadoc2() throws JavaModelException {
+ this.moduleUnit = getWorkingCopy("/Converter_15_1/src/module-info.java",
+ "module test1.one.two {\r\n" +
+ "}");
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy("/Converter_15_1/src/javadoc/X.java",
+ "package javadoc;\n" +
+ "public class X {\n" +
+ " /** \n" +
+ " * @see test1.one.two/javadoc.X \n" +
+ " */ \n" +
+ " public static void foo(Object object) {\n" +
+ " }\n" +
+ "}\n"
+ );
+ CompilationUnit compilUnit = verifyComments(this.workingCopies[0]);
+ if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
+ // Get comments
+ List unitComments = compilUnit.getCommentList();
+ int size = unitComments.size();
+ assertEquals("Wrong number of comments", 1, size);
+ Javadoc[] javadocs = new Javadoc[size];
+ Iterator iterator = unitComments.iterator();
+ for (int i=0; i<size; i++) {
+ Comment comment = (Comment) iterator.next();
+ assertEquals("Expect javadoc for comment: "+comment, ASTNode.JAVADOC, comment.getNodeType());
+ javadocs[i] = (Javadoc) comment;
+ }
+
+ // Verify member type declaration start
+ ASTNode node = getASTNode(compilUnit, 0, 0);
+ assertEquals("Expected method declaration for node: "+node, ASTNode.METHOD_DECLARATION, node.getNodeType());
+ MethodDeclaration methodDeclaration = (MethodDeclaration) node;
+ int javadocStart = javadocs[0].getStartPosition();
+ assertEquals("Invalid start position for MethodDeclaration: "+methodDeclaration, methodDeclaration.getStartPosition(), javadocStart);
+ }
+ }
+}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java
index 7acd5398fb..8f95c0368d 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
@@ -236,6 +240,7 @@ public abstract class ConverterTestSetup extends AbstractASTTests {
setUpJavaProject("Converter13", "13"); //$NON-NLS-1$ //$NON-NLS-2$
setUpJavaProject("Converter14", "14"); //$NON-NLS-1$ //$NON-NLS-2$
setUpJavaProject("Converter_15", "15"); //$NON-NLS-1$ //$NON-NLS-2$
+ setUpJavaProject("Converter_15_1", "15"); //$NON-NLS-1$ //$NON-NLS-2$
waitUntilIndexesReady(); // needed to find secondary types
PROJECT_SETUP = true;
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/RunConverterTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/RunConverterTests.java
index 0e9b411d14..edd2325e05 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/RunConverterTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/RunConverterTests.java
@@ -39,6 +39,7 @@ public static Class[] getAllTestClasses() {
ASTConverterBugsTest.class,
ASTConverterBugsTestJLS3.class,
ASTConverterJavadocTest.class,
+ ASTConverterJavadocTest_15.class,
ASTConverter15Test.class,
ASTConverter16Test.class,
ASTConverter17Test.class,
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath
new file mode 100644
index 0000000000..0d9ce0d3bb
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="var" path="CONVERTER_JCL15_LIB" sourcepath="CONVERTER_JCL15_SRC" rootpath="CONVERTER_JCL_SRCROOT"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.project b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.project
new file mode 100644
index 0000000000..8dad5b66b3
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>Converter_15_1</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/javadoc/test0001/X.java b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/javadoc/test0001/X.java
new file mode 100644
index 0000000000..7e327a4494
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/javadoc/test0001/X.java
@@ -0,0 +1,7 @@
+public class X {
+ /**
+ * @see test1.one.two/
+ */
+ public void foo(X this) {
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/module-info.java b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/module-info.java
new file mode 100644
index 0000000000..fa1d49312e
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/src/module-info.java
@@ -0,0 +1,4 @@
+public class X {
+ public void foo(X this) {
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jdt.core/.settings/.api_filters b/org.eclipse.jdt.core/.settings/.api_filters
index a8bf2a3c05..3cd0ea0ffa 100644
--- a/org.eclipse.jdt.core/.settings/.api_filters
+++ b/org.eclipse.jdt.core/.settings/.api_filters
@@ -159,6 +159,14 @@
</message_arguments>
</filter>
</resource>
+ <resource path="dom/org/eclipse/jdt/core/dom/ModuleQualifiedName.java" type="org.eclipse.jdt.core.dom.ModuleQualifiedName">
+ <filter id="576778288">
+ <message_arguments>
+ <message_argument value="Name"/>
+ <message_argument value="ModuleQualifiedName"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="dom/org/eclipse/jdt/core/dom/Name.java" type="org.eclipse.jdt.core.dom.Name">
<filter id="576725006">
<message_arguments>
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionJavadocParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionJavadocParser.java
index f2c1f77d8d..bc59b3b5ad 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionJavadocParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionJavadocParser.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
@@ -550,8 +554,13 @@ public class CompletionJavadocParser extends JavadocParser {
@Override
protected boolean parseReference() throws InvalidInputException {
+ return parseReference(false);
+ }
+
+ @Override
+ protected boolean parseReference(boolean allowModule) throws InvalidInputException {
boolean completed = this.completionNode != null;
- boolean valid = super.parseReference();
+ boolean valid = super.parseReference(allowModule);
if (!completed && this.completionNode != null) {
this.completionNode.addCompletionFlags(CompletionOnJavadoc.FORMAL_REFERENCE);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java
index 5fea8a60ff..1ec743ee8b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Javadoc.java
@@ -150,6 +150,27 @@ public class Javadoc extends ASTNode {
}
}
}
+ } else if (expression instanceof JavadocModuleReference) {
+ JavadocModuleReference modRef = (JavadocModuleReference) expression;
+ if (modRef.typeReference != null) {
+ if (modRef.typeReference.sourceStart == start) {
+ return modRef.typeReference;
+ }
+ }
+ } else if (expression instanceof JavadocFieldReference) {
+ JavadocFieldReference fieldRef = (JavadocFieldReference) expression;
+ if (fieldRef.receiver instanceof JavadocModuleReference) {
+ JavadocModuleReference modRef = (JavadocModuleReference) fieldRef.receiver;
+ if (modRef.sourceStart == start) {
+ return modRef;
+ } else {
+ if (modRef.typeReference != null) {
+ if (modRef.typeReference.sourceStart == start) {
+ return modRef.typeReference;
+ }
+ }
+ }
+ }
}
}
}
@@ -487,8 +508,8 @@ public class Javadoc extends ASTNode {
ref.resolve(scope);
ModuleReference mRef = ref.getModuleReference();
if (mRef != null) {
- ModuleBinding mType = mRef.binding;
- if (verifyModuleReference(reference, reference, scope, source15, mType, mType.modifiers)) {
+ ModuleBinding mType = mRef.resolve(scope);
+ if (mType != null && verifyModuleReference(reference, reference, scope, source15, mType, mType.modifiers)) {
TypeReference tRef= ref.getTypeReference();
if ((tRef instanceof JavadocSingleTypeReference || tRef instanceof JavadocQualifiedTypeReference) && tRef.resolvedType instanceof ReferenceBinding) {
ReferenceBinding resolvedType = (ReferenceBinding) tRef.resolvedType;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocModuleReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocModuleReference.java
index 4650b1f8ca..0465c55df9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocModuleReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocModuleReference.java
@@ -36,6 +36,8 @@ public class JavadocModuleReference extends Expression implements IJavadocTypeRe
this.moduleReference = new ModuleReference(sources, pos);
this.tagSourceStart = tagStart;
this.tagSourceEnd = tagEnd;
+ this.sourceStart = this.moduleReference.sourceStart;
+ this.sourceEnd = this.moduleReference.sourceEnd;
this.bits |= ASTNode.InsideJavadoc;
}
@@ -65,6 +67,9 @@ public class JavadocModuleReference extends Expression implements IJavadocTypeRe
public void setTypeReference(TypeReference typeReference) {
this.typeReference = typeReference;
+ if (this.typeReference != null) {
+ this.sourceEnd = this.typeReference.sourceEnd;
+ }
}
public ModuleReference getModuleReference() {
@@ -73,6 +78,8 @@ public class JavadocModuleReference extends Expression implements IJavadocTypeRe
public void setModuleReference(ModuleReference moduleReference) {
this.moduleReference = moduleReference;
+ this.sourceStart = this.moduleReference.sourceStart;
+ this.sourceStart = this.moduleReference.sourceEnd;
}
@Override
@@ -80,8 +87,8 @@ public class JavadocModuleReference extends Expression implements IJavadocTypeRe
if (this.moduleReference != null) {
output.append(this.moduleReference.moduleName);
}
+ output.append('/');
if (this.typeReference != null) {
- output.append('/');
this.typeReference.printExpression(indent, output);
}
return output;
@@ -91,7 +98,11 @@ public class JavadocModuleReference extends Expression implements IJavadocTypeRe
return this.moduleReference.resolve(scope);
}
- public ModuleBinding resolveModule(BlockScope scope) {
+ private ModuleBinding resolveModule(BlockScope scope) {
+ return this.moduleReference.resolve(scope);
+ }
+
+ private ModuleBinding resolveModule(ClassScope scope) {
return this.moduleReference.resolve(scope);
}
@@ -107,6 +118,8 @@ public class JavadocModuleReference extends Expression implements IJavadocTypeRe
@Override
public TypeBinding resolveType(ClassScope classScope) {
+ this.resolveModule(classScope);
+ assert(this.moduleReference.binding != null);
if (this.typeReference != null) {
return this.typeReference.resolveType(classScope, -1);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java
index dc9dded36d..94472996c1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java
@@ -1076,10 +1076,14 @@ public abstract class AbstractCommentParser implements JavadocTagConstants {
&& (moduleRefTokenCount > 0));
}
+ protected Object parseQualifiedName(boolean reset) throws InvalidInputException {
+ return parseQualifiedName(reset, false);
+ }
+
/*
* Parse a qualified name and built a type reference if the syntax is valid.
*/
- protected Object parseQualifiedName(boolean reset) throws InvalidInputException {
+ protected Object parseQualifiedName(boolean reset, boolean allowModule) throws InvalidInputException {
// Reset identifier stack if requested
if (reset) {
@@ -1111,7 +1115,7 @@ public abstract class AbstractCommentParser implements JavadocTagConstants {
}
pushIdentifier(iToken == 0, false);
consumeToken();
- if (parsingJava15Plus && getChar() == '/' ) {
+ if (allowModule && parsingJava15Plus && getChar() == '/') {
lookForModule = true;
}
break;
@@ -1187,7 +1191,7 @@ public abstract class AbstractCommentParser implements JavadocTagConstants {
//$FALL-THROUGH$
case TerminalTokens.TokenNameDIVIDE:
if (parsingJava15Plus && lookForModule) {
- if (((iToken & 1) == 0) && (moduleRefTokenCount > 0)) { // '/' must be even token
+ if (((iToken & 1) == 0) || (moduleRefTokenCount > 0)) { // '/' must be even token
throw new InvalidInputException();
}
moduleRefTokenCount = (iToken+1) / 2;
@@ -1244,10 +1248,14 @@ public abstract class AbstractCommentParser implements JavadocTagConstants {
return createTypeReference(primitiveToken);
}
+ protected boolean parseReference() throws InvalidInputException {
+ return parseReference(false);
+ }
+
/*
* Parse a reference in @see tag
*/
- protected boolean parseReference() throws InvalidInputException {
+ protected boolean parseReference(boolean allowModule) throws InvalidInputException {
int currentPosition = this.scanner.currentPosition;
try {
Object typeRef = null;
@@ -1337,7 +1345,7 @@ public abstract class AbstractCommentParser implements JavadocTagConstants {
case TerminalTokens.TokenNameIdentifier :
if (typeRef == null) {
typeRefStartPosition = this.scanner.getCurrentTokenStartPosition();
- typeRef = parseQualifiedName(true);
+ typeRef = parseQualifiedName(true, allowModule);
if (this.abort) return false; // May be aborted by specialized parser
break;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java
index 880b2e021e..dda60c6bdf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java
@@ -208,14 +208,24 @@ public class JavadocParser extends AbstractCommentParser {
protected Object createFieldReference(Object receiver) throws InvalidInputException {
try {
// Get receiver type
- TypeReference typeRef = (TypeReference) receiver;
+ TypeReference typeRef = null;
+ boolean useReceiver = false;
+ if (receiver instanceof JavadocModuleReference) {
+ JavadocModuleReference jRef = (JavadocModuleReference)receiver;
+ if (jRef.typeReference != null) {
+ typeRef = jRef.typeReference;
+ useReceiver = true;
+ }
+ } else {
+ typeRef = (TypeReference) receiver;
+ }
if (typeRef == null) {
char[] name = this.sourceParser.compilationUnit.getMainTypeName();
typeRef = new JavadocImplicitTypeReference(name, this.memberStart);
}
// Create field
JavadocFieldReference field = new JavadocFieldReference(this.identifierStack[0], this.identifierPositionStack[0]);
- field.receiver = typeRef;
+ field.receiver = useReceiver ? (Expression)receiver : typeRef;
field.tagSourceStart = this.tagSourceStart;
field.tagSourceEnd = this.tagSourceEnd;
field.tagValue = this.tagValue;
@@ -230,7 +240,15 @@ public class JavadocParser extends AbstractCommentParser {
protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException {
try {
// Get receiver type
- TypeReference typeRef = (TypeReference) receiver;
+ TypeReference typeRef = null;
+ if (receiver instanceof JavadocModuleReference) {
+ JavadocModuleReference jRef = (JavadocModuleReference)receiver;
+ if (jRef.typeReference != null) {
+ typeRef = jRef.typeReference;
+ }
+ } else {
+ typeRef = (TypeReference) receiver;
+ }
// Decide whether we have a constructor or not
boolean isConstructor = false;
int length = this.identifierLengthStack[0]; // may be > 1 for member class constructor reference
@@ -668,12 +686,12 @@ public class JavadocParser extends AbstractCommentParser {
if (length == TAG_LINK_LENGTH && CharOperation.equals(TAG_LINK, tagName, 0, length)) {
this.tagValue = TAG_LINK_VALUE;
if (this.inlineTagStarted || (this.kind & COMPLETION_PARSER) != 0) {
- valid= parseReference();
+ valid= parseReference(true);
}
} else if (length == TAG_LINKPLAIN_LENGTH && CharOperation.equals(TAG_LINKPLAIN, tagName, 0, length)) {
this.tagValue = TAG_LINKPLAIN_VALUE;
if (this.inlineTagStarted) {
- valid = parseReference();
+ valid = parseReference(true);
}
} else if (length == TAG_LITERAL_LENGTH && this.inlineTagStarted && CharOperation.equals(TAG_LITERAL, tagName, 0, length)) {
this.tagValue = TAG_LITERAL_VALUE;
@@ -705,7 +723,7 @@ public class JavadocParser extends AbstractCommentParser {
if (length == TAG_SEE_LENGTH && CharOperation.equals(TAG_SEE, tagName, 0, length)) {
this.tagValue = TAG_SEE_VALUE;
if (!this.inlineTagStarted) {
- valid = parseReference();
+ valid = parseReference(true);
}
} else if (length == TAG_SERIAL_LENGTH && CharOperation.equals(TAG_SERIAL, tagName, 0, length)) {
this.tagValue = TAG_SERIAL_VALUE;
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
index b2828f6412..3930658554 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
@@ -4859,6 +4859,14 @@ class ASTConverter {
protected void recordName(Name name, org.eclipse.jdt.internal.compiler.ast.ASTNode compilerNode) {
if (compilerNode != null) {
+ if (name instanceof ModuleQualifiedName &&
+ compilerNode instanceof org.eclipse.jdt.internal.compiler.ast.TypeReference) {
+ Name tName = ((ModuleQualifiedName)name).getName();
+ if (tName != null) {
+ recordName(tName, compilerNode);
+ return;
+ }
+ }
recordNodes(name, compilerNode);
if (compilerNode instanceof org.eclipse.jdt.internal.compiler.ast.TypeReference) {
org.eclipse.jdt.internal.compiler.ast.TypeReference typeRef = (org.eclipse.jdt.internal.compiler.ast.TypeReference) compilerNode;
@@ -4900,10 +4908,16 @@ class ASTConverter {
// Replace qualifier to have all nodes recorded
if (memberRef.getQualifier() != null) {
org.eclipse.jdt.internal.compiler.ast.TypeReference typeRef = null;
+ org.eclipse.jdt.internal.compiler.ast.JavadocModuleReference modRef = null;
if (compilerNode instanceof JavadocFieldReference) {
org.eclipse.jdt.internal.compiler.ast.Expression expression = ((JavadocFieldReference)compilerNode).receiver;
if (expression instanceof org.eclipse.jdt.internal.compiler.ast.TypeReference) {
typeRef = (org.eclipse.jdt.internal.compiler.ast.TypeReference) expression;
+ } else if (expression instanceof org.eclipse.jdt.internal.compiler.ast.JavadocModuleReference) {
+ modRef = (org.eclipse.jdt.internal.compiler.ast.JavadocModuleReference) expression;
+ if (modRef.typeReference != null) {
+ typeRef = modRef.typeReference;
+ }
}
}
else if (compilerNode instanceof JavadocMessageSend) {
@@ -4912,8 +4926,17 @@ class ASTConverter {
typeRef = (org.eclipse.jdt.internal.compiler.ast.TypeReference) expression;
}
}
+ Name mQual = memberRef.getQualifier();
if (typeRef != null) {
- recordName(memberRef.getQualifier(), typeRef);
+ if (mQual instanceof ModuleQualifiedName
+ && modRef != null) {
+ ModuleQualifiedName moduleQualifiedName = (ModuleQualifiedName)mQual;
+ recordName(moduleQualifiedName, modRef);
+ recordName(moduleQualifiedName.getModuleQualifier(), modRef.moduleReference);
+ recordName(moduleQualifiedName.getName(), typeRef);
+ } else {
+ recordName(memberRef.getQualifier(), typeRef);
+ }
}
}
} else if (node.getNodeType() == ASTNode.METHOD_REF) {
@@ -4952,8 +4975,12 @@ class ASTConverter {
}
recordNodes(name, compilerNode);
}
+ Name mQual= methodRef.getQualifier();
// record name and qualifier
- if (typeRef != null && methodRef.getQualifier() != null) {
+ if (typeRef != null && mQual != null) {
+ if (mQual instanceof ModuleQualifiedName) {
+ recordName(mQual, javadoc.getNodeStartingAt(mQual.getStartPosition()));
+ }
recordName(methodRef.getQualifier(), typeRef);
}
}
@@ -4987,6 +5014,19 @@ class ASTConverter {
node.getNodeType() == ASTNode.QUALIFIED_NAME) {
org.eclipse.jdt.internal.compiler.ast.ASTNode compilerNode = javadoc.getNodeStartingAt(node.getStartPosition());
recordName((Name) node, compilerNode);
+ } else if (node.getNodeType() == ASTNode.MODULE_QUALIFIED_NAME) {
+ ModuleQualifiedName mqName = (ModuleQualifiedName) node;
+ org.eclipse.jdt.internal.compiler.ast.ASTNode compilerNode = javadoc.getNodeStartingAt(mqName.getStartPosition());
+ recordName(mqName, compilerNode);
+ Name name = mqName.getName();
+ if (name != null) {
+ org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode = javadoc.getNodeStartingAt(name.getStartPosition());
+ recordName(name, internalNode);
+ }
+ if (compilerNode instanceof org.eclipse.jdt.internal.compiler.ast.JavadocModuleReference) {
+ org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode = ((org.eclipse.jdt.internal.compiler.ast.JavadocModuleReference)compilerNode).moduleReference;
+ recordNodes(mqName.getModuleQualifier(), internalNode);
+ }
} else if (node.getNodeType() == ASTNode.TAG_ELEMENT) {
// resolve member and method references binding
recordNodes(javadoc, (TagElement) node);
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTMatcher.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTMatcher.java
index b159195f94..556e2a8abe 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTMatcher.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTMatcher.java
@@ -1957,6 +1957,30 @@ public class ASTMatcher {
* @return <code>true</code> if the subtree matches, or
* <code>false</code> if they do not match or the other object has a
* different node type or is <code>null</code>
+ * @noreference
+ */
+ public boolean match(ModuleQualifiedName node, Object other) {
+ if (!(other instanceof ModuleQualifiedName)) {
+ return false;
+ }
+ ModuleQualifiedName o = (ModuleQualifiedName) other;
+ return safeSubtreeMatch(node.getModuleQualifier(), o.getModuleQualifier())
+ && safeSubtreeMatch(node.getName(), o.getName());
+ }
+
+ /**
+ * Returns whether the given node and the other object match.
+ * <p>
+ * The default implementation provided by this class tests whether the
+ * other object is a node of the same type with structurally isomorphic
+ * child subtrees. Subclasses may override this method as needed.
+ * </p>
+ *
+ * @param node the node
+ * @param other the other object, or <code>null</code>
+ * @return <code>true</code> if the subtree matches, or
+ * <code>false</code> if they do not match or the other object has a
+ * different node type or is <code>null</code>
* @since 3.1
*/
public boolean match(QualifiedType node, Object other) {
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTNode.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTNode.java
index 4db1f26955..1b7d3658df 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTNode.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTNode.java
@@ -992,6 +992,14 @@ public abstract class ASTNode {
/**
+ * Node type constant indicating a node of type
+ * <code>ModuleQualifiedName</code>.
+ * @see ModuleQualifiedName
+ * @since 3.23 BETA_JAVA15
+ */
+ public static final int MODULE_QUALIFIED_NAME = 103;
+
+ /**
* Returns the node class for the corresponding node type.
*
* @param nodeType AST node type
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTVisitor.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTVisitor.java
index b0e323caa9..f7eeca7bd9 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTVisitor.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTVisitor.java
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
@@ -1329,6 +1333,23 @@ public abstract class ASTVisitor {
* Visits the given type-specific AST node.
* <p>
* The default implementation does nothing and return true.
+ * Subclasses may reimplement.
+ * </p>
+ *
+ * @param node the node to visit
+ * @return <code>true</code> if the children of this node should be
+ * visited, and <code>false</code> if the children of this node should
+ * be skipped
+ * @noreference
+ */
+ public boolean visit(ModuleQualifiedName node) {
+ return true;
+ }
+
+ /**
+ * Visits the given type-specific AST node.
+ * <p>
+ * The default implementation does nothing and return true.
* Subclasses may re-implement.
* </p>
*
@@ -2758,6 +2779,19 @@ public abstract class ASTVisitor {
* </p>
*
* @param node the node to visit
+ * @noreference
+ */
+ public void endVisit(ModuleQualifiedName node) {
+ // default implementation: do nothing
+ }
+
+ /**
+ * End of visit the given type-specific AST node.
+ * <p>
+ * The default implementation does nothing. Subclasses may reimplement.
+ * </p>
+ *
+ * @param node the node to visit
* @since 3.14
*/
public void endVisit(RequiresDirective node) {
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
index df1ff90752..ce4f8d059d 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
@@ -8,6 +8,11 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
+
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for
@@ -36,6 +41,7 @@ import org.eclipse.jdt.internal.compiler.ast.JavadocAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.JavadocFieldReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocImplicitTypeReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend;
+import org.eclipse.jdt.internal.compiler.ast.JavadocModuleReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocSingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocSingleTypeReference;
@@ -1332,6 +1338,19 @@ class DefaultBindingResolver extends BindingResolver {
return this.getVariableBinding(qualifiedNameReference.otherBindings[index - indexOfFirstFieldBinding - 1]);
}
}
+ } else if (node instanceof JavadocModuleReference) {
+ JavadocModuleReference modRef = (JavadocModuleReference) node;
+ if (modRef.typeReference == null) {
+ ModuleReference moduleReference = modRef.moduleReference;
+ IModuleBinding moduleBinding = getModuleBinding(moduleReference.binding);
+ if (moduleBinding != null) {
+ return moduleBinding;
+ }
+ } else {
+ if (name instanceof ModuleQualifiedName) {
+ return resolveName(((ModuleQualifiedName)name).getName());
+ }
+ }
} else if (node instanceof QualifiedTypeReference) {
QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference) node;
if (qualifiedTypeReference.resolvedType == null) {
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java
index 1f73e1c662..1381924b8f 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2016 IBM Corporation and others.
+ * Copyright (c) 2004, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -331,9 +331,96 @@ class DocCommentParser extends AbstractCommentParser {
return typeRef;
}
+ private ModuleQualifiedName createModuleReference(int moduleRefTokenCount) {
+ String[] identifiers = new String[moduleRefTokenCount];
+ for (int i = 0; i < moduleRefTokenCount; i++) {
+ identifiers[i] = new String(this.identifierStack[i]);
+ }
+ ModuleQualifiedName moduleRef = new ModuleQualifiedName(this.ast);
+
+ ASTNode typeRef = null;
+ typeRef = this.ast.internalNewName(identifiers);
+ int start = (int) (this.identifierPositionStack[0] >>> 32);
+ if (moduleRefTokenCount > 1) {
+ Name name = (Name)typeRef;
+ int nameIndex = moduleRefTokenCount;
+ for (int i=moduleRefTokenCount-1; i>0; i--, nameIndex--) {
+ int s = (int) (this.identifierPositionStack[i] >>> 32);
+ int e = (int) this.identifierPositionStack[i];
+ name.index = nameIndex;
+ SimpleName simpleName = ((QualifiedName)name).getName();
+ simpleName.index = nameIndex;
+ simpleName.setSourceRange(s, e-s+1);
+ name.setSourceRange(start, e-start+1);
+ name = ((QualifiedName)name).getQualifier();
+ }
+ int end = (int) this.identifierPositionStack[0];
+ name.setSourceRange(start, end-start+1);
+ name.index = nameIndex;
+ } else {
+ int end = (int) this.identifierPositionStack[0];
+ typeRef.setSourceRange(start, end-start+1);
+ }
+ moduleRef.setModuleQualifier((Name)typeRef);
+ moduleRef.setName(null);
+ moduleRef.setSourceRange(typeRef.getStartPosition(), typeRef.getLength()+1);
+ return moduleRef;
+ }
+
@Override
- protected Object createModuleTypeReference(int primitiveToken, int moduleRefTokenCount) {
- return createTypeReference(primitiveToken);
+ protected Object createModuleTypeReference(int primitiveToken, int moduleRefTokenCount) {
+ int size = this.identifierLengthStack[this.identifierLengthPtr];
+ ModuleQualifiedName moduleRef= null;
+ Name typeRef= null;
+ if (size == moduleRefTokenCount) {
+ moduleRef= createModuleReference(moduleRefTokenCount);
+ this.lastIdentifierEndPosition++;
+ } else {
+ String[] moduleIdentifiers = new String[moduleRefTokenCount];
+ String[] identifiers = new String[size- moduleRefTokenCount];
+ int pos = this.identifierPtr - size + 1;
+ for (int i = 0; i < size; i++) {
+ if (i < moduleRefTokenCount) {
+ moduleIdentifiers[i] = new String(this.identifierStack[pos+i]);
+ } else {
+ identifiers[i-moduleRefTokenCount] = new String(this.identifierStack[pos+i]);
+ }
+ }
+ moduleRef= createModuleReference(moduleRefTokenCount);
+ pos = this.identifierPtr+moduleRefTokenCount - size + 1;
+
+ if (primitiveToken == -1) {
+ typeRef = this.ast.internalNewName(identifiers);
+ // Update ref for whole name
+ int start = (int) (this.identifierPositionStack[pos] >>> 32);
+// int end = (int) this.identifierPositionStack[this.identifierPtr];
+// typeRef.setSourceRange(start, end-start+1);
+ // Update references of each simple name
+ if (size-moduleRefTokenCount > 1) {
+ Name name = typeRef;
+ int nameIndex = size-moduleRefTokenCount;
+ for (int i=this.identifierPtr; i>pos; i--, nameIndex--) {
+ int s = (int) (this.identifierPositionStack[i] >>> 32);
+ int e = (int) this.identifierPositionStack[i];
+ name.index = nameIndex;
+ SimpleName simpleName = ((QualifiedName)name).getName();
+ simpleName.index = nameIndex;
+ simpleName.setSourceRange(s, e-s+1);
+ name.setSourceRange(start, e-start+1);
+ name = ((QualifiedName)name).getQualifier();
+ }
+ int end = (int) this.identifierPositionStack[pos];
+ name.setSourceRange(start, end-start+1);
+ name.index = nameIndex;
+ } else {
+ int end = (int) this.identifierPositionStack[pos];
+ typeRef.setSourceRange(start, end-start+1);
+ }
+ moduleRef.setName(typeRef);
+ moduleRef.setSourceRange(moduleRef.getStartPosition(), moduleRef.getLength() + typeRef.getLength());
+ }
+ }
+ return moduleRef;
}
@Override
@@ -482,7 +569,7 @@ class DocCommentParser extends AbstractCommentParser {
// Cannot have @see inside inline comment
valid = false;
} else {
- valid = parseReference();
+ valid = parseReference(true);
}
} else {
this.tagValue = TAG_OTHERS_VALUE;
@@ -500,7 +587,7 @@ class DocCommentParser extends AbstractCommentParser {
if (this.tagValue != NO_TAG_VALUE && this.tagValue != TAG_LITERAL_VALUE) {
if (this.inlineTagStarted) {
- valid = parseReference();
+ valid = parseReference(true);
} else {
// bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290
// Cannot have @link outside inline comment
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleQualifiedName.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleQualifiedName.java
new file mode 100644
index 0000000000..9a41782ce6
--- /dev/null
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleQualifiedName.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.core.dom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * AST node for a module qualified name. A module qualified name is defined as
+ * a qualified/simple name preceded by a module name, which qualifies it. Expressing it this
+ * way means that the module qualifier and the qualified name get their own AST nodes.
+ * <pre>
+ * ModuleQualifiedName:
+ * Name <b>.</b> Name
+ * </pre>
+ *
+ *
+ *
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @since 3.23 BETA_JAVA15
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class ModuleQualifiedName extends Name {
+
+ /**
+ * The "qualifier" structural property of this node type (child type: {@link Name}). *
+ */
+ public static final ChildPropertyDescriptor MODULE_QUALIFIER_PROPERTY =
+ new ChildPropertyDescriptor(ModuleQualifiedName.class, "moduleQualifier", SimpleName.class, MANDATORY, CYCLE_RISK); //$NON-NLS-1$
+
+ /**
+ * The "name" structural property of this node type (child type: {@link SimpleName}). *
+ */
+ public static final ChildPropertyDescriptor NAME_PROPERTY =
+ new ChildPropertyDescriptor(ModuleQualifiedName.class, "name", QualifiedName.class, MANDATORY, NO_CYCLE_RISK); //$NON-NLS-1$
+
+ /**
+ * A list of property descriptors (element type:
+ * {@link StructuralPropertyDescriptor}),
+ * or null if uninitialized.
+ */
+ private static final List PROPERTY_DESCRIPTORS;
+
+ static {
+ List propertyList = new ArrayList(3);
+ createPropertyList(ModuleQualifiedName.class, propertyList);
+ addProperty(MODULE_QUALIFIER_PROPERTY, propertyList);
+ addProperty(NAME_PROPERTY, propertyList);
+ PROPERTY_DESCRIPTORS = reapPropertyList(propertyList);
+ }
+
+ /**
+ * Returns a list of structural property descriptors for this node type.
+ * Clients must not modify the result.
+ *
+ * @param apiLevel the API level; one of the
+ * <code>AST.JLS*</code> constants
+ * @return a list of property descriptors (element type:
+ * {@link StructuralPropertyDescriptor})
+ */
+ public static List propertyDescriptors(int apiLevel) {
+ return PROPERTY_DESCRIPTORS;
+ }
+
+ /**
+ * The identifier; lazily initialized; defaults to a unspecified, legal
+ * Java identifier.
+ */
+ private Name moduleQualifier = null;
+
+ /**
+ * The name being module veysqualified; lazily initialized; defaults to a unspecified,
+ * legal Java identifier.
+ */
+ private Name name = null;
+
+ /**
+ * Creates a new AST node for a module qualified name owned by the given AST.
+ * <p>
+ * N.B. This constructor is package-private; all subclasses must be
+ * declared in the same package; clients are unable to declare
+ * additional subclasses.
+ * </p>
+ *
+ * @param ast the AST that is to own this node
+ */
+ ModuleQualifiedName(AST ast) {
+ super(ast);
+ unsupportedBelow15();
+ }
+
+ @Override
+ final List internalStructuralPropertiesForType(int apiLevel) {
+ return propertyDescriptors(apiLevel);
+ }
+
+ @Override
+ final ASTNode internalGetSetChildProperty(ChildPropertyDescriptor property, boolean get, ASTNode child) {
+ if (property == MODULE_QUALIFIER_PROPERTY) {
+ if (get) {
+ return getModuleQualifier();
+ } else {
+ setModuleQualifier((SimpleName) child);
+ return null;
+ }
+ }
+ if (property == NAME_PROPERTY) {
+ if (get) {
+ return getName();
+ } else {
+ setName((QualifiedName) child);
+ return null;
+ }
+ }
+ // allow default implementation to flag the error
+ return super.internalGetSetChildProperty(property, get, child);
+ }
+
+ @Override
+ final int getNodeType0() {
+ return MODULE_QUALIFIED_NAME;
+ }
+
+ @Override
+ ASTNode clone0(AST target) {
+ ModuleQualifiedName result = new ModuleQualifiedName(target);
+ result.setSourceRange(getStartPosition(), getLength());
+ result.setModuleQualifier((SimpleName) getModuleQualifier().clone(target));
+ result.setName((QualifiedName) getName().clone(target));
+ return result;
+ }
+
+ @Override
+ final boolean subtreeMatch0(ASTMatcher matcher, Object other) {
+ // dispatch to correct overloaded match method
+ return matcher.match(this, other);
+ }
+
+ @Override
+ void accept0(ASTVisitor visitor) {
+ boolean visitChildren = visitor.visit(this);
+ if (visitChildren) {
+ // visit children in normal left to right reading order
+ acceptChild(visitor, getModuleQualifier());
+ acceptChild(visitor, getName());
+ }
+ visitor.endVisit(this);
+ }
+
+ /**
+ * Returns the qualifier part of this qualified name.
+ *
+ * @return the qualifier part of this qualified name
+ */
+ public Name getModuleQualifier() {
+ if (this.moduleQualifier == null) {
+ // lazy init must be thread-safe for readers
+ synchronized (this) {
+ if (this.moduleQualifier == null) {
+ preLazyInit();
+ this.moduleQualifier = new SimpleName(this.ast);
+ postLazyInit(this.moduleQualifier, MODULE_QUALIFIER_PROPERTY);
+ }
+ }
+ }
+ return this.moduleQualifier;
+ }
+
+ /**
+ * Sets the qualifier of this qualified name to the given name.
+ *
+ * @param moduleQualifier the qualifier of this qualified name
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the node belongs to a different AST</li>
+ * <li>the node already has a parent</li>
+ * <li>a cycle in would be created</li>
+ * </ul>
+ */
+ public void setModuleQualifier(Name moduleQualifier) {
+ if (moduleQualifier == null) {
+ throw new IllegalArgumentException();
+ }
+ ASTNode oldChild = this.moduleQualifier;
+ preReplaceChild(oldChild, moduleQualifier, MODULE_QUALIFIER_PROPERTY);
+ this.moduleQualifier = moduleQualifier;
+ postReplaceChild(oldChild, moduleQualifier, MODULE_QUALIFIER_PROPERTY);
+ }
+
+ /**
+ * Returns the name part of this qualified name.
+ *
+ * @return the name being qualified
+ */
+ public Name getName() {
+ return this.name;
+ }
+
+ /**
+ * Sets the name part of this qualified name to the given simple name.
+ *
+ * @param name the identifier of this qualified name
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the node belongs to a different AST</li>
+ * <li>the node already has a parent</li>
+ * </ul>
+ */
+ public void setName(Name name) {
+ ASTNode oldChild = this.name;
+ preReplaceChild(oldChild, name, NAME_PROPERTY);
+ this.name = name;
+ postReplaceChild(oldChild, name, NAME_PROPERTY);
+ }
+
+ @Override
+ void appendName(StringBuffer buffer) {
+ getModuleQualifier().appendName(buffer);
+ buffer.append('/');
+ if (getName() != null) {
+ getName().appendName(buffer);
+ }
+ }
+
+ @Override
+ int memSize() {
+ return BASE_NAME_NODE_SIZE + 3 * 4;
+ }
+
+ @Override
+ int treeSize() {
+ return
+ memSize()
+ + (this.name == null ? 0 : getName().treeSize())
+ + (this.moduleQualifier == null ? 0 : getModuleQualifier().treeSize());
+ }
+}
+
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java
index 51dc98d2bd..57090dab25 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java
@@ -1307,6 +1307,17 @@ public class NaiveASTFlattener extends ASTVisitor {
}
@Override
+ public boolean visit(ModuleQualifiedName node) {
+ node.getModuleQualifier().accept(this);
+ this.buffer.append("/");//$NON-NLS-1$
+ ASTNode cNode = node.getName();
+ if (cNode != null) {
+ cNode.accept(this);
+ }
+ return false;
+ }
+
+ @Override
public boolean visit(QualifiedName node) {
node.getQualifier().accept(this);
this.buffer.append(".");//$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java
index 97359e882d..f4176dad3f 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java
@@ -120,6 +120,11 @@ private void acceptJavadocTypeReference(Expression expression) {
} else if (expression instanceof JavadocQualifiedTypeReference) {
JavadocQualifiedTypeReference qualifiedRef = (JavadocQualifiedTypeReference) expression;
this.requestor.acceptTypeReference(qualifiedRef.tokens, qualifiedRef.sourceStart, qualifiedRef.sourceEnd);
+ } else if (expression instanceof JavadocModuleReference) {
+ Expression exp = ((JavadocModuleReference) expression).getTypeReference();
+ if (exp != null) {
+ acceptJavadocTypeReference(exp);
+ }
}
}
public void addUnknownRef(NameReference nameRef) {

Back to the top