Bug 519167: [9] Create rtstubs9.jar for Java 9 test setup
diff --git a/org.eclipse.jdt.ui.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.ui.tests/META-INF/MANIFEST.MF
index 84487e0..109e59c 100644
--- a/org.eclipse.jdt.ui.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.ui.tests/META-INF/MANIFEST.MF
@@ -39,6 +39,7 @@
  org.eclipse.jdt.ui.tests.wizardapi;x-internal:=true
 Require-Bundle: 
  org.eclipse.core.expressions,
+ org.eclipse.core.filesystem,
  org.eclipse.core.resources,
  org.eclipse.core.runtime,
  org.eclipse.debug.core,
diff --git a/org.eclipse.jdt.ui.tests/plugin.xml b/org.eclipse.jdt.ui.tests/plugin.xml
index b051006..d12ed4d 100644
--- a/org.eclipse.jdt.ui.tests/plugin.xml
+++ b/org.eclipse.jdt.ui.tests/plugin.xml
@@ -2,6 +2,40 @@
 <?eclipse version="3.0"?>
 <plugin>
     
+<!-- **************** See testresources/rtstubs-README.txt ******************* -->
+   <extension
+         point="org.eclipse.ui.popupMenus">
+      <objectContribution
+            objectClass="org.eclipse.jdt.core.IJavaElement"
+            id="org.eclipse.jdt.ui.tests.contribution">
+         <visibility>
+            <or>
+               <objectClass
+                     name="org.eclipse.jdt.core.IPackageFragment">
+               </objectClass>
+               <objectClass
+                     name="org.eclipse.jdt.core.IPackageFragmentRoot">
+               </objectClass>
+            </or>
+         </visibility>
+         <menu
+               label="&amp;jdt.ui test tools"
+               path="additions"
+               id="org.eclipse.jdt.ui.tests.menu">
+            <separator
+                  name="group">
+            </separator>
+         </menu>
+         <action
+               label="Create stubs in pro&amp;ject..."
+               class="org.eclipse.jdt.testplugin.util.CreateStubsAction"
+               menubarPath="org.eclipse.jdt.ui.tests.menu/group"
+               enablesFor="+"
+               id="org.eclipse.jdt.ui.examples.CreateStubsAction">
+         </action>
+      </objectContribution>
+   </extension>
+
 <!-- **************** TEST DECORATOR ******************* -->
    <extension
          point="org.eclipse.ui.decorators">
diff --git a/org.eclipse.jdt.ui.tests/test plugin/org/eclipse/jdt/testplugin/JavaTestPlugin.java b/org.eclipse.jdt.ui.tests/test plugin/org/eclipse/jdt/testplugin/JavaTestPlugin.java
index f60f1b9..b099332 100644
--- a/org.eclipse.jdt.ui.tests/test plugin/org/eclipse/jdt/testplugin/JavaTestPlugin.java
+++ b/org.eclipse.jdt.ui.tests/test plugin/org/eclipse/jdt/testplugin/JavaTestPlugin.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -19,14 +19,15 @@
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
-import org.eclipse.core.runtime.Plugin;
 import org.eclipse.core.runtime.Status;
 
 import org.eclipse.core.resources.IWorkspace;
 import org.eclipse.core.resources.ResourcesPlugin;
 
+import org.eclipse.ui.plugin.AbstractUIPlugin;
 
-public class JavaTestPlugin extends Plugin {
+
+public class JavaTestPlugin extends AbstractUIPlugin {
 
 	private static JavaTestPlugin fgDefault;
 
diff --git a/org.eclipse.jdt.ui.tests/test plugin/org/eclipse/jdt/testplugin/util/CreateStubsAction.java b/org.eclipse.jdt.ui.tests/test plugin/org/eclipse/jdt/testplugin/util/CreateStubsAction.java
new file mode 100644
index 0000000..15e140f
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests/test plugin/org/eclipse/jdt/testplugin/util/CreateStubsAction.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2017 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.testplugin.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jdt.testplugin.JavaProjectHelper;
+import org.eclipse.jdt.testplugin.JavaTestPlugin;
+
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+
+import org.eclipse.jdt.internal.corext.refactoring.binary.StubCreationOperation;
+
+/**
+ * See testresources/rtstubs-README.txt
+ */
+public final class CreateStubsAction implements IObjectActionDelegate {
+
+	private static final String[] DEFAULT_PACKAGES= new String[] {
+			"java.beans",
+			"java.io",
+			"java.lang",
+			"java.lang.annotation",
+			"java.lang.ref",
+			"java.lang.reflect",
+			"java.math",
+			"java.net",
+			"java.nio",
+			"java.nio.channels",
+			"java.nio.channels.spi",
+			"java.nio.charset",
+			"java.nio.charset.spi",
+			"java.security",
+			"java.security.acl",
+			"java.security.cert",
+			"java.security.interfaces",
+			"java.security.spec",
+			"java.sql",
+			"java.text",
+			"java.util",
+			"java.util.jar",
+			"java.util.regex",
+			"java.util.zip",
+			//1.7:
+			"java.lang.invoke",
+			"java.nio.file",
+			"java.nio.file.attribute",
+			"java.util.concurrent",
+			//1.8:
+			"java.util.function",
+			"java.util.stream",
+			"javax.annotation",
+			"javax.annotation.processing",
+			"javax.lang.model",
+			"javax.lang.model.element",
+			"javax.lang.model.type",
+			"javax.lang.model.util",
+			"javax.tools",
+			//9:
+			"java.lang.module",
+			"java.nio.file.spi",
+	};
+
+	private static final String SETTINGS_ID_STUBS_PROJECT= "stubsProject";
+
+	private static final String CREATE_STUBS_DIALOG_TITLE= "Create Stubs"; //$NON-NLS-1$
+
+	private IWorkbenchPart fTargetPart;
+
+	public CreateStubsAction() {
+		super();
+	}
+
+	void createJavaProject(String projectName) throws CoreException {
+		IJavaProject javaProject= JavaProjectHelper.createJavaProject(projectName, "bin");
+		JavaProjectHelper.addSourceContainer(javaProject, "src");
+	}
+
+	private void fail(String msg) {
+		Shell shell= fTargetPart.getSite().getShell();
+		MessageDialog.openInformation(shell, CREATE_STUBS_DIALOG_TITLE, msg);
+	}
+
+	@Override
+	public void run(IAction action) {
+		ISelection selection= fTargetPart.getSite().getSelectionProvider().getSelection();
+		if (!(selection instanceof IStructuredSelection)) {
+			fail("Select packages to create stubs."); //$NON-NLS-1$
+			return;
+		}
+		final IStructuredSelection structuredSelection= (IStructuredSelection) selection;
+
+		Shell shell= fTargetPart.getSite().getShell();
+		String initialValue= JavaTestPlugin.getDefault().getDialogSettings().get(SETTINGS_ID_STUBS_PROJECT); //$NON-NLS-1$
+		if (initialValue == null)
+			initialValue = "stubs"; //$NON-NLS-1$
+		final InputDialog inputDialog= new InputDialog(shell, CREATE_STUBS_DIALOG_TITLE, "Target project name:", initialValue, new IInputValidator() { //$NON-NLS-1$
+					@Override
+					public String isValid(String newText) {
+						IStatus status = ResourcesPlugin.getWorkspace().validateName(newText, IResource.PROJECT);
+						return status.isOK() ? null : (String) status.getMessage();
+					}
+				});
+		if (inputDialog.open() != Window.OK)
+			return;
+		try {
+
+			final String name= inputDialog.getValue();
+			JavaTestPlugin.getDefault().getDialogSettings().put(SETTINGS_ID_STUBS_PROJECT, name); //$NON-NLS-1$
+			long start= System.currentTimeMillis();
+
+			ArrayList<String> defaultPackages= new ArrayList<>(Arrays.asList(DEFAULT_PACKAGES));
+			
+			ProgressMonitorDialog progressMonitorDialog= new ProgressMonitorDialog(shell);
+			progressMonitorDialog.run(true, true, new IRunnableWithProgress() {
+
+				@Override
+				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+					try {
+						createJavaProject(name);
+						IFolder target = ResourcesPlugin.getWorkspace().getRoot().getProject(inputDialog.getValue()).getFolder("src"); //$NON-NLS-1$
+						List<IPackageFragment> packageFragments= new ArrayList<>();
+						
+						boolean checkCompletionOfDefaultPackages= false;
+						for (Object sel : structuredSelection.toList()) {
+							if (sel instanceof IPackageFragment) {
+								packageFragments.add((IPackageFragment) sel);
+							} else if (sel instanceof IPackageFragmentRoot) {
+								IPackageFragmentRoot root = (IPackageFragmentRoot) sel;
+								for (Iterator<String> iter= defaultPackages.iterator(); iter.hasNext();) {
+									String packName= iter.next();
+									IPackageFragment packageFragment = root.getPackageFragment(packName);
+									if (packageFragment.exists()) {
+										packageFragments.add(packageFragment);
+										iter.remove();
+									} else {
+										checkCompletionOfDefaultPackages= true;
+									}
+								}
+							}
+						}
+						ResourcesPlugin.getWorkspace().run(new StubCreationOperation(target.getLocationURI(), packageFragments), monitor);
+						if (!checkCompletionOfDefaultPackages) {
+							defaultPackages.clear();
+						}
+					} catch (CoreException e) {
+						throw new InvocationTargetException(e);
+					}
+				}
+			});
+			IProject project= ResourcesPlugin.getWorkspace().getRoot().getProject(name);
+			project.refreshLocal(IResource.DEPTH_INFINITE, null);
+			long end= System.currentTimeMillis();
+			String message= "Took " + ((end - start) / 1000) + "s to create the stubs";
+			if (!defaultPackages.isEmpty() ) {
+				message+= "\n\nNo stubs generated for packages: " + defaultPackages.toString();
+			}
+			MessageDialog.openInformation(fTargetPart.getSite().getShell(), CREATE_STUBS_DIALOG_TITLE, message); 
+		} catch (InterruptedException e) {
+			// Do not log
+		} catch (InvocationTargetException e) {
+			JavaTestPlugin.log(e);
+		} catch (CoreException exception) {
+			JavaTestPlugin.log(exception);
+		}
+	}
+
+	@Override
+	public void selectionChanged(IAction action, ISelection selection) {
+		// Do nothing
+	}
+
+	@Override
+	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+		fTargetPart= targetPart;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests/testresources/rtstubs-README.txt b/org.eclipse.jdt.ui.tests/testresources/rtstubs-README.txt
new file mode 100644
index 0000000..0d267fc
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests/testresources/rtstubs-README.txt
@@ -0,0 +1,62 @@
+About rtstubs*.jar
+*****************
+
+Many JDT tests require a specific JRE version that may be newer or older than
+the default JRE that is being used to run the tests. To create a stable
+environment, tests usually put rtstubs*.jar on the classpath.
+
+rtstubs*.jar contain class file stubs for a few of the most basic java.*
+packages. The class files only contain the APIs, but no method bodies etc.
+(except for required super constructor invocations).
+
+The process of creating a new rtstubs*.jar is partly automated, but there's
+still quite some manual work involved to get a source project that compiles
+without errors.
+
+Here are the generator classes that likely need some tweaks in the future:
+- org.eclipse.jdt.testplugin.util.CreateStubsAction: add packages to
+    DEFAULT_PACKAGES if there are many references to them
+- org.eclipse.jdt.internal.corext.refactoring.binary.StubCreator: Make sure new
+    or newly used language constructs are properly handled.
+
+Building rtstubs*.jar
+*********************
+- Have org.eclipse.jdt.ui.tests in your JDT source workspace.
+- Start a runtime workbench.
+- Create a Java project with the JRE for which you need to generate stubs
+   (e.g. "JDK9")
+
+- Select the all the modules. This will use the hardcoded default list of
+   packages. In Java 9: from modules java.base, java.compiler, java.desktop, and
+   java.sql. Before Java 9: select rt.jar. 
+   (in CreateStubsAction, add additionally required packages there).
+- Alternatively, select the package(s) for which to (re-)create stubs.
+
+- Context menu > jdt.ui test tools > Create stubs in project...
+- Enter name of target project (e.g. "stubs9") and click OK. Use a new project
+   or one that doesn't contain any JRE System Library on the build path -- we
+   want a self-sufficient source folder that doesn't depend on binaries.
+
+- Team > Share Project... > Git, and commit a base version in case you later
+   want to revert.
+
+- Manually remove everything from the java.beans package, except for:
+PropertyChangeEvent
+PropertyChangeListener
+
+- Manually remove the following: (hint: use Ctrl+Shift+C to comment lines or
+   Ctrl+Shift+/ to comment supertypes)
+  - default-accessible types with errors (Java Browsing perspective or
+     "Java Type Indicator" decorator help :-)
+  - all methods/implements clauses with references to javax, sun, or java.time.*
+     (could maybe generate java.time* in the future if this avoids manual work)
+
+- Make sure there are no compile errors remaining.
+
+- Create a module-info.java that declares module java.base and exports all
+   packages.
+
+- On project preference page 'Java Compiler', uncheck all options for
+   'Classfile Generation'
+
+- Export generated class files to rtstubs*.jar
diff --git a/org.eclipse.jdt.ui.tests/testresources/rtstubs9.jar b/org.eclipse.jdt.ui.tests/testresources/rtstubs9.jar
new file mode 100644
index 0000000..6278e02
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests/testresources/rtstubs9.jar
Binary files differ
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/binary/StubCreator.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/binary/StubCreator.java
index 61ee17f..fe868a9 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/binary/StubCreator.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/binary/StubCreator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2014 IBM Corporation and others.
+ * Copyright (c) 2006, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -135,11 +135,11 @@
 
 	private void appendAnnotation(IAnnotation annotation) throws JavaModelException {
 		String name= annotation.getElementName();
-		if (!fStubInvisible && name.startsWith("sun.")) //$NON-NLS-1$
+		if (!fStubInvisible && name.startsWith("sun.") || name.startsWith("jdk.internal.")) //$NON-NLS-1$ //$NON-NLS-2$
 			return; // skip Sun-internal annotations 
 		
 		fBuffer.append('@');
-		fBuffer.append(name);
+		fBuffer.append(name.replace('$', '.'));
 		fBuffer.append('(');
 		
 		IMemberValuePair[] memberValuePairs= annotation.getMemberValuePairs();
@@ -194,7 +194,8 @@
 				final boolean isDefault= !Flags.isPublic(flags) && !Flags.isProtected(flags) && !isPrivate;
 				final boolean stub= fStubInvisible || (!isPrivate && !isDefault);
 				if (child instanceof IType) {
-					if (stub)
+					if (stub || "java.lang.invoke.MethodHandle".equals(type.getFullyQualifiedName()) //$NON-NLS-1$
+							|| "java.util.concurrent.ConcurrentHashMap$CollectionView".equals(((IType) child).getFullyQualifiedName())) //$NON-NLS-1$
 						appendTypeDeclaration((IType) child, new SubProgressMonitor(monitor, 1));
 				} else if (child instanceof IField) {
 					if (stub && !Flags.isEnum(flags) && !Flags.isSynthetic(flags))
@@ -350,6 +351,11 @@
 				fBuffer.append(","); //$NON-NLS-1$
 			fBuffer.append(Signature.toString(exceptionTypes[index]));
 		}
+		if (Flags.isAnnnotationDefault(flags) ) {
+			fBuffer.append(" default "); //$NON-NLS-1$
+			IMemberValuePair pair= method.getDefaultValue();
+			appendAnnotationValue(pair.getValue(), pair.getValueKind());
+		}
 		if (Flags.isAbstract(flags) || Flags.isNative(flags))
 			fBuffer.append(";"); //$NON-NLS-1$
 		else {