Fix for bug 509961 [1.9][ast rewrite] AST Rewrite support for
module-info.java file
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingModuleDeclarationTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingModuleDeclarationTest.java
new file mode 100644
index 0000000..240e0d2
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingModuleDeclarationTest.java
@@ -0,0 +1,258 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ *
+ * 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.rewrite.describing;
+
+import java.util.List;
+import junit.framework.Test;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.dom.*;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+public class ASTRewritingModuleDeclarationTest extends ASTRewritingTest {
+
+	public ASTRewritingModuleDeclarationTest(String name) {
+		super(name);
+	}
+
+	public ASTRewritingModuleDeclarationTest(String name, int apiLevel) {
+		super(name, apiLevel);
+	}
+
+	public static Test suite() {
+		return createSuite(ASTRewritingModuleDeclarationTest.class);
+	}
+
+	public void testBug509961_0001_since_9() throws Exception {
+		IJavaProject javaProject = null ;
+		try {
+			javaProject = createProject("P_9", JavaCore.VERSION_9);
+			IPackageFragmentRoot currentSourceFolder = getPackageFragmentRoot("P_9", "src");
+			IPackageFragment pack1= currentSourceFolder.getPackageFragment(Util.EMPTY_STRING);
+			StringBuffer buf= new StringBuffer();
+			buf.append("module first {\n");
+			buf.append("    requires second;\n");
+			buf.append("    requires removeme;\n");
+			buf.append("    exports pack11 to third, fourth;\n");
+			buf.append("    exports pack12 to fifth;\n");
+			buf.append("    exports pack12 to remove.mod1;\n");
+			buf.append("    exports pack13 to well.founded.module2;\n");
+			buf.append("    uses MyType;\n");
+			buf.append("    uses Type.Remove;\n");
+			buf.append("    provides pack22.I22 with pack11.packinternal.Z11;\n");
+			buf.append("    provides pack23.I23 with pack11.Z23, pack12.ZZ23;\n");
+			buf.append("}");
+			ICompilationUnit cu= pack1.createCompilationUnit("module-info.java", buf.toString(), false, null);
+			CompilationUnit astRoot= createAST(cu);
+			ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST());
+			AST ast= astRoot.getAST();
+
+			ModuleDeclaration moduleDecl = astRoot.getModule();
+			ListRewrite listRewrite = rewrite.getListRewrite(moduleDecl, ModuleDeclaration.MODULE_STATEMENTS_PROPERTY);
+			List<ModuleStatement> moduleStatements = moduleDecl.moduleStatements();
+			int index = 0;
+			{
+				RequiresStatement req = (RequiresStatement) moduleStatements.get(index++); // replace the module in first required
+				Name newName = ast.newSimpleName("newSecond");
+				rewrite.replace(req.getName(), newName, null);
+				listRewrite.remove(moduleStatements.get(index++), null); // remove the second required
+
+				RequiresStatement newNode = ast.newRequiresStatement(); // add a new required
+				newNode.setName(ast.newSimpleName("addedme"));
+				listRewrite.insertAfter(newNode, req, null);
+			}
+			{
+				// exports pack11 to third, fourth; -> exports newpack11 to third;
+				ExportsStatement exp = (ExportsStatement) moduleStatements.get(index++);
+				Name newName = ast.newSimpleName("newpack11");
+				rewrite.replace(exp.getName(), newName, null);
+				ListRewrite expListRewrite = rewrite.getListRewrite(exp, ExportsStatement.MODULES_PROPERTY);
+				expListRewrite.remove((ASTNode) exp.modules().get(1), null); 
+
+				// exports pack12 to fifth -> exports pack12 to fifth, sixth
+				exp = (ExportsStatement) moduleStatements.get(index++);
+				newName = ast.newSimpleName("sixth");
+				expListRewrite = rewrite.getListRewrite(exp, ExportsStatement.MODULES_PROPERTY);
+				expListRewrite.insertLast(newName, null);
+
+				// exports pack12 to remove.mod1 -> exports pack12
+				exp = (ExportsStatement) moduleStatements.get(index++);
+				expListRewrite = rewrite.getListRewrite(exp, ExportsStatement.MODULES_PROPERTY);
+				expListRewrite.remove((ASTNode) exp.modules().get(0), null);
+
+				// exports pack12 to never.to.be.module - remove the export
+				listRewrite.remove((ASTNode) moduleDecl.moduleStatements().get(index++), null);
+
+				// exports pack12 to new.found.module - add the export
+				exp = ast.newExportsStatement();
+				exp.setName(ast.newSimpleName("pack12"));
+				Name name = ast.newName("well.founded.module3");
+				exp.modules().add(name);
+				listRewrite.insertLast(exp, null);
+			}
+			{
+				// uses MyType -> uses MyNewType;
+				UsesStatement usesStatement = (UsesStatement) moduleStatements.get(index++);
+				Name newName = ast.newSimpleName("MyNewType");
+				SimpleType type = ast.newSimpleType(newName);
+				rewrite.replace(usesStatement.getType(), type, null);
+
+				// uses Type.Remove - remove the uses
+				listRewrite.remove(moduleStatements.get(index++), null);
+
+				// uses MyNewFoundType - add the uses
+				usesStatement = ast.newUsesStatement();
+				newName = ast.newSimpleName("MyNewFoundType");
+				type = ast.newSimpleType(newName);
+				usesStatement.setType(type);
+				listRewrite.insertLast(usesStatement, null);
+			}
+			{
+				// provides pack22.I22 with pack11.packinternal.Z11 ->  provides pack22.INew22 with pack11.packinternal.NewZ11, pack11.Y11
+				ProvidesStatement providesStatement = (ProvidesStatement) moduleStatements.get(index++);
+				Name newName = ast.newName("pack22.INew22");
+				SimpleType type = ast.newSimpleType(newName);
+				rewrite.replace(providesStatement.getType(), type, null);
+				newName = ast.newName("pack11.packinternal.NewZ11");
+				type = ast.newSimpleType(newName);
+				ListRewrite pListRewrite = rewrite.getListRewrite(providesStatement, ProvidesStatement.IMPLEMENTATIONS_PROPERTY);
+				pListRewrite.replace((ASTNode) providesStatement.implementations().get(0), type ,null);
+
+				newName = ast.newName("pack11.Y11");
+				type = ast.newSimpleType(newName);
+				pListRewrite.insertLast(type, null);
+				// provides pack23.I23 with pack11.Z23, pack12.ZZ23 -> provides pack23.I23 with pack12.ZZ23
+				providesStatement = (ProvidesStatement) moduleStatements.get(index++);
+				pListRewrite = rewrite.getListRewrite(providesStatement, ProvidesStatement.IMPLEMENTATIONS_PROPERTY);
+				pListRewrite.remove((ASTNode) providesStatement.implementations().get(0), null);
+			}
+			String preview= evaluateRewrite(cu, rewrite);
+			buf= new StringBuffer();
+			buf.append("module first {\n");
+			buf.append("    requires newSecond;\n");
+			buf.append("    requires addedme;\n");
+			buf.append("    exports newpack11 to third;\n");
+			buf.append("    exports pack12 to fifth, sixth;\n");
+			buf.append("    exports pack12;\n");
+			buf.append("    uses MyNewType;\n");
+			buf.append("    provides pack22.INew22 with pack11.packinternal.NewZ11, pack11.Y11;\n");
+			buf.append("    provides pack23.I23 with pack12.ZZ23;\n");
+			buf.append("    exports pack12 to well.founded.module3;\n");
+			buf.append("    uses MyNewFoundType;\n");
+			buf.append("}");
+			assertEqualString(preview, buf.toString());
+		} finally {
+			if (javaProject != null)
+				deleteProject(javaProject);
+		}
+	}
+	public void testBug509961_0002_since_9() throws Exception {
+		IJavaProject javaProject = null;
+		try {
+			javaProject = createProject("P_9", JavaCore.VERSION_9);
+			IPackageFragmentRoot currentSourceFolder = getPackageFragmentRoot("P_9", "src");
+			IPackageFragment pack1= currentSourceFolder.getPackageFragment(Util.EMPTY_STRING);
+			StringBuffer buf= new StringBuffer();
+			buf.append("module first {\n");
+			buf.append("    requires existing;\n");
+			buf.append("}");
+
+			ICompilationUnit cu= pack1.createCompilationUnit("module-info.java", buf.toString(), false, null);
+			CompilationUnit astRoot= createAST(cu);
+			ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST());
+			AST ast= astRoot.getAST();
+
+			ModuleDeclaration moduleDecl = astRoot.getModule();
+			ListRewrite listRewrite = rewrite.getListRewrite(moduleDecl, ModuleDeclaration.MODULE_STATEMENTS_PROPERTY);
+			{
+				RequiresStatement newNode = ast.newRequiresStatement(); // add a new required
+				newNode.setName(ast.newSimpleName("addedme"));
+				listRewrite.insertLast(newNode, null);
+			}
+			String preview= evaluateRewrite(cu, rewrite);
+			buf= new StringBuffer();
+			buf.append("module first {\n");
+			buf.append("    requires existing;\n");
+			buf.append("    requires addedme;\n");
+			buf.append("}");
+			assertEqualString(preview, buf.toString());		
+		} finally {
+			if (javaProject != null) deleteProject(javaProject);
+		}
+	}
+
+	public void testBug509961_0003_since_9() throws Exception {
+		IJavaProject javaProject = null;
+		try {
+			javaProject = createProject("P_9", JavaCore.VERSION_9);
+			IPackageFragmentRoot currentSourceFolder = getPackageFragmentRoot("P_9", "src");
+			IPackageFragment pack1= currentSourceFolder.getPackageFragment(Util.EMPTY_STRING);
+			StringBuffer buf= new StringBuffer();
+			buf.append("module first {\n");
+			buf.append("    requires existing;\n");
+			buf.append("    requires static module1;\n");
+			buf.append("    requires static module2;\n");
+			buf.append("    requires static module3;\n");
+			buf.append("}");
+			ICompilationUnit cu= pack1.createCompilationUnit("module-info.java", buf.toString(), false, null);
+			CompilationUnit astRoot= createAST(cu);
+			ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST());
+			AST ast= astRoot.getAST();
+			ModuleDeclaration moduleDecl = astRoot.getModule();
+			ListRewrite listRewrite = rewrite.getListRewrite(moduleDecl, ModuleDeclaration.MODULE_STATEMENTS_PROPERTY);
+			{
+				RequiresStatement reqNode = (RequiresStatement) moduleDecl.moduleStatements().get(0);
+				ASTNode newModifier = ast.newModifier(ModifierKeyword.STATIC_KEYWORD);
+				rewrite.getListRewrite(reqNode, RequiresStatement.MODIFIERS_PROPERTY).insertFirst(newModifier, null);
+
+				reqNode = (RequiresStatement) moduleDecl.moduleStatements().get(1);
+				rewrite.getListRewrite(reqNode, RequiresStatement.MODIFIERS_PROPERTY).remove((ASTNode) reqNode.modifiers().get(0), null);
+
+				reqNode = (RequiresStatement) moduleDecl.moduleStatements().get(2);
+				newModifier = ast.newModifier(ModifierKeyword.TRANSIENT_KEYWORD);
+				rewrite.getListRewrite(reqNode, RequiresStatement.MODIFIERS_PROPERTY).replace((ASTNode) reqNode.modifiers().get(0), newModifier, null);
+
+				reqNode = (RequiresStatement) moduleDecl.moduleStatements().get(3);
+				newModifier = ast.newModifier(ModifierKeyword.TRANSIENT_KEYWORD);
+				rewrite.getListRewrite(reqNode, RequiresStatement.MODIFIERS_PROPERTY).insertLast(newModifier, null);
+
+				RequiresStatement newNode = ast.newRequiresStatement(); // add a new required
+				newNode.setName(ast.newSimpleName("addedme"));
+				newModifier = ast.newModifier(ModifierKeyword.TRANSIENT_KEYWORD);
+				rewrite.getListRewrite(newNode, RequiresStatement.MODIFIERS_PROPERTY).insertFirst(newModifier, null);
+				listRewrite.insertLast(newNode, null);
+			}
+			String preview= evaluateRewrite(cu, rewrite);
+			buf= new StringBuffer();
+			buf.append("module first {\n");
+			buf.append("    requires static existing;\n");
+			buf.append("    requires module1;\n");
+			buf.append("    requires transient module2;\n");
+			buf.append("    requires static transient module3;\n");
+			buf.append("    requires transient addedme;\n");
+		buf.append("}");
+			assertEqualString(preview, buf.toString());		
+		} finally {
+			if (javaProject != null) deleteProject(javaProject);
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java
index 2ced827..565be69 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java
@@ -1,10 +1,14 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 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
  * http://www.eclipse.org/legal/epl-v10.html
  *
+ * 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
  *******************************************************************************/
@@ -62,7 +66,7 @@
 	/** @deprecated using deprecated code */
 	private final static int JLS8_INTERNAL = AST.JLS8;
 
-	private final static int[] JLS_LEVELS = { JLS2_INTERNAL, JLS3_INTERNAL, JLS4_INTERNAL, JLS8_INTERNAL };
+	private final static int[] JLS_LEVELS = { JLS2_INTERNAL, JLS3_INTERNAL, JLS4_INTERNAL, JLS8_INTERNAL, AST.JLS9 };
 
 	private static final String ONLY_AST_STRING = "_only";
 	private static final String SINCE_AST_STRING = "_since";
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
index a4133ae..8b46444 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
@@ -1,10 +1,14 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2015 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
  * http://www.eclipse.org/legal/epl-v10.html
  *
+ * 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 bug 186342 - [compiler][null] Using annotations for null checking
@@ -98,6 +102,8 @@
 	public int linePtr = -1;
 	public boolean wasAcr = false;
 
+	public boolean fakeInModule = false;
+
 	public static final String END_OF_SOURCE = "End_Of_Source"; //$NON-NLS-1$
 
 	public static final String INVALID_HEXA = "Invalid_Hexa_Literal"; //$NON-NLS-1$
@@ -2530,8 +2536,9 @@
 	//newIdentCount++;
 	return table[this.newEntry6 = max] = r; //(r = new char[] {c0, c1, c2, c3, c4, c5});
 }
-private boolean isInModuleDeclaration() {
-	return this.activeParser != null ? this.activeParser.isParsingModuleDeclaration() : false;
+public boolean isInModuleDeclaration() {
+	return this.fakeInModule || 
+			(this.activeParser != null ? this.activeParser.isParsingModuleDeclaration() : false);
 }
 private void parseTags() {
 	int position = 0;
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 88aa9e3..34dfcc6 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
@@ -1,10 +1,14 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 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
  * http://www.eclipse.org/legal/epl-v10.html
  *
+ * 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
  *******************************************************************************/
@@ -1417,6 +1421,7 @@
 		printIndent();
 		this.buffer.append("requires");//$NON-NLS-1$
 		this.buffer.append(" ");//$NON-NLS-1$
+		printModifiers(node.modifiers());
 		node.getName().accept(this);
 		this.buffer.append(";\n");//$NON-NLS-1$
 		return false;
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java
index 7f08aab..f96ca35 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 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
@@ -1330,13 +1330,10 @@
 		}
 	}
 
-	/*
-	 * Next token is a left brace. Returns the offset after the brace. For incomplete code, return the start offset.
-	 */
-	private int getPosAfterLeftBrace(int pos) {
+	private int getPosAfterToken(int pos, int token) {
 		try {
 			int nextToken= getScanner().readNext(pos, true);
-			if (nextToken == TerminalTokens.TokenNameLBRACE) {
+			if (nextToken == token) {
 				return getScanner().getCurrentEndOffset();
 			}
 		} catch (CoreException e) {
@@ -1344,6 +1341,12 @@
 		}
 		return pos;
 	}
+	/*
+	 * Next token is a left brace. Returns the offset after the brace. For incomplete code, return the start offset.
+	 */
+	private int getPosAfterLeftBrace(int pos) {
+		return getPosAfterToken(pos, TerminalTokens.TokenNameLBRACE);
+	}
 
 	/*
 	 * Next token is try keyword. Returns the offset after 'try' keyword. For incomplete code, return the start offset.
@@ -1694,6 +1697,10 @@
 			return doVisitUnchangedChildren(node);
 		}
 
+		if (node.getAST().apiLevel() >= JLS9_INTERNAL && node.getModule() != null) {
+			rewriteNode(node, CompilationUnit.MODULE_PROPERTY, 0, ASTRewriteFormatter.NONE);
+			return false;
+		}
 		int startPos= rewriteNode(node, CompilationUnit.PACKAGE_PROPERTY, 0, ASTRewriteFormatter.NONE);
 
 		if (getChangeKind(node, CompilationUnit.PACKAGE_PROPERTY) == RewriteEvent.INSERTED) {
@@ -2075,6 +2082,22 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(ModuleDeclaration node) {
+		if (!hasChildrenChanges(node)) {
+			return doVisitUnchangedChildren(node);
+		}
+		int pos= rewriteJavadoc(node, ModuleDeclaration.JAVADOC_PROPERTY);
+		pos= rewriteModifiers2(node, ModuleDeclaration.MODIFIERS_PROPERTY, pos);
+		pos= rewriteRequiredNode(node, ModuleDeclaration.NAME_PROPERTY);
+		int startPos = getPosAfterLeftBrace(pos);
+		int startIndent= getIndent(node.getStartPosition()) + 1;
+		this.tokenScanner.getScanner().fakeInModule = true;
+		rewriteParagraphList(node, ModuleDeclaration.MODULE_STATEMENTS_PROPERTY, startPos, startIndent, 0, 1);
+		this.tokenScanner.getScanner().fakeInModule = false;
+		return false;
+	}
+
 	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(Block)
 	 */
@@ -2114,6 +2137,15 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(RequiresStatement node) {
+		if (!hasChildrenChanges(node)) {
+			return doVisitUnchangedChildren(node);
+		}
+		rewriteModifiers2(node, RequiresStatement.MODIFIERS_PROPERTY, getPosAfterToken(node.getStartPosition(), TerminalTokens.TokenNamerequires));
+		rewriteRequiredNode(node, RequiresStatement.NAME_PROPERTY);
+		return false;
+	}
 
 	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(AnonymousClassDeclaration)
@@ -2652,6 +2684,16 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(ExportsStatement node) {
+		if (!hasChildrenChanges(node)) {
+			return doVisitUnchangedChildren(node);
+		}
+		int pos = rewriteRequiredNode(node, ExportsStatement.NAME_PROPERTY);
+		rewriteNodeList(node, ExportsStatement.MODULES_PROPERTY, pos, "to ", ", "); //$NON-NLS-1$ //$NON-NLS-2$ 
+
+		return false;
+	}
 	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(ExpressionStatement)
 	 */
@@ -3225,6 +3267,15 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(ProvidesStatement node) {
+		if (!hasChildrenChanges(node)) {
+			return doVisitUnchangedChildren(node);
+		}
+		int pos = rewriteRequiredNode(node, ProvidesStatement.TYPE_PROPERTY);
+		pos= rewriteNodeList(node, ProvidesStatement.IMPLEMENTATIONS_PROPERTY, pos, " with ", ", "); //$NON-NLS-1$ //$NON-NLS-2$
+		return false;
+	}
 	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(QualifiedName)
 	 */
@@ -3690,7 +3741,16 @@
 		rewriteNodeList(node, UnionType.TYPES_PROPERTY, node.getStartPosition(), Util.EMPTY_STRING, " | "); //$NON-NLS-1$
 		return false;
 	}
-	
+
+	@Override
+	public boolean visit(UsesStatement node) {
+		if (!hasChildrenChanges(node)) {
+			return doVisitUnchangedChildren(node);
+		}
+		rewriteRequiredNode(node,UsesStatement.TYPE_PROPERTY);
+		return false;
+	}
+
 	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(VariableDeclarationExpression)
 	 */
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java
index 5a25ea8..6850a42 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 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
@@ -440,10 +440,13 @@
 		return false;
 	}
 
-	/*
-	 * @see ASTVisitor#visit(CompilationUnit)
-	 */
+	@Override
 	public boolean visit(CompilationUnit node) {
+		ASTNode module= getChildNode(node, CompilationUnit.MODULE_PROPERTY);
+		if (module != null) {
+			module.accept(this);
+		}
+
 		ASTNode pack= getChildNode(node, CompilationUnit.PACKAGE_PROPERTY);
 		if (pack != null) {
 			pack.accept(this);
@@ -529,6 +532,19 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(ExportsStatement node) {
+		this.result.append("exports "); //$NON-NLS-1$
+		getChildNode(node, ExportsStatement.NAME_PROPERTY).accept(this);
+		List<Name> modules = node.modules();
+		if (modules.size() > 0) {
+			this.result.append(" to "); //$NON-NLS-1$
+			visitList(node, ExportsStatement.MODULES_PROPERTY, Util.COMMA_SEPARATOR, Util.EMPTY_STRING, Util.EMPTY_STRING);
+		}
+		this.result.append(';');
+		return false;
+	}
+
 	/*
 	 * @see ASTVisitor#visit(ExpressionStatement)
 	 */
@@ -782,6 +798,21 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(ModuleDeclaration node) {
+		ASTNode javadoc= getChildNode(node, ModuleDeclaration.JAVADOC_PROPERTY);
+		if (javadoc != null) {
+			javadoc.accept(this);
+		}
+		visitList(node, ModuleDeclaration.MODIFIERS_PROPERTY, String.valueOf(' '), Util.EMPTY_STRING, String.valueOf(' '));
+		this.result.append("module "); //$NON-NLS-1$
+		getChildNode(node, ModuleDeclaration.NAME_PROPERTY).accept(this);
+		this.result.append('{');
+		visitList(node, ModuleDeclaration.MODULE_STATEMENTS_PROPERTY, null);
+		this.result.append('}');
+		return false;
+	}
+
 	/*
 	 * @see ASTVisitor#visit(MethodInvocation)
 	 */
@@ -863,6 +894,16 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(ProvidesStatement node) {
+		this.result.append("provides "); //$NON-NLS-1$
+		getChildNode(node, ProvidesStatement.TYPE_PROPERTY).accept(this);
+		this.result.append(" with "); //$NON-NLS-1$
+		visitList(node, ProvidesStatement.IMPLEMENTATIONS_PROPERTY, Util.EMPTY_STRING, Util.COMMA_SEPARATOR, Util.EMPTY_STRING);
+		this.result.append(';');
+		return false;
+	}
+
 	/*
 	 * @see ASTVisitor#visit(PrimitiveType)
 	 */
@@ -884,6 +925,15 @@
 		return false;
 	}
 
+	@Override
+	public boolean visit(RequiresStatement node) {
+		this.result.append("requires "); //$NON-NLS-1$
+		visitList(node, RequiresStatement.MODIFIERS_PROPERTY, Util.EMPTY_STRING, Util.EMPTY_STRING, String.valueOf(' '));
+		getChildNode(node, RequiresStatement.NAME_PROPERTY).accept(this);
+		this.result.append(';');
+		return false;
+	}
+
 	/*
 	 * @see ASTVisitor#visit(ReturnStatement)
 	 */
@@ -1161,6 +1211,14 @@
 		return false;
 	}
 	
+	@Override
+	public boolean visit(UsesStatement node) {
+		this.result.append("uses "); //$NON-NLS-1$
+		getChildNode(node, UsesStatement.TYPE_PROPERTY).accept(this);
+		this.result.append(';');
+		return false;
+	}
+
 	/*
 	 * @see ASTVisitor#visit(VariableDeclarationExpression)
 	 */