Bug 535691 - [15] @see, @link and @linkplain Javadoc tags for
modules in UI

Change-Id: Ia5dcc28b6b5ddc4e4bfb2e20367839868b17478a
Signed-off-by: Kalyan Prasad Tatavarthi <kalyan_prasad@in.ibm.com>
diff --git a/org.eclipse.jdt.core.manipulation/.settings/.api_filters b/org.eclipse.jdt.core.manipulation/.settings/.api_filters
index f527d5c..ead234c 100644
--- a/org.eclipse.jdt.core.manipulation/.settings/.api_filters
+++ b/org.eclipse.jdt.core.manipulation/.settings/.api_filters
@@ -22,6 +22,13 @@
                 <message_argument value="getAST()"/>
             </message_arguments>
         </filter>
+        <filter comment="This is a Java15 preview feature" id="640712815">
+            <message_arguments>
+                <message_argument value="ModuleQualifiedName"/>
+                <message_argument value="ImportReferencesCollector"/>
+                <message_argument value="getName()"/>
+            </message_arguments>
+        </filter>
         <filter comment="For Java 13 Enable Preview support" id="640712815">
             <message_arguments>
                 <message_argument value="YieldStatement"/>
diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/core/manipulation/ImportReferencesCollector.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/core/manipulation/ImportReferencesCollector.java
index dc71ae2..0776b1d 100644
--- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/core/manipulation/ImportReferencesCollector.java
+++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/core/manipulation/ImportReferencesCollector.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 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
  *******************************************************************************/
@@ -43,6 +47,7 @@
 import org.eclipse.jdt.core.dom.MethodRef;
 import org.eclipse.jdt.core.dom.MethodRefParameter;
 import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.ModuleQualifiedName;
 import org.eclipse.jdt.core.dom.Name;
 import org.eclipse.jdt.core.dom.NameQualifiedType;
 import org.eclipse.jdt.core.dom.NormalAnnotation;
@@ -148,6 +153,14 @@
 
 	private void typeRefFound(Name node) {
 		if (node != null) {
+			if (node instanceof ModuleQualifiedName) {
+				ModuleQualifiedName mName = (ModuleQualifiedName)node;
+				if (mName.getName() != null) {
+					node= mName.getName();
+				} else {
+					return;
+				}
+			}
 			while (node.isQualifiedName()) {
 				node= ((QualifiedName) node).getQualifier();
 			}
@@ -156,6 +169,14 @@
 	}
 
 	private void possibleTypeRefFound(Name node) {
+		if (node instanceof ModuleQualifiedName) {
+			ModuleQualifiedName mName= (ModuleQualifiedName) node;
+			Name name= mName.getName();
+			if (name != null) {
+				possibleTypeRefFound(name);
+			}
+			return;
+		}
 		while (node.isQualifiedName()) {
 			node= ((QualifiedName) node).getQualifier();
 		}
@@ -268,6 +289,20 @@
 
 	/*
 	 * @see ASTVisitor#visit(QualifiedName)
+	 * @since 1.14
+	 */
+	@Override
+	public boolean visit(ModuleQualifiedName node) {
+		Name name = node.getName();
+		if (name != null) {
+			possibleTypeRefFound(node); // possible ref
+			possibleStaticImportFound(node);
+		}
+		return false;
+	}
+
+	/*
+	 * @see ASTVisitor#visit(QualifiedName)
 	 */
 	@Override
 	public boolean visit(QualifiedName node) {
@@ -538,7 +573,15 @@
 					typeRefFound((Name) first);
 				} else if ("@see".equals(tagName) || "@link".equals(tagName) || "@linkplain".equals(tagName)) {  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
 					Name name= (Name) first;
-					possibleTypeRefFound(name);
+					if (name instanceof ModuleQualifiedName) {
+						ModuleQualifiedName mqName = (ModuleQualifiedName)name;
+						Name iname = mqName.getName();
+						if (iname != null) {
+							possibleTypeRefFound(iname);
+						}
+					} else {
+						possibleTypeRefFound(name);
+					}
 				}
 				idx++;
 			}
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/core/manipulation/search/OccurrencesFinder.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/core/manipulation/search/OccurrencesFinder.java
index 419c174..09d123a 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/core/manipulation/search/OccurrencesFinder.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/core/manipulation/search/OccurrencesFinder.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
  *******************************************************************************/
@@ -33,6 +37,7 @@
 import org.eclipse.jdt.core.dom.ImportDeclaration;
 import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.ModuleQualifiedName;
 import org.eclipse.jdt.core.dom.Name;
 import org.eclipse.jdt.core.dom.NameQualifiedType;
 import org.eclipse.jdt.core.dom.NodeFinder;
@@ -160,6 +165,12 @@
 		return !addUsage(node, binding);
 	}
 
+	@Override
+	public boolean visit(ModuleQualifiedName node) {
+		node.resolveBinding();
+		return true;
+	}
+
 	private static boolean isStaticImport(ASTNode node) {
 		if (!(node instanceof QualifiedName))
 			return false;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
index 1d62fb1..8b96d2d 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
@@ -46,9 +46,9 @@
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jdt.core.dom.IBinding;
 
+import org.eclipse.jdt.internal.core.manipulation.util.Strings;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
 import org.eclipse.jdt.internal.corext.util.Messages;
-import org.eclipse.jdt.internal.core.manipulation.util.Strings;
 import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache;
 
 import org.eclipse.jdt.ui.JavaElementLabels;
@@ -387,19 +387,30 @@
 	public static IJavaElement parseURI(URI uri) {
 		String ssp= uri.getSchemeSpecificPart();
 		String[] segments= ssp.split(String.valueOf(LINK_SEPARATOR), -1);
-
+		String refModuleName= null;
 		// replace '[' manually, since URI confuses it for an IPv6 address as per RFC 2732:
 		IJavaElement element= JavaCore.create(segments[1].replace(LINK_BRACKET_REPLACEMENT, '['));
 
 		if (segments.length > 2) {
 			String refTypeName= segments[2];
-			if (refTypeName.indexOf('.') == -1) {
-				try {
-					ITypeParameter resolvedTypeVariable= resolveTypeVariable(element, refTypeName);
-					if (resolvedTypeVariable != null)
-						return resolvedTypeVariable;
-				} catch (JavaModelException e) {
-					JavaPlugin.log(e);
+			int index= refTypeName.indexOf('/');
+			if (index != -1) {
+				refModuleName= refTypeName.substring(0, index);
+				refTypeName= refTypeName.substring(index+1);
+			}
+			if ((refTypeName== null || refTypeName.isEmpty()) &&
+					(refModuleName != null && !refModuleName.isEmpty())) {
+				return getModule(element, refModuleName);
+			}
+			if (refTypeName.indexOf('/') == -1) {
+				if (refTypeName.indexOf('.') == -1) {
+					try {
+						ITypeParameter resolvedTypeVariable= resolveTypeVariable(element, refTypeName);
+						if (resolvedTypeVariable != null)
+							return resolvedTypeVariable;
+					} catch (JavaModelException e) {
+						JavaPlugin.log(e);
+					}
 				}
 			}
 			if (element instanceof IAnnotation) {
@@ -490,6 +501,11 @@
 						// FIXME: either remove or show dialog
 //						JavaPlugin.logErrorMessage("JavaElementLinks could not resolve " + uri); //$NON-NLS-1$
 					}
+					if (type == null &&
+							refModuleName == null &&
+							segments.length <= 3) {
+						return getModule(element, refTypeName);
+					}
 					return type;
 				} catch (JavaModelException e) {
 					JavaPlugin.log(e);
@@ -499,6 +515,19 @@
 		return element;
 	}
 
+
+	private static IJavaElement getModule(IJavaElement element, String moduleName) {
+		if (element == null || moduleName == null) {
+			return null;
+		}
+		IJavaProject javaProject= element.getJavaProject();
+		try {
+			return javaProject.findModule(moduleName, null);
+		} catch (JavaModelException e1) {
+			// do nothing
+		}
+		return null;
+	}
 	private static IType resolvePackageInfoType(IPackageFragment pack, String refTypeName) throws JavaModelException {
 		// Note: The scoping rules of JLS7 6.3 are broken for package-info.java, see https://bugs.eclipse.org/216451#c4
 		// We follow the javadoc tool's implementation and only support fully-qualified type references: