Bug 526040: Merge Java 9 related changes from JDT
- rest of o.e.jdt.core
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
index ec5b9a8..c2d0b95 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
@@ -20,10 +20,12 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
+import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.jdt.core.CompletionContext;
@@ -32,8 +34,11 @@
 import org.eclipse.jdt.core.CompletionRequestor;
 import org.eclipse.jdt.core.Flags;
 import org.eclipse.jdt.core.IAccessRule;
+import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IModuleDescription;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.ITypeRoot;
 import org.eclipse.jdt.core.JavaCore;
@@ -44,6 +49,12 @@
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.SearchRequestor;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeDetector;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeFound;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnAnnotationOfType;
@@ -65,6 +76,8 @@
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocTypeParamReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeyword;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeyword3;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeywordModuleDeclaration;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeywordModuleInfo;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnLocalName;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMarkerAnnotationName;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess;
@@ -73,8 +86,15 @@
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMessageSendName;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMethodName;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMethodReturnType;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnModuleDeclaration;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnModuleReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnPackageReference;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnPackageVisibilityReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnParameterizedQualifiedTypeReference;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesImplementationsQualifiedTypeReference;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesImplementationsSingleTypeReference;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesInterfacesQualifiedTypeReference;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesInterfacesSingleTypeReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedAllocationExpression;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedTypeReference;
@@ -82,6 +102,8 @@
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleTypeReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionOnStringLiteral;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnUsesQualifiedTypeReference;
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnUsesSingleTypeReference;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionParser;
 import org.eclipse.jdt.internal.codeassist.complete.CompletionScanner;
 import org.eclipse.jdt.internal.codeassist.complete.InvalidCursorLocation;
@@ -125,16 +147,21 @@
 import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
 import org.eclipse.jdt.internal.compiler.ast.MessageSend;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
 import org.eclipse.jdt.internal.compiler.ast.NameReference;
 import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
 import org.eclipse.jdt.internal.compiler.ast.OperatorExpression;
 import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.PackageVisibilityStatement;
 import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
 import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
+import org.eclipse.jdt.internal.compiler.ast.RequiresStatement;
 import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.ProvidesStatement;
 import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
 import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.SuperReference;
@@ -146,6 +173,7 @@
 import org.eclipse.jdt.internal.compiler.ast.TypeReference;
 import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
 import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.UsesStatement;
 import org.eclipse.jdt.internal.compiler.ast.WhileStatement;
 import org.eclipse.jdt.internal.compiler.ast.Wildcard;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -202,12 +230,15 @@
 import org.eclipse.jdt.internal.core.BinaryTypeConverter;
 import org.eclipse.jdt.internal.core.INamingRequestor;
 import org.eclipse.jdt.internal.core.InternalNamingConventions;
+import org.eclipse.jdt.internal.core.JavaElementRequestor;
 import org.eclipse.jdt.internal.core.JavaModelManager;
-import org.eclipse.jdt.internal.core.SearchableEnvironment;
+import org.eclipse.jdt.internal.core.ModuleSourcePathManager;
 import org.eclipse.jdt.internal.core.SourceMethod;
 import org.eclipse.jdt.internal.core.SourceMethodElementInfo;
 import org.eclipse.jdt.internal.core.SourceType;
+import org.eclipse.jdt.internal.core.SearchableEnvironment;
 import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
+import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
 import org.eclipse.jdt.internal.core.search.matching.IndexBasedJavaSearchEnvironment;
 import org.eclipse.jdt.internal.core.util.Messages;
 import org.eclipse.jdt.internal.core.util.Util;
@@ -715,7 +746,9 @@
 	ProblemReporter problemReporter;
 	private INameEnvironment noCacheNameEnvironment;
 	char[] source;
+	ModuleDeclaration moduleDeclaration;
 	char[] completionToken;
+
 	char[] qualifiedCompletionToken;
 	boolean resolvingImports = false;
 	boolean resolvingStaticImports = false;
@@ -730,6 +763,7 @@
 	String complianceLevel;
 	SimpleSetOfCharArray validPackageNames = new SimpleSetOfCharArray(10);
 	SimpleSetOfCharArray invalidPackageNames = new SimpleSetOfCharArray(1);
+	HashtableOfObject knownModules = new HashtableOfObject(10);
 	HashtableOfObject knownPkgs = new HashtableOfObject(10);
 	HashtableOfObject knownTypes = new HashtableOfObject(10);
 	
@@ -1282,6 +1316,42 @@
 			this.acceptedTypes = null; // reset
 		}
 	}
+	
+	/**
+	 * One result of the search consists of a new module.
+	 *
+	 * NOTE - All module names are presented in their readable form:
+	 *    Module names are in the form "a.b.c".
+	 *    The default module is represented by an empty array.
+	 */
+	public void acceptModule(char[] moduleName) {
+		if (this.knownModules.containsKey(moduleName)) return;
+		if (CharOperation.equals(moduleName, this.moduleDeclaration.moduleName)) return;
+		if (CharOperation.equals(moduleName, CharOperation.NO_CHAR)) return;
+		this.knownModules.put(moduleName, this);
+		char[] completion = moduleName;
+		int relevance = computeBaseRelevance();
+		relevance += computeRelevanceForResolution();
+		relevance += computeRelevanceForInterestingProposal();
+		relevance += computeRelevanceForCaseMatching(this.qualifiedCompletionToken == null ? this.completionToken : this.qualifiedCompletionToken, moduleName);
+		relevance += computeRelevanceForQualification(true);
+		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
+		this.noProposal = false;
+		if(!this.requestor.isIgnored(CompletionProposal.MODULE_REF)) {
+			InternalCompletionProposal proposal = createProposal(CompletionProposal.MODULE_REF, this.actualCompletionPosition);
+			proposal.setModuleName(moduleName);
+			proposal.setDeclarationSignature(moduleName);
+			proposal.setCompletion(completion);
+			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
+			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
+			proposal.setRelevance(relevance);
+			this.requestor.accept(proposal);
+			if(DEBUG) {
+				this.printDebug(proposal);
+			}
+		}
+		
+	}
 
 	/**
 	 * One result of the search consists of a new package.
@@ -1902,6 +1972,14 @@
 			completionOnMethodReturnType(astNode, scope);
 		} else if (astNode instanceof CompletionOnSingleNameReference) {
 			completionOnSingleNameReference(astNode, astNodeParent, scope, insideTypeAnnotation);
+		} else if (astNode instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
+			completionOnProvidesInterfacesQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+		} else if (astNode instanceof CompletionOnProvidesInterfacesSingleTypeReference) {
+			completionOnProvidesInterfacesSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+		} else if (astNode instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
+			completionOnProvidesImplementationsQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+		} else if (astNode instanceof CompletionOnProvidesImplementationsSingleTypeReference) {
+			completionOnProvidesImplementationsSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
 		} else if (astNode instanceof CompletionOnSingleTypeReference) {
 			completionOnSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
 		} else if (astNode instanceof CompletionOnQualifiedNameReference) {
@@ -2015,6 +2093,100 @@
 					System.out.println(parsedUnit.toString());
 				}
 
+				if (parsedUnit.isModuleInfo()) {
+					this.moduleDeclaration = parsedUnit.moduleDeclaration;
+					if (this.moduleDeclaration == null) return;
+					if (this.moduleDeclaration instanceof CompletionOnModuleDeclaration) {
+						contextAccepted = true;
+						buildContext(parsedUnit.moduleDeclaration, null, parsedUnit, null, null);
+						//this.requestor.setIgnored(CompletionProposal.MODULE_DECLARATION, false); //TODO: Hack until ui fixes this issue.
+						if(!this.requestor.isIgnored(CompletionProposal.MODULE_DECLARATION)) {
+							findModuleName(parsedUnit);
+						}
+						debugPrintf(); 
+						return;
+					}
+					if (this.moduleDeclaration instanceof CompletionOnKeywordModuleDeclaration) {
+						contextAccepted = true;
+						processModuleKeywordCompletion(parsedUnit, this.moduleDeclaration, (CompletionOnKeyword) this.moduleDeclaration);
+						return;
+					}
+					if (this.moduleDeclaration.exports != null) {
+						contextAccepted = completeOnPackageVisibilityStatements(contextAccepted, parsedUnit, this.moduleDeclaration.exports);
+						if (contextAccepted) return;
+					}
+					if (this.moduleDeclaration.opens != null) {
+						contextAccepted = completeOnPackageVisibilityStatements(contextAccepted, parsedUnit, this.moduleDeclaration.opens);
+						if (contextAccepted) return;
+					}
+					RequiresStatement[] moduleRefs = this.moduleDeclaration.requires;
+					if (moduleRefs != null) {
+						for (int i = 0, l = moduleRefs.length; i < l; ++i) {
+							ModuleReference reference = moduleRefs[i].module;
+							if (reference instanceof CompletionOnModuleReference) {
+								contextAccepted = true;
+								buildContext(reference, null, parsedUnit, null, null);
+								if(!this.requestor.isIgnored(CompletionProposal.MODULE_REF)) {
+									findModules((CompletionOnModuleReference) reference, false /* targetted */);
+								}
+								debugPrintf();
+								return;
+							}
+						}
+					}
+					UsesStatement[] uses = this.moduleDeclaration.uses;
+					if (uses != null) {
+						for (int i = 0, l = uses.length; i < l; ++i) {
+							TypeReference usesReference = uses[i].serviceInterface;
+							if (usesReference instanceof CompletionOnUsesSingleTypeReference ||
+									usesReference instanceof CompletionOnUsesQualifiedTypeReference) {
+								this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
+								if ((this.unitScope = parsedUnit.scope) != null) {
+									contextAccepted = true;
+									buildContext(usesReference, null, parsedUnit, null, null);
+									findTypeReferences(usesReference, true);
+									debugPrintf();
+									return;
+								}
+							}
+						}
+					}
+					ProvidesStatement[] providesStmts = this.moduleDeclaration.services;
+					for (int i = 0, l = providesStmts != null ? providesStmts.length : 0; i < l; ++i) {
+						ProvidesStatement providesStmt = providesStmts[i];
+						TypeReference pInterface = providesStmt.serviceInterface;
+						if (pInterface instanceof CompletionOnProvidesInterfacesSingleTypeReference ||
+								pInterface instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
+							this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
+							if ((this.unitScope = parsedUnit.scope) != null) {
+								contextAccepted = true;
+								buildContext(pInterface, null, parsedUnit, null, null);
+								findTypeReferences(pInterface, true);
+								debugPrintf();
+								return;
+							}
+						}
+						TypeReference[] implementations = providesStmt.implementations;
+						for (int j = 0, k = implementations.length; j < k; ++j) {
+							TypeReference implementation = implementations[i];
+							
+							if (implementation instanceof CompletionOnProvidesImplementationsSingleTypeReference ||
+									implementation instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
+								this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
+								if ((this.unitScope = parsedUnit.scope) != null) {
+									contextAccepted = true;
+									buildContext(implementation, null, parsedUnit, null, null);
+									findImplementations(providesStmt, i/* stmtIndex */, j/* implIndex */);
+									debugPrintf();
+									return;
+								}
+							} else if (implementation instanceof CompletionOnKeyword) {
+								contextAccepted = true;
+								processModuleKeywordCompletion(parsedUnit, implementation, (CompletionOnKeyword) implementation);
+							}
+						}
+					}
+				}
 				// scan the package & import statements first
 				if (parsedUnit.currentPackage instanceof CompletionOnPackageReference) {
 					contextAccepted = true;
@@ -2022,12 +2194,7 @@
 					if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
 						findPackages((CompletionOnPackageReference) parsedUnit.currentPackage);
 					}
-					if(this.noProposal && this.problem != null) {
-						this.requestor.completionFailure(this.problem);
-						if(DEBUG) {
-							this.printDebug(this.problem);
-						}
-					}
+					debugPrintf();
 					return;
 				}
 
@@ -2081,12 +2248,7 @@
 									}
 								}
 
-								if(this.noProposal && this.problem != null) {
-									this.requestor.completionFailure(this.problem);
-									if(DEBUG) {
-										this.printDebug(this.problem);
-									}
-								}
+								debugPrintf();
 							}
 //{ObjectTeams:
 						  }
@@ -2100,12 +2262,7 @@
 								CompletionOnKeyword keyword = (CompletionOnKeyword)importReference;
 								findKeywords(keyword.getToken(), keyword.getPossibleKeywords(), false, parsedUnit.currentPackage != null);
 							}
-							if(this.noProposal && this.problem != null) {
-								this.requestor.completionFailure(this.problem);
-								if(DEBUG) {
-									this.printDebug(this.problem);
-								}
-							}
+							debugPrintf();
 							return;
 						}
 					}
@@ -2238,6 +2395,68 @@
 		}
 	}
 
+	private boolean completeOnPackageVisibilityStatements(boolean contextAccepted,
+			CompilationUnitDeclaration parsedUnit, PackageVisibilityStatement[] pvsStmts) {
+		for (int i = 0, l = pvsStmts.length; i < l; ++i) {
+			PackageVisibilityStatement pvs = pvsStmts[i];
+			if (pvs instanceof CompletionOnKeywordModuleInfo) { // dummy pvs statement
+				contextAccepted = true;
+				processModuleKeywordCompletion(parsedUnit, pvs, (CompletionOnKeyword) pvs);
+				return contextAccepted;
+			}
+			if (pvs.pkgRef instanceof CompletionOnPackageVisibilityReference) {
+				contextAccepted = true;
+				buildContext(pvs, null, parsedUnit, null, null);
+				if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
+					findPackages((CompletionOnPackageVisibilityReference) pvs.pkgRef);
+				}
+				debugPrintf();
+				return contextAccepted;
+			}
+			ModuleReference[] targets = pvs.targets;
+			if (targets == null) continue;
+			HashSet<String> skipSet = new HashSet<>();
+			for (int j = 0, lj = targets.length; j < lj; j++) {
+				ModuleReference target = targets[j];
+				if (target == null) break;
+				if (target instanceof CompletionOnModuleReference) {
+					buildContext(target, null, parsedUnit, null, null);
+					contextAccepted = true;
+					if(!this.requestor.isIgnored(CompletionProposal.MODULE_REF)) {
+						findTargettedModules((CompletionOnModuleReference) target, skipSet);
+					}
+					debugPrintf();
+					return contextAccepted;
+				} else if (target instanceof CompletionOnKeyword) {
+					contextAccepted = true;
+					processModuleKeywordCompletion(parsedUnit, target, (CompletionOnKeyword) target);
+				} else {
+					if (target.moduleName != null || target.moduleName.equals(CharOperation.NO_CHAR))
+						skipSet.add(new String(target.moduleName));
+				}
+			}
+		}
+		return contextAccepted;
+	}
+
+	private void debugPrintf() {
+		if(this.noProposal && this.problem != null) {
+			this.requestor.completionFailure(this.problem);
+			if(DEBUG) {
+				this.printDebug(this.problem);
+			}
+		}
+	}
+
+	private void processModuleKeywordCompletion(CompilationUnitDeclaration parsedUnit, ASTNode node, CompletionOnKeyword keyword) {
+		buildContext(node, null, parsedUnit, null, null);
+		if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
+			setSourceAndTokenRange(node.sourceStart, node.sourceEnd);
+			findKeywords(keyword.getToken(), keyword.getPossibleKeywords(), false, parsedUnit.currentPackage != null);
+		}
+		debugPrintf();
+	}
+
 	public void complete(IType type, char[] snippet, int position, char[][] localVariableTypeNames, char[][] localVariableNames, int[] localVariableModifiers, boolean isStatic){
 		if(this.requestor != null){
 			this.requestor.beginReporting();
@@ -3502,6 +3721,16 @@
 		}
 	}
 	
+	private void completionOnProvidesInterfacesQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
+		// TODO: Filter the results wrt accessibility and add relevance to the results.
+		completionOnQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+	}
+
+	private void completionOnProvidesImplementationsQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
+		// TODO: Filter the results wrt accessibility and add relevance to the results.
+		completionOnQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+	}
+
 	private void completionOnSingleNameReference(ASTNode astNode, ASTNode astNodeParent, Scope scope,
 			boolean insideTypeAnnotation) {
 		CompletionOnSingleNameReference singleNameReference = (CompletionOnSingleNameReference) astNode;
@@ -3643,6 +3872,15 @@
 		}
 	}
 
+	private void completionOnProvidesInterfacesSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
+		// TODO : filter the results.
+		completionOnSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+	}
+	private void completionOnProvidesImplementationsSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
+		// TODO : filter the results.
+		completionOnSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
+	}
+
 	private char[][] computeAlreadyDefinedName(
 			BlockScope scope,
 			InvocationSite invocationSite) {
@@ -10686,7 +10924,7 @@
 					sourceType = (ISourceType) type;
 				}
 			} else {
-				NameEnvironmentAnswer answer = this.nameEnvironment.findType(bindingType.compoundName);
+				NameEnvironmentAnswer answer = this.nameEnvironment.findTypeInModules(bindingType.compoundName, this.unitScope.module());
 				if(answer != null && answer.isSourceType()) {
 					sourceType = answer.getSourceTypes()[0];
 					this.typeCache.put(compoundName, sourceType);
@@ -11158,11 +11396,9 @@
 // SH}
 
 	private void findPackages(CompletionOnPackageReference packageStatement) {
-
 		this.completionToken = CharOperation.concatWithAll(packageStatement.tokens, '.');
 		if (this.completionToken.length == 0)
 			return;
-
 		setSourceRange(packageStatement.sourceStart, packageStatement.sourceEnd);
 		long completionPosition = packageStatement.sourcePositions[packageStatement.sourcePositions.length - 1];
 		setTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
@@ -11509,15 +11745,15 @@
 								hasArrayTypeAsExpectedSuperTypes()) {
 					char[] typeName = sourceType.sourceName();
 					createTypeProposal(
-							sourceType,
-							typeName,
-							IAccessRule.K_ACCESSIBLE,
-							typeName,
-							relevance,
-							null,
-							null,
-							null,
-							false);
+								sourceType,
+								typeName,
+								IAccessRule.K_ACCESSIBLE,
+								typeName,
+								relevance,
+								null,
+								null,
+								null,
+								false);
 				}
 				
 				if (proposeConstructor) {
@@ -12085,6 +12321,7 @@
 	
 	private void findTypesFromStaticImports(char[] token, Scope scope, boolean proposeAllMemberTypes, ObjectVector typesFound) {
 		ImportBinding[] importBindings = scope.compilationUnitScope().imports;
+		if (importBindings == null) return;
 		for (int i = 0; i < importBindings.length; i++) {
 			ImportBinding importBinding = importBindings[i];
 			if(importBinding.isValidBinding() && importBinding.isStatic()) {
@@ -12324,6 +12561,138 @@
 		return null;
 	}
 
+	private void findTypeReferences(TypeReference reference, boolean findMembers) {
+		char[][] tokens = reference.getTypeName();
+
+		char[] typeName = CharOperation.concatWithAll(tokens, '.');
+
+		if (typeName.length == 0) {
+			this.completionToken = new char[] {'*'};
+		} else if (reference instanceof CompletionOnUsesQualifiedTypeReference ||
+				reference instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
+			CompletionOnQualifiedTypeReference qReference = (CompletionOnQualifiedTypeReference) reference;
+			if (qReference.completionIdentifier != null) {
+				this.completionToken = CharOperation.concatAll(typeName, qReference.completionIdentifier, '.');
+			}
+		} else {
+			 char[] lastToken = tokens[tokens.length - 1];
+			 this.completionToken = lastToken != null && lastToken.length == 0 ?
+					 CharOperation.concat(typeName, new char[]{'.'}) :lastToken;
+		}
+		setSourceRange(reference.sourceStart, reference.sourceEnd);
+		findTypesAndPackages(this.completionToken, this.unitScope, true, true, new ObjectVector());
+	}
+
+	private void findImplementations(ProvidesStatement providesStmt, int stmtIndex, int implIndex) {
+		TypeReference reference = providesStmt.implementations[implIndex];
+		char[][] tokens = reference.getTypeName();
+		char[] typeName = CharOperation.concatWithAll(tokens, '.');
+
+		if (typeName.length == 0) {
+			this.completionToken = CharOperation.ALL_PREFIX;
+		} else if (reference instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
+			CompletionOnQualifiedTypeReference qReference = (CompletionOnQualifiedTypeReference) reference;
+			if (qReference.completionIdentifier != null) {
+				this.completionToken = CharOperation.concatAll(typeName, qReference.completionIdentifier, '.');
+			}
+		} else {
+			 char[] lastToken = tokens[tokens.length - 1];
+			 this.completionToken = lastToken != null && lastToken.length == 0 ?
+					 CharOperation.concat(typeName, new char[]{'.'}) :lastToken;
+		}
+		setSourceRange(reference.sourceStart, reference.sourceEnd);
+		findImplementations(this.completionToken, this.unitScope, providesStmt, stmtIndex);
+	}
+
+	private void findImplementations(char[] token, Scope scope, ProvidesStatement providesStmt, int stmtIndex) {
+
+		TypeReference theInterface = providesStmt.serviceInterface;
+
+		if (token == null) return;
+		char[][] theInterfaceType = theInterface.getTypeName();
+		if (theInterfaceType == null) return;
+		SearchPattern pattern  = null;
+		NameEnvironmentAnswer answer =  this.nameEnvironment.findTypeInModules(theInterfaceType, scope.module());
+		if (answer != null ) {
+			if (answer.isSourceType()) {
+				IType typeHandle = ((SourceTypeElementInfo) answer.getSourceTypes()[0]).getHandle();
+				pattern = SearchPattern.createPattern(typeHandle, IJavaSearchConstants.IMPLEMENTORS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
+			} else if (answer.isBinaryType()) {
+				String typeName = new String(CharOperation.replaceOnCopy(answer.getBinaryType().getName(), '/', '.'));
+				pattern = SearchPattern.createPattern(typeName,
+						IJavaSearchConstants.CLASS_AND_INTERFACE,
+						IJavaSearchConstants.IMPLEMENTORS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
+			}
+		}
+		if (pattern == null) return;
+		IJavaSearchScope searchScope = BasicSearchEngine.createJavaSearchScope(new IJavaElement[] {this.javaProject});
+		class ImplSearchRequestor extends SearchRequestor {
+			String prefix;
+			List<String> filter;
+			public List<IType> types = new ArrayList<>();
+			public ImplSearchRequestor(char[] prefixToken, List<String> filter) {
+				this.prefix = (prefixToken == CharOperation.ALL_PREFIX) ? null : new String(prefixToken);
+				this.filter = filter;
+			}
+			@Override
+			public void acceptSearchMatch(SearchMatch match) throws CoreException {
+				checkCancel();
+				IJavaElement element = ((IJavaElement) match.getElement());
+				if (element.getElementType() == IJavaElement.TYPE) {
+					IType type = (IType) element;
+					if (this.prefix != null) {
+						String fullTypeName = type.getPackageFragment().getElementName();
+						if (fullTypeName != null) {
+							fullTypeName = fullTypeName.concat(".").concat(type.getElementName()); //$NON-NLS-1$
+						} else {
+							fullTypeName = type.getElementName();
+						}
+						if (!fullTypeName.startsWith(this.prefix) || this.filter.contains(fullTypeName)) return;
+					}
+					this.types.add(type);
+				}
+			}
+		}
+		try {
+			List<String> existingImpl = new ArrayList<>();
+			char[][] theInterfaceName = theInterface.getTypeName();
+			// filter out existing implementations of the same interfaces
+			for (int i = 0, l = this.moduleDeclaration.servicesCount; i < l; ++i) {
+				if (i == stmtIndex) continue;
+				ProvidesStatement prevProvides = this.moduleDeclaration.services[i];
+				if (!CharOperation.equals(theInterfaceName, prevProvides.serviceInterface.getTypeName())) continue;
+				TypeReference[] prevImpls = prevProvides.implementations;
+				for (TypeReference prevImpl : prevImpls) {
+					char[][] typeName = prevImpl.getTypeName();
+					if (typeName.equals(CharOperation.NO_CHAR_CHAR)) continue;
+					existingImpl.add(CharOperation.toString(typeName));
+				}
+			}
+			// use search infrastructure - faster than using model
+			ImplSearchRequestor searchRequestor = new ImplSearchRequestor(this.completionToken, existingImpl);
+			new SearchEngine(this.owner == null ? null : JavaModelManager.getJavaModelManager().getWorkingCopies(this.owner, true/*add primary WCs*/)).search(
+					pattern,
+					new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()},
+					searchScope,
+					searchRequestor,
+					null
+					);
+			for (IType type : searchRequestor.types) {
+				String pkg = type.getPackageFragment().getElementName();
+				String name = type.getElementName();
+				this.acceptType(pkg.toCharArray(), name.toCharArray(), CharOperation.NO_CHAR_CHAR, type.getFlags(), null);
+				acceptTypes(scope);
+			}
+		} catch (CoreException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
+			checkCancel();
+			findPackagesInCurrentModule();
+		}
+	}
+
 	private char[][] findVariableFromUnresolvedReference(LocalDeclaration variable, BlockScope scope, final char[][] discouragedNames) {
 		final TypeReference type = variable.type;
 		if(type != null &&
@@ -13027,6 +13396,11 @@
 			if(target != 0 && (target & TagBits.AnnotationForPackage) == 0) {
 				return false;
 			}
+		} else if (this.targetedElement == TagBits.AnnotationForModule) {
+			long target = typeBinding.getAnnotationTagBits() & TagBits.AnnotationTargetMASK;
+			if(target != 0 && (target & TagBits.AnnotationForModule) == 0) {
+				return false;
+			}
 		} else if ((this.targetedElement & (TagBits.AnnotationForType | TagBits.AnnotationForTypeUse)) != 0) {
 			if (scope.parent != null &&
 					scope.parent.parent != null &&
@@ -13840,6 +14214,7 @@
 		super.reset(false);
 		this.validPackageNames = new SimpleSetOfCharArray(10);
 		this.invalidPackageNames = new SimpleSetOfCharArray(1);
+		this.knownModules = new HashtableOfObject(10);
 		this.knownPkgs = new HashtableOfObject(10);
 		this.knownTypes = new HashtableOfObject(10);
 		if (this.noCacheNameEnvironment != null) {
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISearchRequestor.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISearchRequestor.java
index 23beccc..f87241b 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISearchRequestor.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISearchRequestor.java
@@ -90,4 +90,6 @@
 	 *    The default package is represented by an empty array.
 	 */
 	public void acceptPackage(char[] packageName);
+	
+	public void acceptModule(char[] moduleName);
 }
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISelectionRequestor.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISelectionRequestor.java
index cd0b4bb..9bf0875 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISelectionRequestor.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/ISelectionRequestor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -52,6 +52,20 @@
 		int end);
 
 	/**
+	 * Code assist notification of a module selection.
+	 *
+	 * @param moduleName name of the module
+	 * @param uniqueKey unique key of this module
+	 * @param start Start of the selection
+	 * @param end End of the selection
+	 */
+	void acceptModule(
+			char[] moduleName,
+			char[] uniqueKey,
+			int start,
+			int end);
+
+	/**
 	 * Code assist notification of a compilation error detected during selection.
 	 *  @param error CategorizedProblem
 	 *      Only problems which are categorized as errors are notified to the requestor,
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
index 4bea202..23e59c6 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2015 IBM Corporation and others.
+ * Copyright (c) 2004, 2016 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
@@ -37,6 +37,7 @@
 import org.eclipse.jdt.internal.core.JavaElement;
 import org.eclipse.jdt.internal.core.JavaModelManager;
 import org.eclipse.jdt.internal.core.NameLookup;
+import org.eclipse.jdt.internal.core.NamedMember;
 import org.eclipse.jdt.internal.core.SourceMapper;
 
 /**
@@ -51,6 +52,7 @@
 
 	protected char[] declarationPackageName;
 	protected char[] declarationTypeName;
+	protected char[] moduleName;
 	protected char[] packageName;
 	protected char[] typeName;
 	protected char[][] parameterPackageNames;
@@ -244,7 +246,7 @@
 								IBinaryType info = (IBinaryType) ((BinaryType) type).getElementInfo();
 								char[] source = mapper.findSource(type, info);
 								if (source != null){
-									mapper.mapSource(type, source, info);
+									mapper.mapSource((NamedMember) type, source, info);
 								}
 								paramNames = mapper.getMethodParameterNames(method);
 							}
@@ -378,6 +380,10 @@
 		return JavaModelManager.getJavaModelManager().getOpenableCacheSize() / 10;
 	}
 
+	protected char[] getModuleName() {
+		return this.moduleName;
+	}
+
 	protected char[] getPackageName() {
 		return this.packageName;
 	}
@@ -403,6 +409,10 @@
 		this.declarationTypeName = declarationTypeName;
 	}
 
+	protected void setModuleName(char[] moduleName) {
+		this.moduleName = moduleName;
+	}
+
 	protected void setPackageName(char[] packageName) {
 		this.packageName = packageName;
 	}
@@ -848,7 +858,7 @@
 	}
 
 	/**
-	 * Sets the type or package signature of the relevant
+	 * Sets the type or package or module(1.9) signature of the relevant
 	 * declaration in the context, or <code>null</code> if none.
 	 * <p>
 	 * If not set, defaults to none.
@@ -858,7 +868,7 @@
 	 * its properties; this method is not intended to be used by other clients.
 	 * </p>
 	 *
-	 * @param signature the type or package signature, or
+	 * @param signature the type or package or module(1.9) signature, or
 	 * <code>null</code> if none
 	 */
 	public void setDeclarationSignature(char[] signature) {
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/MissingTypesGuesser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/MissingTypesGuesser.java
index 1d2c010..16dcd1e 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/MissingTypesGuesser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/MissingTypesGuesser.java
@@ -477,6 +477,10 @@
 					AccessRestriction access) {
 				// constructors aren't searched
 			}
+			@Override
+			public void acceptModule(char[] moduleName) {
+				// TODO Auto-generated method stub
+			}
 			public void acceptPackage(char[] packageName) {
 				// package aren't searched
 			}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java
index 97f8a5e..eb00196 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java
@@ -56,4 +56,5 @@
 	int R_TARGET = 5;
 	int R_FINAL = 3; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=195346
 	int R_CONSTRUCTOR = 3; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=373409
+	int R_MODULE_DECLARATION = 31;
 }
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java
index 6aa7dea..7bfcf01 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.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
@@ -20,6 +20,7 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.jdt.core.IBuffer;
+import org.eclipse.jdt.core.IClassFile;
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IMember;
 import org.eclipse.jdt.core.IOpenable;
@@ -42,6 +43,7 @@
 import org.eclipse.jdt.internal.codeassist.impl.Engine;
 import org.eclipse.jdt.internal.codeassist.select.SelectionJavadocParser;
 import org.eclipse.jdt.internal.codeassist.select.SelectionNodeFound;
+import org.eclipse.jdt.internal.codeassist.select.SelectionOnPackageVisibilityReference;
 import org.eclipse.jdt.internal.codeassist.select.SelectionOnImportReference;
 import org.eclipse.jdt.internal.codeassist.select.SelectionOnPackageReference;
 import org.eclipse.jdt.internal.codeassist.select.SelectionOnQualifiedTypeReference;
@@ -58,6 +60,8 @@
 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.ImportReference;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.PackageVisibilityStatement;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -78,15 +82,18 @@
 import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
 import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
 import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
 import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
 import org.eclipse.jdt.internal.compiler.parser.Scanner;
 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
@@ -563,7 +570,8 @@
 	private boolean checkSelection(
 			char[] source,
 			int selectionStart,
-			int selectionEnd) {
+			int selectionEnd,
+			boolean isModuleInfo) {
 
 		Scanner scanner =
 			new Scanner(
@@ -655,7 +663,7 @@
 			}
 
 			// compute start and end of the last token
-			scanner.resetTo(nextCharacterPosition, end);
+			scanner.resetTo(nextCharacterPosition, end, isModuleInfo);
 			isolateLastName: do {
 				try {
 					token = scanner.getNextToken();
@@ -709,7 +717,7 @@
 					}
 				}  
 			} // there could be some innocuous widening, shouldn't matter.
-			scanner.resetTo(selectionStart, selectionEnd);
+			scanner.resetTo(selectionStart, selectionEnd, isModuleInfo);
 
 			boolean expectingIdentifier = true;
 			do {
@@ -979,7 +987,8 @@
 			System.out.println("SELECTION - Source :"); //$NON-NLS-1$
 			System.out.println(source);
 		}
-		if (!checkSelection(source, selectionSourceStart, selectionSourceEnd)) {
+		boolean isModuleInfo = CharOperation.endsWith(sourceUnit.getFileName(), TypeConstants.MODULE_INFO_FILE_NAME);
+		if (!checkSelection(source, selectionSourceStart, selectionSourceEnd, isModuleInfo)) {
 			return;
 		}
 		if (DEBUG) {
@@ -1082,20 +1091,21 @@
 						}
 					}
 				}
-				if (parsedUnit.types != null || parsedUnit.isPackageInfo()) {
-					if(selectDeclaration(parsedUnit))
-						return;
-//{ObjectTeams: include buildTypeBindings in try-catch: (may throw SelectionNodeFound)
-				  try {
-// orig:
-					this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
-					if ((this.unitScope = parsedUnit.scope)  != null) {
-/* orig:
-						try {
-  :giro */
-// SH}
+				try {
+					if (parsedUnit.isModuleInfo() && parsedUnit.moduleDeclaration != null) {
+						ModuleDeclaration module = parsedUnit.moduleDeclaration;
+						this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
+						module.resolveModuleDirectives(parsedUnit.scope);
+						module.resolvePackageDirectives(parsedUnit.scope);
+						module.resolveTypeDirectives(parsedUnit.scope);
+						acceptPackageVisibilityStatements(module.exports, parsedUnit.scope);
+						acceptPackageVisibilityStatements(module.opens, parsedUnit.scope);
+					} else if (parsedUnit.types != null || parsedUnit.isPackageInfo()) {
+						if(selectDeclaration(parsedUnit))
+							return;
+						this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
+						if ((this.unitScope = parsedUnit.scope)  != null) {
 							this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
-							
 							CompilationUnitDeclaration previousUnitBeingCompleted = this.lookupEnvironment.unitBeingCompleted;
 							this.lookupEnvironment.unitBeingCompleted = parsedUnit;
 //{ObjectTeams
@@ -1130,38 +1140,34 @@
 							if (parsedUnit.types != null)
 								for (TypeDeclaration type : parsedUnit.types)
 									resolveIncompleteParamMappings(type);
-					// close "if ((this.unitScope = parsedUnit.scope)  != null)":
-					}
 // SH}
-						} catch (SelectionNodeFound e) {
-							if (e.binding != null) {
-								if(DEBUG) {
-									System.out.println("SELECTION - Selection binding:"); //$NON-NLS-1$
-									System.out.println(e.binding.toString());
-								}
-								// if null then we found a problem in the selection node
-								selectFrom(e.binding, parsedUnit, sourceUnit, e.isDeclaration);
-							}
 						}
-//{ObjectTeams :
-						catch(SelectionNodesFound exc)
-						{
-						    if(exc._bindings != null)
-						    {
-								if(DEBUG) {
-									System.out.println("SELECTION - Selection binding:"); //$NON-NLS-1$
-									System.out.println(exc._bindings.toString());
-								}
-								// if null then we found a problem in the selection node
-							    for(int idx = 0; idx < exc._bindings.length; idx++)
-							    {
-							        selectFrom(exc._bindings[idx], parsedUnit, exc._isDeclaration);
-							    }
-						    }
-						}
-/* originally "if" was closed here:
 					}
-  :giro */
+				} catch (SelectionNodeFound e) {
+					if (e.binding != null) {
+						if(DEBUG) {
+							System.out.println("SELECTION - Selection binding:"); //$NON-NLS-1$
+							System.out.println(e.binding.toString());
+						}
+						// if null then we found a problem in the selection node
+						selectFrom(e.binding, parsedUnit, sourceUnit, e.isDeclaration);
+					}
+//{ObjectTeams :
+				} catch(SelectionNodesFound exc)
+					{
+					    if(exc._bindings != null)
+					    {
+							if(DEBUG) {
+								System.out.println("SELECTION - Selection binding:"); //$NON-NLS-1$
+								System.out.println(exc._bindings.toString());
+							}
+							// if null then we found a problem in the selection node
+						    for(int idx = 0; idx < exc._bindings.length; idx++)
+						    {
+						        selectFrom(exc._bindings[idx], parsedUnit, exc._isDeclaration);
+						    }
+					    }
+					}
 //haebor}
 				}
 			}
@@ -1224,6 +1230,16 @@
 	}
 // SH}
 
+	private void acceptPackageVisibilityStatements(PackageVisibilityStatement[] pvs, Scope scope) {
+		if (pvs != null) {
+			for (PackageVisibilityStatement pv : pvs) {
+				if (pv.pkgRef instanceof SelectionOnPackageVisibilityReference) {
+					this.noProposal = false;
+					this.requestor.acceptPackage(CharOperation.concatWith(((SelectionOnPackageVisibilityReference) pv.pkgRef).tokens, '.'));
+				}
+			}
+		}
+	}
 	private void selectMemberTypeFromImport(CompilationUnitDeclaration parsedUnit, char[] lastToken, ReferenceBinding ref, boolean staticOnly) {
 		int fieldLength = lastToken.length;
 		ReferenceBinding[] memberTypes = ref.memberTypes();
@@ -1541,6 +1557,15 @@
 			this.acceptedAnswer = true;
 		} else if(binding instanceof BaseTypeBinding) {
 			this.acceptedAnswer = true;
+		} else if (binding instanceof ModuleBinding) {
+			this.noProposal = false;
+			ModuleBinding moduleBinding = (ModuleBinding) binding;
+			this.requestor.acceptModule(
+					moduleBinding.moduleName,
+					moduleBinding.computeUniqueKey(),
+					this.actualSelectionStart,
+					this.actualSelectionEnd);
+			this.acceptedAnswer = true;
 		}
 	}
 
@@ -1717,26 +1742,29 @@
 					typeDeclaration = new ASTNodeFinder(parsedUnit).findType(context);
 				}
 			} else { // binary type
-				ClassFile classFile = (ClassFile) context.getClassFile();
-				BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(classFile);
-				ClassFileReader reader = null;
-				try {
-					reader = BinaryTypeFactory.rawReadType(descriptor, false/*don't fully initialize so as to keep constant pool (used below)*/);
-				} catch (ClassFormatException e) {
-					if (JavaCore.getPlugin().isDebugging()) {
-						e.printStackTrace(System.err);
+				IClassFile iClassFile = context.getClassFile();
+				if (iClassFile instanceof ClassFile) {
+					ClassFile classFile = (ClassFile) iClassFile;
+					BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(classFile);
+					ClassFileReader reader = null;
+					try {
+						reader = BinaryTypeFactory.rawReadType(descriptor, false/*don't fully initialize so as to keep constant pool (used below)*/);
+					} catch (ClassFormatException e) {
+						if (JavaCore.getPlugin().isDebugging()) {
+							e.printStackTrace(System.err);
+						}
 					}
+					if (reader == null) {
+						throw classFile.newNotPresentException();
+					}
+					CompilationResult result = new CompilationResult(reader.getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit);
+					parsedUnit = new CompilationUnitDeclaration(this.parser.problemReporter(), result, 0);
+					HashSetOfCharArrayArray typeNames = new HashSetOfCharArrayArray();
+					
+					BinaryTypeConverter converter = new BinaryTypeConverter(this.parser.problemReporter(), result, typeNames);
+					typeDeclaration = converter.buildTypeDeclaration(context, parsedUnit);
+					parsedUnit.imports = converter.buildImports(reader);
 				}
-				if (reader == null) {
-					throw classFile.newNotPresentException();
-				}
-				CompilationResult result = new CompilationResult(reader.getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit);
-				parsedUnit = new CompilationUnitDeclaration(this.parser.problemReporter(), result, 0);
-				HashSetOfCharArrayArray typeNames = new HashSetOfCharArrayArray();
-
-				BinaryTypeConverter converter = new BinaryTypeConverter(this.parser.problemReporter(), result, typeNames);
-				typeDeclaration = converter.buildTypeDeclaration(context, parsedUnit);
-				parsedUnit.imports = converter.buildImports(reader);
 			}
 
 			if (typeDeclaration != null) {
@@ -2203,4 +2231,10 @@
 			return InheritDocVisitor.CONTINUE;
 		}
 	}
+
+	@Override
+	public void acceptModule(char[] moduleName) {
+		// TODO Auto-generated method stub
+		
+	}
 }
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnExportReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnExportReference.java
new file mode 100644
index 0000000..0f8ff29
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnExportReference.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+import org.eclipse.jdt.internal.compiler.ast.ExportsStatement;
+import org.eclipse.jdt.internal.compiler.ast.ImportReference;
+/*
+ * Completion node build by the parser in any case it was intending to
+ * reduce an exports reference containing the cursor location.
+ * e.g.
+ *
+ *	module myModule {
+ *  exports packageo[cursor];
+ *  }
+ *
+ *	module myModule {
+ *	---> <CompleteOnExport:packageo>
+ *  }
+ *
+ * The source range is always of length 0.
+ * The arguments of the allocation expression are all the arguments defined
+ * before the cursor.
+ */
+
+public class CompletionOnExportReference extends ExportsStatement {
+
+	public CompletionOnExportReference(ImportReference ref) {
+		super(ref, null);
+	}
+	public StringBuffer print(int indent, StringBuffer output) {
+
+		printIndent(indent, output).append("<CompleteOnExport:"); //$NON-NLS-1$
+		output.append(this.pkgName);
+		return output.append('>');
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModule2.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModule2.java
new file mode 100644
index 0000000..21f282a
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModule2.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
+
+public class CompletionOnKeywordModule2 extends ModuleReference implements CompletionOnKeyword {
+	private char[] token;
+	private char[][] possibleKeywords;
+
+	public CompletionOnKeywordModule2(char[] token, long pos, char[][] possibleKeywords) {
+		super(new char[][] {token}, new long[] {pos}); // dummy
+		this.token = token;
+		this.possibleKeywords = possibleKeywords;
+		this.sourceStart = (int) (pos>>>32)  ;
+		this.sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
+	}
+
+	@Override
+	public char[] getToken() {
+		return this.token;
+	}
+
+	@Override
+	public char[][] getPossibleKeywords() {
+		return this.possibleKeywords;
+	}
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModuleDeclaration.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModuleDeclaration.java
new file mode 100644
index 0000000..3650305
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModuleDeclaration.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+
+public class CompletionOnKeywordModuleDeclaration extends ModuleDeclaration implements CompletionOnKeyword {
+
+	private char[] token;
+	private char[][] possibleKeywords;
+
+	public CompletionOnKeywordModuleDeclaration(char[] token, long pos, char[][] possibleKeywords) {
+		super(null, new char[][]{token}, new long[]{pos});
+		this.token = token;
+		this.possibleKeywords = possibleKeywords;
+	}
+
+	@Override
+	public char[] getToken() {
+		return this.token;
+	}
+
+	@Override
+	public char[][] getPossibleKeywords() {
+		return this.possibleKeywords;
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModuleInfo.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModuleInfo.java
new file mode 100644
index 0000000..91d6f4f
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnKeywordModuleInfo.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+import org.eclipse.jdt.internal.compiler.ast.ExportsStatement;
+import org.eclipse.jdt.internal.compiler.ast.ImportReference;
+
+/**
+ * 
+ * This class is independent of its parent class and is in fact a dummy ExportsStatement. Used to hook
+ * into the existing module declaration type and is used as a placeholder for keyword completion. This can
+ * be any module keyword completion and not necessarily related to exports statement.
+ */
+public class CompletionOnKeywordModuleInfo extends ExportsStatement implements CompletionOnKeyword {
+	private char[] token;
+	private char[][] possibleKeywords;
+
+	public CompletionOnKeywordModuleInfo(char[] token, long pos, char[][] possibleKeywords) {
+		super(new ImportReference(new char[][] {token}, new long[] {pos}, false, 0), null); // dummy
+		this.token = token;
+		this.possibleKeywords = possibleKeywords;
+		this.sourceStart = (int) (pos>>>32)  ;
+		this.sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
+	}
+
+	@Override
+	public char[] getToken() {
+		return this.token;
+	}
+
+	@Override
+	public char[][] getPossibleKeywords() {
+		return this.possibleKeywords;
+	}
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnModuleDeclaration.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnModuleDeclaration.java
new file mode 100644
index 0000000..763b861
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnModuleDeclaration.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+
+public class CompletionOnModuleDeclaration extends ModuleDeclaration {
+
+	public CompletionOnModuleDeclaration(CompilationResult compilationResult, char[][] tokens, long[] positions) {
+		super(compilationResult, tokens, positions);
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnModuleReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnModuleReference.java
new file mode 100644
index 0000000..7ccea8c
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnModuleReference.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
+
+public class CompletionOnModuleReference extends ModuleReference {
+
+	public CompletionOnModuleReference(char[] ident, long pos) {
+		this(new char[][]{ident}, new long[]{pos});
+	}
+	public CompletionOnModuleReference(char[][] tokens, long[] sourcePositions) {
+		super(tokens, sourcePositions);
+	}
+
+	public ModuleBinding resolve(Scope scope) {
+		super.resolve(scope);
+//		if (this.binding != null) {
+//			throw new CompletionNodeFound(this, this.binding, scope);
+//		} else {
+			throw new CompletionNodeFound();
+		//}
+	}
+	public StringBuffer print(int indent, StringBuffer output) {
+
+		printIndent(indent, output).append("<CompleteOnModuleReference:"); //$NON-NLS-1$
+		for (int i = 0; i < this.tokens.length; i++) {
+			if (i > 0) output.append('.');
+			output.append(this.tokens[i]);
+		}
+		return output.append('>');
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnPackageVisibilityReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnPackageVisibilityReference.java
new file mode 100644
index 0000000..aad8358
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnPackageVisibilityReference.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+
+/*
+ * Completion node build by the parser in any case it was intending to
+ * reduce an exports or an opens reference containing the cursor location.
+ * e.g.
+ *
+ *	module myModule {
+ *  exports packageo[cursor];
+ *  opens packageo[cursor];
+
+ *  }
+ *
+ *	module myModule {
+ *	---> <CompleteOnPackageVisibilityReference:packageo>
+ *  }
+ *
+ * The source range is always of length 0.
+ * The arguments of the allocation expression are all the arguments defined
+ * before the cursor.
+ */
+
+public class CompletionOnPackageVisibilityReference extends CompletionOnImportReference {
+
+	String pkgName;
+	public CompletionOnPackageVisibilityReference(char[][] ident, long[] pos) {
+		super(ident, pos, 0);
+		this.pkgName = new String(CharOperation.concatWith(ident, '.'));
+	}
+
+	public StringBuffer print(int indent, StringBuffer output) {
+		printIndent(indent, output).append("<CompleteOnPackageVisibilityReference:"); //$NON-NLS-1$
+		output.append(this.pkgName);
+		return output.append('>');
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesImplementationsQualifiedTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesImplementationsQualifiedTypeReference.java
new file mode 100644
index 0000000..8564a03
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesImplementationsQualifiedTypeReference.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+public class CompletionOnProvidesImplementationsQualifiedTypeReference extends CompletionOnQualifiedTypeReference {
+
+	public CompletionOnProvidesImplementationsQualifiedTypeReference(char[][] previousIdentifiers, char[] completionIdentifier,
+			long[] positions) {
+		super(previousIdentifiers, completionIdentifier, positions);
+	}
+
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesImplementationsSingleTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesImplementationsSingleTypeReference.java
new file mode 100644
index 0000000..420fffe
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesImplementationsSingleTypeReference.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+public class CompletionOnProvidesImplementationsSingleTypeReference extends CompletionOnSingleTypeReference {
+	public CompletionOnProvidesImplementationsSingleTypeReference(char[] source, long pos) {
+		super(source, pos);
+	}
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesInterfacesQualifiedTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesInterfacesQualifiedTypeReference.java
new file mode 100644
index 0000000..180c0ca
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesInterfacesQualifiedTypeReference.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+public class CompletionOnProvidesInterfacesQualifiedTypeReference extends CompletionOnQualifiedTypeReference {
+
+	public CompletionOnProvidesInterfacesQualifiedTypeReference(char[][] previousIdentifiers, char[] completionIdentifier,
+			long[] positions) {
+		super(previousIdentifiers, completionIdentifier, positions);
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesInterfacesSingleTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesInterfacesSingleTypeReference.java
new file mode 100644
index 0000000..1d08099
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnProvidesInterfacesSingleTypeReference.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+public class CompletionOnProvidesInterfacesSingleTypeReference extends CompletionOnSingleTypeReference {
+	public CompletionOnProvidesInterfacesSingleTypeReference(char[] source, long pos) {
+		super(source, pos);
+	}
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnUsesQualifiedTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnUsesQualifiedTypeReference.java
new file mode 100644
index 0000000..ae5a64c
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnUsesQualifiedTypeReference.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+public class CompletionOnUsesQualifiedTypeReference extends CompletionOnQualifiedTypeReference {
+
+	public CompletionOnUsesQualifiedTypeReference(char[][] previousIdentifiers, char[] completionIdentifier,
+			long[] positions) {
+		super(previousIdentifiers, completionIdentifier, positions);
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnUsesSingleTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnUsesSingleTypeReference.java
new file mode 100644
index 0000000..2cc19b2
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnUsesSingleTypeReference.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.codeassist.complete;
+
+public class CompletionOnUsesSingleTypeReference extends CompletionOnSingleTypeReference {
+
+	public CompletionOnUsesSingleTypeReference(char[] source, long pos) {
+		super(source, pos);
+	}
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
index 195942f..9daa066 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
@@ -99,11 +99,19 @@
 	// added for https://bugs.eclipse.org/bugs/show_bug.cgi?id=261534
 	protected static final int K_BETWEEN_INSTANCEOF_AND_RPAREN = COMPLETION_PARSER + 41;
 	protected static final int K_INSIDE_IMPORT_STATEMENT = COMPLETION_PARSER + 43;
+	protected static final int K_INSIDE_EXPORTS_STATEMENT = COMPLETION_PARSER + 44;
+	protected static final int K_INSIDE_REQUIRES_STATEMENT = COMPLETION_PARSER + 45;
+	protected static final int K_INSIDE_USES_STATEMENT = COMPLETION_PARSER + 46;
+	protected static final int K_INSIDE_PROVIDES_STATEMENT = COMPLETION_PARSER + 47;
+	protected static final int K_AFTER_PACKAGE_IN_PACKAGE_VISIBILITY_STATEMENT = COMPLETION_PARSER + 48;
+	protected static final int K_AFTER_NAME_IN_PROVIDES_STATEMENT = COMPLETION_PARSER + 49;
+	protected static final int K_AFTER_WITH_IN_PROVIDES_STATEMENT = COMPLETION_PARSER + 50;
+	protected static final int K_INSIDE_OPENS_STATEMENT = COMPLETION_PARSER + 51;
 
 //{ObjectTeams: OT specific kinds
-	protected static final int K_BETWEEN_WITH_AND_RIGHT_PAREN = COMPLETION_PARSER + 42;
-	protected static final int K_BETWEEN_WITHIN_AND_RIGHT_PAREN = COMPLETION_PARSER + 43;
-	protected static final int K_EXPECTING_RIGHT_METHODSPEC = COMPLETION_PARSER + 44;
+	protected static final int K_BETWEEN_WITH_AND_RIGHT_PAREN = COMPLETION_PARSER + 52;
+	protected static final int K_BETWEEN_WITHIN_AND_RIGHT_PAREN = COMPLETION_PARSER + 53;
+	protected static final int K_EXPECTING_RIGHT_METHODSPEC = COMPLETION_PARSER + 54;
 //gbr+SH}
 
 	public final static char[] FAKE_TYPE_NAME = new char[]{' '};
@@ -276,16 +284,17 @@
 		if (this.currentElement instanceof RecoveredUnit){
 			if (orphan instanceof ImportReference){
 				this.currentElement.add((ImportReference)orphan, 0);
+			} else if (orphan instanceof ModuleDeclaration) {
+				this.currentElement.add((ModuleDeclaration)orphan, 0);
 			}
-		}
-
-		/* if in context of a type, then persists the identifier into a fake field return type */
-		if (this.currentElement instanceof RecoveredType){
+		} else if (this.currentElement instanceof RecoveredType){	/* if in context of a type, then persists the identifier into a fake field return type */
 			RecoveredType recoveredType = (RecoveredType)this.currentElement;
 			/* filter out cases where scanner is still inside type header */
 			if (recoveredType.foundOpeningBrace) {
 				/* generate a pseudo field with a completion on type reference */
 				if (orphan instanceof TypeReference){
+					if (isInsideModuleInfo()) return; //taken care elsewhere
+
 					TypeReference fieldType;
 
 					int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER);
@@ -1503,6 +1512,7 @@
 private boolean checkKeyword() {
 	if (this.currentElement instanceof RecoveredUnit) {
 		RecoveredUnit unit = (RecoveredUnit) this.currentElement;
+		if (unit.unitDeclaration.isModuleInfo()) return false;
 		int index = -1;
 		if ((index = this.indexOfAssistIdentifier()) > -1) {
 			int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1;
@@ -1571,6 +1581,78 @@
 	}
 	return false;
 }
+
+private enum ModuleKeyword {
+	FIRST_ALL,
+	TO,
+	PROVIDES_WITH,
+	NOT_A_KEYWORD
+}
+
+private ModuleKeyword getKeyword() {
+	ModuleKeyword keyword = ModuleKeyword.FIRST_ALL;
+	if (isInModuleStatements()) {
+		if (foundToken(K_AFTER_PACKAGE_IN_PACKAGE_VISIBILITY_STATEMENT)) keyword = ModuleKeyword.TO;
+		else if (foundToken(K_AFTER_NAME_IN_PROVIDES_STATEMENT)) keyword = ModuleKeyword.PROVIDES_WITH;
+		else keyword = ModuleKeyword.NOT_A_KEYWORD;
+	}
+	return keyword;
+}
+private char[][] getModuleKeywords(ModuleKeyword keyword) {
+	if (keyword == ModuleKeyword.TO) return new char[][]{Keywords.TO};
+	else if (keyword == ModuleKeyword.PROVIDES_WITH) return new char[][]{Keywords.WITH};
+	else return new char[][]{Keywords.EXPORTS, Keywords.OPENS, Keywords.REQUIRES, Keywords.PROVIDES, Keywords.USES};
+}
+private boolean checkModuleInfoConstructs() {
+
+	if (!isInsideModuleInfo()) return false;
+
+	int index = -1;
+	if ((index = this.indexOfAssistIdentifier()) <= -1) return false;
+	
+	if (this.currentElement instanceof RecoveredModule) {
+		RecoveredModule module = (RecoveredModule) this.currentElement;
+		if (checkModuleInfoKeyword(module, index)) return true;
+	} else  {
+		ModuleKeyword keyword = ModuleKeyword.NOT_A_KEYWORD;
+		if (isInModuleStatements()) {
+			if (foundToken(K_AFTER_PACKAGE_IN_PACKAGE_VISIBILITY_STATEMENT)) keyword =  ModuleKeyword.TO;
+			if (foundToken(K_AFTER_NAME_IN_PROVIDES_STATEMENT)) keyword = ModuleKeyword.PROVIDES_WITH;
+		}
+		if (keyword == ModuleKeyword.NOT_A_KEYWORD) return false;
+		
+		int length = this.identifierLengthStack[this.identifierLengthPtr];
+		int ptr = this.identifierPtr - length + index + 1;
+		
+		char[] ident = this.identifierStack[ptr];
+		long pos = this.identifierPositionStack[ptr];		
+		char[][] keywords = getModuleKeywords(keyword);		
+		if (this.currentElement instanceof RecoveredPackageVisibilityStatement) {
+			RecoveredPackageVisibilityStatement rPvs = (RecoveredPackageVisibilityStatement) this.currentElement;
+			rPvs.add(new CompletionOnKeywordModule2(ident, pos, keywords), 0);
+			return true;
+		} else if (this.currentElement instanceof RecoveredProvidesStatement) {
+			RecoveredProvidesStatement rPs = (RecoveredProvidesStatement) this.currentElement;
+			rPs.add(new CompletionOnKeyword1(ident, pos, keywords), 0);
+			return true;
+		}
+	} 
+	return false;
+}
+private boolean checkModuleInfoKeyword(RecoveredModule module, int index) {
+	ModuleKeyword keyword = getKeyword();
+	if (keyword == ModuleKeyword.NOT_A_KEYWORD) return false;
+
+	int length = this.identifierLengthStack[this.identifierLengthPtr];
+	int ptr = this.identifierPtr - length + index + 1;
+
+	char[] ident = this.identifierStack[ptr];
+	long pos = this.identifierPositionStack[ptr];
+	char[][] keywords = getModuleKeywords(keyword);
+	module.add(new CompletionOnKeywordModuleInfo(ident, pos, keywords), 0);
+	return true;
+}
+
 private boolean checkInstanceofKeyword() {
 	if(isInsideMethod()) {
 		int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER);
@@ -2330,6 +2412,7 @@
 
 	if (checkMemberValueName()) return;
 	if (checkKeyword()) return;
+	if (checkModuleInfoConstructs()) return;
 	if (checkRecoveredType()) return;
 	if (checkRecoveredMethod()) return;
 //{ObjectTeams: also consider method mappings
@@ -2340,7 +2423,8 @@
 	if (!(isInsideMethod() && !this.diet)
 		&& !isIndirectlyInsideFieldInitialization()
 		&& !isIndirectlyInsideEnumConstantnitialization()
-		&& !isInsideAttributeValue()) return;
+		&& !isInsideAttributeValue()
+		&& !isInsideModuleInfo()) return;
 
 	/*
 	 	In some cases, the completion identifier may not have yet been consumed,
@@ -2369,6 +2453,7 @@
 	// no need to check further if we are not at the cursor location
 	if (this.indexOfAssistIdentifier() < 0) return;
 
+	if (checkModuleInfoConstructs()) return;
 	if (checkClassInstanceCreation()) return;
 	if (checkMemberAccess()) return;
 	if (checkClassLiteralAccess()) return;
@@ -3727,6 +3812,22 @@
 	popElement(K_EXPECTING_RIGHT_METHODSPEC);
 }
 // SH}
+protected void consumeModuleHeader() {
+	super.consumeModuleHeader();
+}
+protected void consumeProvidesInterface() {
+	super.consumeProvidesInterface();
+	pushOnElementStack(K_AFTER_NAME_IN_PROVIDES_STATEMENT);
+}
+protected void consumeProvidesStatement() {
+	super.consumeProvidesStatement();
+	popElement(K_INSIDE_PROVIDES_STATEMENT);
+}
+protected void consumeWithClause() {
+	super.consumeWithClause();
+	popElement(K_AFTER_WITH_IN_PROVIDES_STATEMENT);
+}
+
 protected void consumeReferenceType() {
 	if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name
 		// potential receiver is being poped, so reset potential receiver
@@ -3735,6 +3836,10 @@
 	}
 	super.consumeReferenceType();
 }
+protected void consumeRequiresStatement() {
+	super.consumeRequiresStatement();
+	popElement(K_INSIDE_REQUIRES_STATEMENT);
+}
 protected void consumeRestoreDiet() {
 	super.consumeRestoreDiet();
 	if (isInsideMethod()) {
@@ -3746,6 +3851,15 @@
 		popElement(K_LOCAL_INITIALIZER_DELIMITER);
 	}
 }
+protected void consumeExportsStatement() {
+	super.consumeExportsStatement();
+	popElement(K_AFTER_PACKAGE_IN_PACKAGE_VISIBILITY_STATEMENT);
+	popElement(K_INSIDE_EXPORTS_STATEMENT);
+}
+protected void consumeSinglePkgName() {
+	super.consumeSinglePkgName();
+	pushOnElementStack(K_AFTER_PACKAGE_IN_PACKAGE_VISIBILITY_STATEMENT);
+}
 protected void consumeSingleMemberAnnotation(boolean isTypeAnnotation) {
 	if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN &&
 			(this.topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) & ANNOTATION_NAME_COMPLETION) != 0 ) {
@@ -3951,6 +4065,21 @@
 	}
 	if (token == TokenNameimport) {
 		pushOnElementStack(K_INSIDE_IMPORT_STATEMENT);
+	}	else if (token == TokenNameexports) {
+		pushOnElementStack(K_INSIDE_EXPORTS_STATEMENT);
+	}	else if (token == TokenNameopens) {
+		pushOnElementStack(K_INSIDE_OPENS_STATEMENT);
+	}	else if (token == TokenNameto) {
+		popElement(K_AFTER_PACKAGE_IN_PACKAGE_VISIBILITY_STATEMENT);
+	}	else if (token == TokenNamerequires) {
+		pushOnElementStack(K_INSIDE_REQUIRES_STATEMENT);
+	} else if (token == TokenNameprovides) {
+		pushOnElementStack(K_INSIDE_PROVIDES_STATEMENT);
+	} else if (token == TokenNameuses) {
+		pushOnElementStack(K_INSIDE_USES_STATEMENT);
+	}	else if (token == TokenNamewith) {
+		popElement(K_AFTER_NAME_IN_PROVIDES_STATEMENT);
+		pushOnElementStack(K_AFTER_WITH_IN_PROVIDES_STATEMENT);
 	}
 
 	// if in a method or if in a field initializer
@@ -4494,6 +4623,12 @@
 	super.consumeOpenFakeBlock();
 	pushOnElementStack(K_BLOCK_DELIMITER);
 }
+@Override
+protected void consumeOpensStatement() {
+	super.consumeOpensStatement();
+	popElement(K_AFTER_PACKAGE_IN_PACKAGE_VISIBILITY_STATEMENT);
+	popElement(K_INSIDE_OPENS_STATEMENT);
+}
 protected void consumeRightParen() {
 	super.consumeRightParen();
 }
@@ -4635,6 +4770,11 @@
 	super.consumeUnionTypeAsClassType();
 	popElement(K_NEXT_TYPEREF_IS_EXCEPTION);
 }
+protected void consumeUsesStatement() {
+	super.consumeUsesStatement();
+	popElement(K_INSIDE_USES_STATEMENT);
+}
+
 protected void consumeWildcard() {
 	super.consumeWildcard();
 	if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource()
@@ -4741,9 +4881,34 @@
 	}
 	return methodDeclaration;
 }
+public ImportReference createAssistPackageVisibilityReference(char[][] tokens, long[] positions){
+	return new CompletionOnPackageVisibilityReference(tokens, positions);
+}
 public ImportReference createAssistImportReference(char[][] tokens, long[] positions, int mod){
 	return new CompletionOnImportReference(tokens, positions, mod);
 }
+@Override
+public ModuleReference createAssistModuleReference(int index) {
+	/* retrieve identifiers subset and whole positions, the assist node positions
+	should include the entire replaced source. */
+	int length = this.identifierLengthStack[this.identifierLengthPtr];
+	char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
+	this.identifierLengthPtr--;
+	this.identifierPtr -= length;
+	long[] positions = new long[length];
+	System.arraycopy(
+			this.identifierPositionStack,
+			this.identifierPtr + 1,
+			positions,
+			0,
+			length);
+	return new CompletionOnModuleReference(subset, positions);
+}
+@Override
+public ModuleDeclaration createAssistModuleDeclaration(CompilationResult compilationResult, char[][] tokens,
+		long[] positions) {
+	return new CompletionOnModuleDeclaration(compilationResult, tokens, positions);
+}
 //{ObjectTeams: packageModifiers added
 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions, int packageModifiers){
 	return new CompletionOnPackageReference(tokens, positions, packageModifiers);
@@ -4756,6 +4921,14 @@
 					positions,
 					isInsideAttributeValue());
 }
+private TypeReference checkAndCreateModuleQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions) {
+	if (isInUsesStatement()) return new CompletionOnUsesQualifiedTypeReference(previousIdentifiers, assistName, positions);
+	if (isInProvidesStatement()) {
+		if (isAfterWithClause()) return new CompletionOnProvidesImplementationsQualifiedTypeReference(previousIdentifiers, assistName, positions);
+		return new CompletionOnProvidesInterfacesQualifiedTypeReference(previousIdentifiers, assistName, positions);
+	}
+	return new CompletionOnQualifiedTypeReference(previousIdentifiers,	assistName,	positions);
+}
 public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions){
 	switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) {
 		case K_NEXT_TYPEREF_IS_EXCEPTION :
@@ -4779,7 +4952,7 @@
 					positions,
 					CompletionOnQualifiedTypeReference.K_INTERFACE);
 		default :
-			return new CompletionOnQualifiedTypeReference(
+			return checkAndCreateModuleQualifiedAssistTypeReference(
 					previousIdentifiers,
 					assistName,
 					positions);
@@ -4959,6 +5132,14 @@
 		}
 	}
 }
+private TypeReference checkAndCreateModuleSingleAssistTypeReference(char[] assistName, long position) {
+	if (isInUsesStatement()) return new CompletionOnUsesSingleTypeReference(assistName, position);
+	if (isInProvidesStatement()) {
+		if (isAfterWithClause()) return new CompletionOnProvidesImplementationsSingleTypeReference(assistName, position);
+		return new CompletionOnProvidesInterfacesSingleTypeReference(assistName, position);
+	}
+	return new CompletionOnSingleTypeReference(assistName,position);
+}
 public TypeReference createSingleAssistTypeReference(char[] assistName, long position) {
 	switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) {
 		case K_NEXT_TYPEREF_IS_EXCEPTION :
@@ -4970,7 +5151,7 @@
 		case K_NEXT_TYPEREF_IS_INTERFACE :
 			return new CompletionOnSingleTypeReference(assistName, position, CompletionOnSingleTypeReference.K_INTERFACE);
 		default :
-			return new CompletionOnSingleTypeReference(assistName, position);
+			return checkAndCreateModuleSingleAssistTypeReference(assistName, position);
 	}
 }
 public TypeReference createParameterizedSingleAssistTypeReference(TypeReference[] typeArguments, char[] assistName, long position) {
@@ -5542,7 +5723,7 @@
 	this.cursorLocation = 0;
 	flushAssistState();
 }
-public void restoreAssistParser(Object parserState) {
+public void restoreAssistParser(Object parserState) { 	
 	int[] state = (int[]) parserState;
 	
 	CompletionScanner completionScanner = (CompletionScanner)this.scanner;
@@ -5744,10 +5925,10 @@
 	}
 	return false;	
 }
-protected boolean isInImportStatement() {
+private boolean foundToken(int token) {
 	int i = this.elementPtr;
 	while (i > -1) {
-		if (this.elementKindStack[i] == K_INSIDE_IMPORT_STATEMENT) {
+		if (this.elementKindStack[i] == token) {
 			return true;
 		}
 		i--;
@@ -5813,4 +5994,33 @@
 	return spec;
 }
 // SH}
+
+protected boolean isInImportStatement() {
+	return foundToken(K_INSIDE_IMPORT_STATEMENT);
+}
+protected boolean isInExportsStatement() {
+	return foundToken(K_INSIDE_EXPORTS_STATEMENT);
+}
+protected boolean isInOpensStatement() {
+	return foundToken(K_INSIDE_OPENS_STATEMENT);
+}
+protected boolean isInRequiresStatement() {
+	return foundToken(K_INSIDE_REQUIRES_STATEMENT);
+}
+protected boolean isInUsesStatement() {
+	return foundToken(K_INSIDE_USES_STATEMENT);
+}
+protected boolean isInProvidesStatement() {
+	return foundToken(K_INSIDE_PROVIDES_STATEMENT);
+}
+protected boolean isAfterWithClause() {
+	return foundToken(K_AFTER_WITH_IN_PROVIDES_STATEMENT);
+}
+protected boolean isInModuleStatements() {
+	return isInExportsStatement() ||
+			isInOpensStatement() ||
+			isInRequiresStatement() ||
+			isInProvidesStatement() ||
+			isInUsesStatement();
+}
 }
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java
index 6ba013b..99fd630 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java
@@ -20,6 +20,7 @@
 import java.util.HashSet;
 
 import org.eclipse.jdt.core.compiler.InvalidInputException;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
@@ -37,7 +38,10 @@
 import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.MessageSend;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
 import org.eclipse.jdt.internal.compiler.ast.NameReference;
+import org.eclipse.jdt.internal.compiler.ast.RequiresStatement;
 import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
 import org.eclipse.jdt.internal.compiler.ast.Statement;
 import org.eclipse.jdt.internal.compiler.ast.SuperReference;
@@ -49,6 +53,7 @@
 import org.eclipse.jdt.internal.compiler.parser.Parser;
 import org.eclipse.jdt.internal.compiler.parser.RecoveredBlock;
 import org.eclipse.jdt.internal.compiler.parser.RecoveredElement;
+import org.eclipse.jdt.internal.compiler.parser.RecoveredExportsStatement;
 import org.eclipse.jdt.internal.compiler.parser.RecoveredField;
 import org.eclipse.jdt.internal.compiler.parser.RecoveredInitializer;
 import org.eclipse.jdt.internal.compiler.parser.RecoveredLocalVariable;
@@ -103,7 +108,8 @@
 	protected static final int K_ATTRIBUTE_VALUE_DELIMITER = ASSIST_PARSER + 5; // whether we are inside a annotation attribute valuer
 	protected static final int K_ENUM_CONSTANT_DELIMITER = ASSIST_PARSER + 6; // whether we are inside a field initializer
 	protected static final int K_LAMBDA_EXPRESSION_DELIMITER = ASSIST_PARSER + 7; // whether we are inside a lambda expression
-	
+	protected static final int K_MODULE_INFO_DELIMITER = ASSIST_PARSER + 8; // whether we are inside a module info declaration
+
 	// selector constants
 	protected static final int THIS_CONSTRUCTOR = -1;
 	protected static final int SUPER_CONSTRUCTOR = -2;
@@ -111,7 +117,7 @@
 	// enum constant constants
 	protected static final int NO_BODY = 0;
 	protected static final int WITH_BODY = 1;
-	
+
 	protected static final int EXPRESSION_BODY = 0;
 	protected static final int BLOCK_BODY = 1;
 
@@ -192,6 +198,7 @@
 		flushAssistState();
 		flushElementStack();
 		this.snapShot = null;
+		initModuleInfo(element);
 		return element;
 	}
 
@@ -378,6 +385,22 @@
 
 	return element;
 }
+
+private void initModuleInfo(RecoveredElement element) {
+	if (element  instanceof RecoveredUnit) {
+		RecoveredUnit unit = (RecoveredUnit) element;
+		if (unit.unitDeclaration.isModuleInfo()) {
+			ASTNode node = null;
+			int i = 0;
+			for (; i <= this.astPtr; i++) {
+				if ((node = this.astStack[i]) instanceof ModuleDeclaration) {
+					unit.add((ModuleDeclaration) node, this.bracketDepth); 
+					break;
+				}
+			}
+		}
+	}
+}
 protected void consumeAnnotationTypeDeclarationHeader() {
 	super.consumeAnnotationTypeDeclarationHeader();
 	pushOnElementStack(K_TYPE_DELIMITER);
@@ -701,6 +724,57 @@
 		this.lastCheckPoint = messageSend.sourceEnd + 1;
 	}
 }
+protected void consumeModuleHeader() {
+	pushOnElementStack(K_MODULE_INFO_DELIMITER);
+	// ModuleHeader ::= 'module' Name
+	/* build an ImportRef build from the last name
+	stored in the identifier stack. */
+
+	int index;
+
+	/* no need to take action if not inside assist identifiers */
+	if ((index = indexOfAssistIdentifier()) < 0) {
+		super.consumeModuleHeader();
+		return;
+	}
+	/* retrieve identifiers subset and whole positions, the assist node positions
+	should include the entire replaced source. */
+	int length = this.identifierLengthStack[this.identifierLengthPtr];
+	char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
+	this.identifierLengthPtr--;
+	this.identifierPtr -= length;
+	long[] positions = new long[length];
+	System.arraycopy(
+			this.identifierPositionStack,
+			this.identifierPtr + 1,
+			positions,
+			0,
+			length);
+	ModuleDeclaration typeDecl = createAssistModuleDeclaration(this.compilationUnit.compilationResult, subset, positions);
+
+	this.compilationUnit.moduleDeclaration = typeDecl;
+	this.assistNode = typeDecl;
+	this.lastCheckPoint = typeDecl.sourceEnd + 1;
+
+	//compute the declaration source too
+	typeDecl.declarationSourceStart = this.intStack[this.intPtr--];
+
+	typeDecl.bodyStart = typeDecl.sourceEnd + 1;
+	pushOnAstStack(typeDecl);
+
+	this.listLength = 0; // will be updated when reading super-interfaces
+	// recovery
+	if (this.currentElement != null){
+		this.lastCheckPoint = typeDecl.bodyStart;
+		this.currentElement = this.currentElement.add(typeDecl, 0);
+		this.lastIgnoredToken = -1;
+	}
+}
+
+protected void consumeModuleDeclaration() {
+	super.consumeModuleDeclaration();
+	popElement(K_MODULE_INFO_DELIMITER);
+}
 protected void consumeNestedMethod() {
 	super.consumeNestedMethod();
 	if(!isInsideMethod()) pushOnElementStack(K_METHOD_DELIMITER);
@@ -928,6 +1002,104 @@
 		this.restartRecovery = true; // used to avoid branching back into the regular automaton
 	}
 }
+protected void consumeSinglePkgName() {
+	int index;
+	/* no need to take action if not inside assist identifiers */
+	if ((index = indexOfAssistIdentifier()) < 0) {
+		super.consumeSinglePkgName();
+		return;
+	}
+	/* retrieve identifiers subset and whole positions, the assist node positions
+	should include the entire replaced source. */
+	int length = this.identifierLengthStack[this.identifierLengthPtr];
+	char[][] subset = identifierSubSet(index+1); // include the assistIdentifier
+	this.identifierLengthPtr--;
+	this.identifierPtr -= length;
+	long[] positions = new long[length];
+	System.arraycopy(
+			this.identifierPositionStack,
+			this.identifierPtr + 1,
+			positions,
+			0,
+			length);
+
+	/* build specific assist node on import statement */
+	ImportReference reference = createAssistPackageVisibilityReference(subset, positions);
+	this.assistNode = reference;
+	this.lastCheckPoint = reference.sourceEnd + 1;
+
+	pushOnAstStack(reference);
+
+	if (this.currentToken == TokenNameSEMICOLON) {
+		reference.declarationSourceEnd = this.scanner.currentPosition - 1;
+	} else {
+		reference.declarationSourceEnd = (int) positions[length-1];
+	}
+}
+protected void consumeSingleTargetModuleName() {
+	int index;
+	/* no need to take action if not inside assist identifiers */
+	if ((index = indexOfAssistIdentifier()) < 0) {
+		super.consumeSingleTargetModuleName();
+		return;
+	}
+
+	/* build specific assist node on targetted exports statement */
+	ModuleReference reference = createAssistModuleReference(index);
+	this.assistNode = reference;
+	this.lastCheckPoint = reference.sourceEnd + 1;
+	pushOnAstStack(reference);
+
+	// recovery - TBD
+	if (this.currentElement instanceof RecoveredExportsStatement){
+		// TODO
+		this.lastCheckPoint = reference.sourceEnd+1;
+		this.currentElement = ((RecoveredExportsStatement) this.currentElement).add(reference, 0);
+		this.lastIgnoredToken = -1;
+		//this.restartRecovery = true; // used to avoid branching back into the regular automaton
+	}
+
+}
+protected void consumeSingleRequiresModuleName() {
+
+	int index = indexOfAssistIdentifier();
+	/* no need to take action if not inside assist identifiers */
+	if (index < 0) {
+		super.consumeSingleRequiresModuleName();
+		return;
+	}
+
+	/* build specific assist node on requires statement */
+	ModuleReference reference = createAssistModuleReference(index);
+	this.assistNode = reference;
+	this.lastCheckPoint = reference.sourceEnd + 1;
+	RequiresStatement req = new RequiresStatement(reference);
+	if (this.currentToken == TokenNameSEMICOLON){
+		req.declarationSourceEnd = this.scanner.currentPosition - 1;
+	} else {
+		req.declarationSourceEnd = reference.sourceEnd;
+	}
+	req.sourceStart = req.declarationSourceStart;
+	req.declarationEnd = req.declarationSourceEnd;
+	req.modifiersSourceStart = this.intStack[this.intPtr--];
+	req.modifiers |= this.intStack[this.intPtr--];
+	req.declarationSourceStart = this.intStack[this.intPtr--];
+	if (req.modifiersSourceStart >= 0) {
+		req.declarationSourceStart = req.modifiersSourceStart;
+	}
+	req.sourceEnd = reference.sourceEnd;
+	pushOnAstStack(req);
+
+	// recovery TBD
+
+	if (this.currentElement != null){
+		this.lastCheckPoint = req.declarationSourceEnd + 1;
+		this.currentElement = this.currentElement.add(req, 0);
+		this.lastIgnoredToken = -1;
+	}
+
+}
+
 protected void consumeSingleTypeImportDeclarationName() {
 	// SingleTypeImportDeclarationName ::= 'import' Name
 	/* push an ImportRef build from the last name
@@ -1098,6 +1270,20 @@
 	super.consumeStaticOnly();
 	pushOnElementStack(K_METHOD_DELIMITER);
 }
+private void adjustBracket(int token) {
+	switch (token) {
+		case TokenNameLPAREN :
+		case TokenNameLBRACE:
+		case TokenNameLBRACKET:
+			this.bracketDepth++;
+			break;
+		case TokenNameRBRACE:
+		case TokenNameRBRACKET:
+		case TokenNameRPAREN:
+			this.bracketDepth--;
+			break;
+	}
+}
 protected void consumeToken(int token) {
 	super.consumeToken(token);
 
@@ -1108,9 +1294,9 @@
 	// register message send selector only if inside a method or if looking at a field initializer
 	// and if the current token is an open parenthesis
 	if (isInsideMethod() || isInsideFieldInitialization() || isInsideAttributeValue() || isInsideEnumConstantnitialization()) {
+		adjustBracket(token);
 		switch (token) {
 			case TokenNameLPAREN :
-				this.bracketDepth++;
 				switch (this.previousToken) {
 					case TokenNameIdentifier:
 						this.pushOnElementStack(K_SELECTOR, this.identifierPtr);
@@ -1135,21 +1321,10 @@
 					popElement(K_LAMBDA_EXPRESSION_DELIMITER);
 					pushOnElementStack(K_LAMBDA_EXPRESSION_DELIMITER, BLOCK_BODY, this.previousObjectInfo);
 				}
-				this.bracketDepth++;
-				break;
-			case TokenNameLBRACKET:
-				this.bracketDepth++;
-				break;
-			case TokenNameRBRACE:
-				this.bracketDepth--;
-				break;
-			case TokenNameRBRACKET:
-				this.bracketDepth--;
-				break;
-			case TokenNameRPAREN:
-				this.bracketDepth--;
 				break;
 		}
+	} else if (isInsideModuleInfo()) { 
+		adjustBracket(token);
 	} else {
 		switch (token) {
 			case TokenNameRBRACE :
@@ -1218,7 +1393,11 @@
 		this.restartRecovery = true; // used to avoid branching back into the regular automaton
 	}
 }
+
+// TODO : Change to ExportsReference/PackageReference once we have the new compiler ast.node
+public abstract ImportReference createAssistPackageVisibilityReference(char[][] tokens, long[] positions);
 public abstract ImportReference createAssistImportReference(char[][] tokens, long[] positions, int mod);
+public abstract ModuleReference createAssistModuleReference(int index);
 //{ObjectTeams: added modifiers:
 public abstract ImportReference createAssistPackageReference(char[][] tokens, long[] positions, int modifiers);
 // SH}
@@ -1228,6 +1407,7 @@
 public abstract NameReference createSingleAssistNameReference(char[] assistName, long position);
 public abstract TypeReference createSingleAssistTypeReference(char[] assistName, long position);
 public abstract TypeReference createParameterizedSingleAssistTypeReference(TypeReference[] typeArguments, char[] assistName, long position);
+public abstract ModuleDeclaration createAssistModuleDeclaration(CompilationResult compilationResult, char[][] tokens, long[] positions);
 /*
  * Flush parser/scanner state regarding to code assist
  */
@@ -1660,6 +1840,21 @@
 	}
 	return false;
 }
+protected boolean isInsideModuleInfo(){
+	int i = this.elementPtr;
+	while(i > -1) {
+		switch (this.elementKindStack[i]) {
+			case K_TYPE_DELIMITER : 
+			case K_METHOD_DELIMITER :
+			case K_FIELD_INITIALIZER_DELIMITER : 
+				return false;
+			case K_MODULE_INFO_DELIMITER:
+				return true;
+		}
+		i--;
+	}
+	return false;
+}
 protected boolean isInsideMethod(){
 	int i = this.elementPtr;
 	while(i > -1) {
@@ -2289,6 +2484,12 @@
 			goForBlockStatementsopt();
 		} else {
 			prepareForHeaders();
+			if (this.referenceContext instanceof CompilationUnitDeclaration) {
+				CompilationUnitDeclaration unit = (CompilationUnitDeclaration) this.referenceContext;
+				if (unit.isModuleInfo()) {
+					pushOnElementStack(K_MODULE_INFO_DELIMITER);
+				}
+			}
 			goForHeaders();
 			this.diet = true; // passed this point, will not consider method bodies
 			this.dietInt = 0;
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Engine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Engine.java
index 187dabd..6d81ae0 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Engine.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Engine.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2013 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
@@ -131,18 +131,21 @@
 		} else {
 			result = new CompilationResult(sourceTypes[0].getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit);
 		}
+		LookupEnvironment environment = packageBinding.environment;
+		if (environment == null)
+			environment = this.lookupEnvironment;
 		CompilationUnitDeclaration unit =
 			SourceTypeConverter.buildCompilationUnit(
 				sourceTypes,//sourceTypes[0] is always toplevel here
 				SourceTypeConverter.FIELD_AND_METHOD // need field and methods
 				| SourceTypeConverter.MEMBER_TYPE, // need member types
 				// no need for field initialization
-				this.lookupEnvironment.problemReporter,
+				environment.problemReporter,
 				result);
 
 		if (unit != null) {
-			this.lookupEnvironment.buildTypeBindings(unit, accessRestriction);
-			this.lookupEnvironment.completeTypeBindings(unit, true);
+			environment.buildTypeBindings(unit, accessRestriction);
+			environment.completeTypeBindings(unit, true);
 		}
 //{ObjectTeams: cleanup:
 	  }
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Keywords.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Keywords.java
index 3f0923e..bce04d4 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Keywords.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/Keywords.java
@@ -15,7 +15,7 @@
 
 public interface Keywords {
 //{ObjectTeams: adapted count
-	int COUNT = 55;
+	int COUNT = 63;
 //carp}
 
 	char[] ABSTRACT = "abstract".toCharArray(); //$NON-NLS-1$
@@ -30,20 +30,25 @@
 	char[] ELSE = "else".toCharArray(); //$NON-NLS-1$
 	char[] ENUM = "enum".toCharArray(); //$NON-NLS-1$
 	char[] EXTENDS = "extends".toCharArray(); //$NON-NLS-1$
+	char[] EXPORTS = "exports".toCharArray(); //$NON-NLS-1$
 	char[] FINAL = "final".toCharArray(); //$NON-NLS-1$
 	char[] FINALLY = "finally".toCharArray(); //$NON-NLS-1$
 	char[] FOR = "for".toCharArray(); //$NON-NLS-1$
 	char[] IF = "if".toCharArray(); //$NON-NLS-1$
 	char[] IMPLEMENTS = "implements".toCharArray(); //$NON-NLS-1$
 	char[] IMPORT = "import".toCharArray(); //$NON-NLS-1$
+	char[] MODULE = "module".toCharArray(); //$NON-NLS-1$
 	char[] INSTANCEOF = "instanceof".toCharArray(); //$NON-NLS-1$
 	char[] INTERFACE = "interface".toCharArray(); //$NON-NLS-1$
 	char[] NATIVE = "native".toCharArray(); //$NON-NLS-1$
 	char[] NEW = "new".toCharArray(); //$NON-NLS-1$
+	char[] OPENS= "opens".toCharArray(); //$NON-NLS-1$
 	char[] PACKAGE = "package".toCharArray(); //$NON-NLS-1$
 	char[] PRIVATE = "private".toCharArray(); //$NON-NLS-1$
 	char[] PROTECTED = "protected".toCharArray(); //$NON-NLS-1$
+	char[] PROVIDES = "provides".toCharArray(); //$NON-NLS-1$
 	char[] PUBLIC = "public".toCharArray(); //$NON-NLS-1$
+	char[] REQUIRES = "requires".toCharArray(); //$NON-NLS-1$
 	char[] RETURN = "return".toCharArray(); //$NON-NLS-1$
 	char[] STATIC = "static".toCharArray(); //$NON-NLS-1$
 	char[] STRICTFP = "strictfp".toCharArray(); //$NON-NLS-1$
@@ -53,10 +58,13 @@
 	char[] THIS = "this".toCharArray(); //$NON-NLS-1$
 	char[] THROW = "throw".toCharArray(); //$NON-NLS-1$
 	char[] THROWS = "throws".toCharArray(); //$NON-NLS-1$
+	char[] TO = "to".toCharArray(); //$NON-NLS-1$
 	char[] TRANSIENT = "transient".toCharArray(); //$NON-NLS-1$
 	char[] TRY = "try".toCharArray(); //$NON-NLS-1$
+	char[] USES = "uses".toCharArray(); //$NON-NLS-1$
 	char[] VOLATILE = "volatile".toCharArray(); //$NON-NLS-1$
 	char[] WHILE = "while".toCharArray(); //$NON-NLS-1$
+	char[] WITH = "with".toCharArray(); //$NON-NLS-1$
 	char[] TRUE = "true".toCharArray(); //$NON-NLS-1$
 	char[] FALSE = "false".toCharArray(); //$NON-NLS-1$
 	char[] NULL = "null".toCharArray(); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnModuleDeclaration.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnModuleDeclaration.java
new file mode 100644
index 0000000..24c388d
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnModuleDeclaration.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 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.internal.codeassist.select;
+
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceModuleBinding;
+
+public class SelectionOnModuleDeclaration extends ModuleDeclaration {
+
+	public SelectionOnModuleDeclaration(CompilationResult compilationResult, char[][] tokens, long[] positions) {
+		super(compilationResult, tokens, positions);
+	}
+
+	@Override
+	public ModuleBinding setBinding(SourceModuleBinding sourceModuleBinding) {
+		super.setBinding(sourceModuleBinding);
+		throw new SelectionNodeFound(this.binding);
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnModuleReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnModuleReference.java
new file mode 100644
index 0000000..500f7c7
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnModuleReference.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 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.internal.codeassist.select;
+
+import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
+
+public class SelectionOnModuleReference extends ModuleReference {
+
+	public SelectionOnModuleReference(char[][] tokens, long[] sourcePositions) {
+		super(tokens, sourcePositions);
+	}
+
+	public ModuleBinding resolve(Scope scope) {
+		ModuleBinding resolvedBinding = super.resolve(scope);
+		if (resolvedBinding != null) {
+			throw new SelectionNodeFound(resolvedBinding);
+		} else {
+			throw new SelectionNodeFound();
+		}
+	}
+
+	public StringBuffer print(int tab, StringBuffer output) {
+		printIndent(tab, output).append("<SelectOnModuleReference:"); //$NON-NLS-1$
+		for (int i = 0; i < this.tokens.length; i++) {
+			if (i > 0) output.append('.');
+			output.append(this.tokens[i]);
+		}
+		return output.append('>');
+	}
+
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnPackageVisibilityReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnPackageVisibilityReference.java
new file mode 100644
index 0000000..a452b13
--- /dev/null
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnPackageVisibilityReference.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.jdt.internal.codeassist.select;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.ast.ImportReference;
+
+/*
+ * Selection node build by the parser in any case it was intending to
+ * reduce an export reference containing the assist identifier.
+ * e.g.
+ *
+ *	module myModule {
+ *  exports packageo[cursor];
+ *  }
+ *
+ *	module myModule {
+ *	---> <SelectionOnExport:packageo>
+ *  }
+ *
+ */
+public class SelectionOnPackageVisibilityReference extends ImportReference {
+
+	public SelectionOnPackageVisibilityReference(char[][] tokens, long[] positions) {
+		super(tokens, positions, false, 0);
+	}
+
+	public StringBuffer print(int indent, StringBuffer output) {
+
+		printIndent(indent, output).append("<SelectOnPackageVisibility:"); //$NON-NLS-1$
+		output.append(new String(CharOperation.concatWith(this.tokens, '.')));
+		return output.append('>');
+	}
+}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
index 3a66c56..7126c32 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
@@ -4,7 +4,7 @@
  * 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
  *     Fraunhofer FIRST - extended API and implementation
@@ -1291,9 +1291,17 @@
 protected SelectionParser createSnapShotParser() {
 	return new SelectionParser(this.problemReporter);
 }
+public ImportReference createAssistPackageVisibilityReference(char[][] tokens, long[] positions){
+	return new SelectionOnPackageVisibilityReference(tokens, positions);
+}
 public ImportReference createAssistImportReference(char[][] tokens, long[] positions, int mod){
 	return new SelectionOnImportReference(tokens, positions, mod);
 }
+@Override
+public ModuleDeclaration createAssistModuleDeclaration(CompilationResult compilationResult, char[][] tokens,
+		long[] positions) {
+	return new SelectionOnModuleDeclaration(compilationResult, tokens, positions);
+}
 //{ObjectTeams: modifiers added:
 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions, int modifiers){
 	return new SelectionOnPackageReference(tokens, positions, modifiers);
@@ -1820,4 +1828,17 @@
 	s = s + "}\n"; //$NON-NLS-1$
 	return s + super.toString();
 }
+@Override
+public ModuleReference createAssistModuleReference(int index) {
+	// ignore index, all segments of the module name are part of a single identifier.
+	/* retrieve identifiers subset and whole positions, the assist node positions
+	should include the entire replaced source. */
+	int length;
+	char[][] tokens = new char[length = this.identifierLengthStack[this.identifierLengthPtr--]][];
+	this.identifierPtr -= length;
+	long[] positions = new long[length];
+	System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length);
+	System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length);
+	return new SelectionOnModuleReference(tokens, positions);
+}
 }
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java
index 7fd6eb2..48f2725 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java
@@ -51,7 +51,7 @@
 	 * @param creatingProblemType <CODE>boolean</CODE>
 	 */
 	this.referenceBinding = aType;
-	initByteArrays();
+	initByteArrays(aType.methods().length + aType.fields().length);
 	// generate the magic numbers inside the header
 	this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 24);
 	this.header[this.headerOffset++] = (byte) (0xCAFEBABEL >> 16);
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetEnvironment.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetEnvironment.java
index 24b0b5e..56d9142 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetEnvironment.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetEnvironment.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetScope.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetScope.java
index 0a22d81..34c4153 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetScope.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetScope.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -449,7 +449,7 @@
 		PackageBinding packageBinding = (PackageBinding) binding;
 
 		while (currentIndex < length) {
-			binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]);
+			binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++], null);
 			invocationSite.setFieldIndex(currentIndex);
  			if (binding == null) {
 	 			if (currentIndex == length) // must be a type if its the last name, otherwise we have no idea if its a package or type
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetSkeleton.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetSkeleton.java
index 950f481..3c8baae 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetSkeleton.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetSkeleton.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
@@ -199,6 +199,11 @@
 	return walker;
 }
 @Override
+public char[] getModule() {
+	// TODO Java 9 Auto-generated method stub
+	return null;
+}
+@Override
 public ExternalAnnotationStatus getExternalAnnotationStatus() {
 	return ExternalAnnotationStatus.NOT_EEA_CONFIGURED;
 }
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetToCuMapper.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetToCuMapper.java
index fe2215b..588335a 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetToCuMapper.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetToCuMapper.java
@@ -321,6 +321,10 @@
 		public void acceptMethodTypeParameter(char[] declaringTypePackageName, char[] declaringTypeName, char[] selector, int selectorStart, int selectorEnd, char[] typeParameterName,boolean isDeclaration, int start, int end) {
 			originalRequestor.acceptMethodTypeParameter(declaringTypePackageName, declaringTypeName, selector, selectorStart, selectorEnd, typeParameterName, isDeclaration, start, end);
 		}
+		@Override
+		public void acceptModule(char[] moduleName, char[] uniqueKey, int start, int end) {
+			originalRequestor.acceptModule(moduleName, uniqueKey, start, end);
+		}
 	};
 }
 }
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/EvaluationContext.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/EvaluationContext.java
index e4587af..811306a 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/EvaluationContext.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/EvaluationContext.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -160,6 +160,11 @@
 		public boolean ignoreOptionalProblems() {
 			return false;
 		}
+		@Override
+		public char[] getModuleName() {
+			// TODO Java 9 Auto-generated method stub
+			return null;
+		}
 	};
 	
 	CompletionEngine engine = new CompletionEngine(environment, mapper.getCompletionRequestor(requestor), options, project, owner, monitor);
@@ -607,6 +612,10 @@
 		public boolean ignoreOptionalProblems() {
 			return false;
 		}
+		@Override
+		public char[] getModuleName() {
+			return null;
+		}
 	};
 	SelectionEngine engine = new SelectionEngine(environment, mapper.getSelectionRequestor(requestor), options, owner);
 	engine.select(sourceUnit, mapper.startPosOffset + selectionSourceStart, mapper.startPosOffset + selectionSourceEnd);
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/Evaluator.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/Evaluator.java
index 6b95315..56bb770 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/Evaluator.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/Evaluator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -145,6 +145,11 @@
 		public boolean ignoreOptionalProblems() {
 			return false;
 		}
+		@Override
+		public char[] getModuleName() {
+			// TODO Java 9 Auto-generated method stub
+			return null;
+		}
 	}});
 	if (compilerRequestor.hasErrors) {
 		return null;
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatter.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatter.java
index 9329638..1efe942 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatter.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatter.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -40,6 +40,8 @@
 	/**
 	 * Kind used to format an expression
 	 * <p>
+	 * This kind is not applicable to module descriptions.
+	 * </p><p>
 	 * Note that using this constant, the comments encountered while formatting
 	 * the expression may be shifted to match the correct indentation but are not
 	 * formatted.
@@ -54,6 +56,8 @@
 	/**
 	 * Kind used to format a set of statements
 	 * <p>
+	 * This kind is not applicable to module descriptions.
+	 * </p><p>
 	 * Note that using this constant, the comments encountered while formatting
 	 * the statements may be shifted to match the correct indentation but are not
 	 * formatted.
@@ -68,6 +72,8 @@
 	/**
 	 * Kind used to format a set of class body declarations
 	 * <p>
+	 * This kind is not applicable to module descriptions.
+	 * </p><p>
 	 * Note that using this constant, the comments encountered while formatting
 	 * the body declarations may be shifted to match the correct indentation but
 	 * are not formatted.
@@ -82,8 +88,9 @@
 	/**
 	 * Kind used to format a compilation unit
 	 * <p>
-	 * Note that using this constant, the comments are only indented while
-	 * formatting the compilation unit.
+	 * <u>Note:</u> <b>since 3.14</b>, if the formatted compilation unit is a
+	 * module description (i.e. it's named module-info.java), the
+	 * {@link #K_MODULE_INFO} kind must be used instead.
 	 * </p><p>
 	 * <b>Since 3.4</b>, if the corresponding comment option is set to
 	 * <code>true</code> then it is also possible to format the comments on the fly
@@ -123,6 +130,17 @@
 	public static final int K_JAVA_DOC = 0x40;
 
 	/**
+	 * Kind used to format a module description (a module-info.java file).
+	 * <p>
+	 * If the corresponding comment option is set to <code>true</code> then it is
+	 * also possible to format the comments on the fly by adding the
+	 * {@link #F_INCLUDE_COMMENTS} flag to this kind of format.
+	 * </p>
+	 * @since 3.14
+	 */
+	public static final int K_MODULE_INFO = 0x80;
+
+	/**
 	 * Flag used to include the comments during the formatting of the code
 	 * snippet.
 	 * <p>
@@ -133,6 +151,7 @@
 	 * 		<li>{@link #K_CLASS_BODY_DECLARATIONS} <i>(since 3.6)</i></li>
 	 * 		<li>{@link #K_EXPRESSION} <i>(since 3.6)</i></li>
 	 * 		<li>{@link #K_STATEMENTS} <i>(since 3.6)</i></li>
+	 * 		<li>{@link #K_MODULE_INFO} <i>(since 3.14)</i></li>
 	 * </ul>
 	 * </p><p>
 	 * Note also that it has an effect only when one or several format comments
@@ -215,17 +234,17 @@
 	 * 	<li>{@link #K_EXPRESSION}</li>
 	 * 	<li>{@link #K_STATEMENTS}</li>
 	 * 	<li>{@link #K_CLASS_BODY_DECLARATIONS}</li>
-	 * 	<li>{@link #K_COMPILATION_UNIT}<br>
-	 * 		<b>Since 3.4</b>, the comments can be formatted on the fly while
-	 * 		using this kind of code snippet<br>
-	 * 		(see {@link #F_INCLUDE_COMMENTS} for more detailed explanation on
-	 * 		this flag)
-	 * 	</li>
+	 * 	<li>{@link #K_COMPILATION_UNIT}</li>
+	 * 	<li>{@link #K_MODULE_INFO}</li>
 	 * 	<li>{@link #K_UNKNOWN}</li>
 	 * 	<li>{@link #K_SINGLE_LINE_COMMENT}</li>
 	 * 	<li>{@link #K_MULTI_LINE_COMMENT}</li>
 	 * 	<li>{@link #K_JAVA_DOC}</li>
 	 * </ul>
+	 * <b>Since 3.4</b> for {@link #K_COMPILATION_UNIT} and <b>since 3.6</b> for other
+	 * kinds unrelated to comments, the {@link #F_INCLUDE_COMMENTS} flag can be
+	 * used to format comments on the fly (see the flag documentation for more
+	 * detailed explanation).
 	 * @param source the source to format
 	 * @param offset the given offset to start recording the edits (inclusive).
 	 * @param length the given length to stop recording the edits (exclusive).
@@ -258,17 +277,17 @@
 	 * 	<li>{@link #K_EXPRESSION}</li>
 	 * 	<li>{@link #K_STATEMENTS}</li>
 	 * 	<li>{@link #K_CLASS_BODY_DECLARATIONS}</li>
-	 * 	<li>{@link #K_COMPILATION_UNIT}<br>
-	 * 		<b>Since 3.4</b>, the comments can be formatted on the fly while
-	 * 		using this kind of code snippet<br>
-	 * 		(see {@link #F_INCLUDE_COMMENTS} for more detailed explanation on
-	 * 		this flag)
-	 * 	</li>
+	 * 	<li>{@link #K_COMPILATION_UNIT}</li>
+	 * 	<li>{@link #K_MODULE_INFO}</li>
 	 * 	<li>{@link #K_UNKNOWN}</li>
 	 * 	<li>{@link #K_SINGLE_LINE_COMMENT}</li>
 	 * 	<li>{@link #K_MULTI_LINE_COMMENT}</li>
 	 * 	<li>{@link #K_JAVA_DOC}</li>
 	 * </ul>
+	 * <b>Since 3.4</b> for {@link #K_COMPILATION_UNIT} and <b>since 3.6</b> for other
+	 * kinds unrelated to comments, the {@link #F_INCLUDE_COMMENTS} flag can be
+	 * used to format comments on the fly (see the flag documentation for more
+	 * detailed explanation).
 	 * @param source the source to format
 	 * @param regions a set of regions in source to format
 	 * @param indentationLevel the initial indentation level, used 
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatterApplication.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatterApplication.java
index a89628f..ab514e1 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatterApplication.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatterApplication.java
@@ -27,6 +27,7 @@
 import org.eclipse.equinox.app.IApplication;
 import org.eclipse.equinox.app.IApplicationContext;
 import org.eclipse.jdt.core.ToolFactory;
+import org.eclipse.jdt.internal.compiler.env.IModule;
 import org.eclipse.jdt.internal.core.util.Util;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.Document;
@@ -221,7 +222,9 @@
 			String contents = new String(org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(file, null));
 			// format the file (the meat and potatoes)
 			doc.set(contents);
-			TextEdit edit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, contents, 0, contents.length(), 0, null);
+			int kind = (file.getName().equals(IModule.MODULE_INFO_JAVA)? CodeFormatter.K_MODULE_INFO
+					: CodeFormatter.K_COMPILATION_UNIT) | CodeFormatter.F_INCLUDE_COMMENTS;
+			TextEdit edit = codeFormatter.format(kind, contents, 0, contents.length(), 0, null);
 			if (edit != null) {
 				edit.apply(doc);
 			} else {
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
index e182aea..2194125 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
@@ -236,6 +236,17 @@
 	public static final String FORMATTER_ALIGNMENT_FOR_METHOD_DECLARATION = JavaCore.PLUGIN_ID + ".formatter.alignment_for_method_declaration";	 //$NON-NLS-1$
 	/**
 	 * <pre>
+	 * FORMATTER / Option for alignment of module statements
+	 *     - option id:         "org.eclipse.jdt.core.formatter.alignment_for_module_statements"
+	 *     - possible values:   values returned by <code>createAlignmentValue(boolean, int, int)</code> call
+	 *     - default:           createAlignmentValue(false, WRAP_COMPACT, INDENT_DEFAULT)
+	 * </pre>
+	 * @see #createAlignmentValue(boolean, int, int)
+	 * @since 3.14
+	 */
+	public static final String FORMATTER_ALIGNMENT_FOR_MODULE_STATEMENTS = JavaCore.PLUGIN_ID + ".formatter.alignment_for_module_statements";	 //$NON-NLS-1$
+	/**
+	 * <pre>
 	 * FORMATTER / Option for alignment of multiple fields
 	 *     - option id:         "org.eclipse.jdt.core.formatter.alignment_for_multiple_fields"
 	 *     - possible values:   values returned by <code>createAlignmentValue(boolean, int, int)</code> call
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java
index 8200ee8..b196404 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java
@@ -60,10 +60,10 @@
 		String breakBeforeTags = "(dd|dt|li|td|th|h1|h2|h3|h4|h5|h6|q)"; //$NON-NLS-1$
 		String breakAfterTags = "(br)"; //$NON-NLS-1$
 		String noFormatTags = "(code|tt)"; //$NON-NLS-1$
-		String otherTags = "([^<>&&\\S]++)"; //$NON-NLS-1$
+		String otherTags = "([\\S&&[^<>]]++)"; //$NON-NLS-1$
 		String ws = "(?>[ \\t]++|[\\r\\n]++[ \\t]*+\\*?)"; // whitespace or line break with optional asterisk //$NON-NLS-1$
-		String attributeValue = "(?>\"[^\"]*\")|(?>\'[^\']*\')|[^/>\"\'&&\\S]++"; //$NON-NLS-1$
-		String attribute = "(?>" + ws + "+[^=&&\\S]+" + ws + "*(=)" + ws + "*(?>" + attributeValue  + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+		String attributeValue = "(?>\"[^\"]*\")|(?>\'[^\']*\')|[\\S&&[^/>\"\']]++"; //$NON-NLS-1$
+		String attribute = "(?>" + ws + "+[\\S&&[^=]]+" + ws + "*(=)" + ws + "*(?>" + attributeValue  + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
 		HTML_TAG_PATTERN = Pattern.compile("<(/)?+(?:" //$NON-NLS-1$
 				+ formatCodeTags + '|' + separateLineTags + '|' + breakBeforeTags + '|' + breakAfterTags + '|' + noFormatTags + '|' + otherTags + ')'
 				+ "(" + attribute + "*)" + ws + "*/?>", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
index 509c626..7bdfffb 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -26,10 +26,15 @@
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.ICompilationUnit;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.core.compiler.InvalidInputException;
@@ -43,8 +48,10 @@
 import org.eclipse.jdt.core.formatter.CodeFormatter;
 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
 import org.eclipse.jdt.internal.compiler.parser.Scanner;
 import org.eclipse.jdt.internal.compiler.util.Util;
+import org.eclipse.jdt.internal.core.PackageFragment;
 import org.eclipse.jdt.internal.formatter.linewrap.CommentWrapExecutor;
 import org.eclipse.jdt.internal.formatter.linewrap.WrapPreparator;
 import org.eclipse.jface.text.IRegion;
@@ -72,8 +79,18 @@
 // SH}
 		| K_CLASS_BODY_DECLARATIONS
 		| K_COMPILATION_UNIT
+		| K_MODULE_INFO
 		| K_COMMENTS_MASK;
 
+	private static final Map<Integer, Integer> FORMAT_TO_PARSER_KIND = new HashMap<>();
+	static {
+		FORMAT_TO_PARSER_KIND.put(K_COMPILATION_UNIT, ASTParser.K_COMPILATION_UNIT);
+		FORMAT_TO_PARSER_KIND.put(K_MODULE_INFO, ASTParser.K_COMPILATION_UNIT);
+		FORMAT_TO_PARSER_KIND.put(K_CLASS_BODY_DECLARATIONS, ASTParser.K_CLASS_BODY_DECLARATIONS);
+		FORMAT_TO_PARSER_KIND.put(K_STATEMENTS, ASTParser.K_STATEMENTS);
+		FORMAT_TO_PARSER_KIND.put(K_EXPRESSION, ASTParser.K_EXPRESSION);
+	}
+
 	private DefaultCodeFormatterOptions originalOptions;
 	private DefaultCodeFormatterOptions workingOptions;
 
@@ -81,7 +98,7 @@
 	private String sourceLevel;
 
 	private String sourceString;
-	private char[] sourceArray;
+	char[] sourceArray;
 	private List<IRegion> formatRegions;
 
 	private ASTNode astRoot;
@@ -181,7 +198,7 @@
 		return result;
 	}
 
-	private boolean init(String source) {
+	private boolean init(String source, int kind) {
 
 		// this is convenient for debugging (see Token.toString())
 		// Token.source = source;
@@ -191,7 +208,7 @@
 		this.tokens.clear();
 		this.tokenManager = new TokenManager(this.tokens, source, this.workingOptions);
 
-		tokenizeSource();
+		tokenizeSource(kind);
 		return !this.tokens.isEmpty();
 	}
 
@@ -201,7 +218,7 @@
 	}
 
 	private List<Token> prepareFormattedCode(String source, int kind) {
-		if (!init(source))
+		if (!init(source, kind))
 			return null;
 
 		this.astRoot = parseSourceCode(kind);
@@ -233,7 +250,7 @@
 
 	private TextEdit formatComments(String source, int kind) {
 		MultiTextEdit result = new MultiTextEdit();
-		if (!init(source))
+		if (!init(source, kind))
 			return result;
 
 		CommentsPreparator commentsPreparator = new CommentsPreparator(this.tokenManager, this.workingOptions,
@@ -241,12 +258,9 @@
 		CommentWrapExecutor commentWrapper = new CommentWrapExecutor(this.tokenManager, this.workingOptions);
 		switch (kind) {
 			case K_JAVA_DOC:
-				ASTParser parser = ASTParser.newParser(AST.JLS8);
 				for (Token token : this.tokens) {
 					if (token.tokenType == TokenNameCOMMENT_JAVADOC) {
-						parser.setSourceRange(token.originalStart, token.countChars());
-						CompilationUnit cu = (CompilationUnit) parseSourceCode(parser, ASTParser.K_COMPILATION_UNIT,
-								true);
+						CompilationUnit cu = (CompilationUnit) parseSourceCode(ASTParser.K_COMPILATION_UNIT);
 						Javadoc javadoc = (Javadoc) cu.getCommentList().get(0);
 						javadoc.accept(commentsPreparator);
 						int startPosition = this.tokenManager.findSourcePositionInLine(token.originalStart);
@@ -301,7 +315,45 @@
 	}
 
 	private ASTNode parseSourceCode(int kind) {
-		ASTParser parser = ASTParser.newParser(AST.JLS8);
+		kind = kind & K_MASK;
+		if (kind != K_UNKNOWN) {
+			ASTNode astNode = createParser(kind).createAST(null);
+			if (kind == K_COMPILATION_UNIT || kind == K_MODULE_INFO)
+				return astNode;
+			return hasErrors(astNode) ? null : astNode;
+		}
+
+		int[] kindsToTry = { K_COMPILATION_UNIT, K_EXPRESSION, K_CLASS_BODY_DECLARATIONS, K_STATEMENTS, K_MODULE_INFO };
+		for (int kindToTry : kindsToTry) {
+			ASTNode astNode = createParser(kindToTry).createAST(null);
+			if (!hasErrors(astNode)) {
+				if (kindToTry == K_MODULE_INFO) 
+					tokenizeSource(kindToTry); // run scanner again to get module specific tokens
+				return astNode;
+			}
+		}
+		return null;
+	}
+
+	private ASTParser createParser(int kind) {
+		ASTParser parser = ASTParser.newParser(AST.JLS9);
+
+		if (kind == K_MODULE_INFO) {
+			Path fakeModuleInfoPath = new Path("project/" + TypeConstants.MODULE_INFO_FILE_NAME_STRING); //$NON-NLS-1$
+			IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(fakeModuleInfoPath);
+			ICompilationUnit unit = JavaCore.createCompilationUnitFrom(file);
+			parser.setSource(new org.eclipse.jdt.internal.core.CompilationUnit((PackageFragment) unit.getParent(),
+					unit.getElementName(), unit.getOwner()) {
+				@Override
+				public char[] getContents() {
+					return DefaultCodeFormatter.this.sourceArray;
+				}
+			});
+		} else {
+			parser.setSource(this.sourceArray);
+		}
+		parser.setKind(FORMAT_TO_PARSER_KIND.get(kind));
+
 		Map<String, String> parserOptions = JavaCore.getOptions();
 		parserOptions.put(CompilerOptions.OPTION_Source, this.sourceLevel);
 		parserOptions.put(CompilerOptions.OPTION_DocCommentSupport, CompilerOptions.ENABLED);
@@ -310,54 +362,19 @@
 		parserOptions.put(CompilerOptions.OPTION_AllowScopedKeywords, this.workingOptions.scopedKeywords ? CompilerOptions.ENABLED : CompilerOptions.DISABLED);
 // SH}
 		parser.setCompilerOptions(parserOptions);
-
-		switch (kind & K_MASK) {
-			case K_COMPILATION_UNIT:
-				return parseSourceCode(parser, ASTParser.K_COMPILATION_UNIT, true);
-			case K_CLASS_BODY_DECLARATIONS:
-				return parseSourceCode(parser, ASTParser.K_CLASS_BODY_DECLARATIONS, false);
-			case K_STATEMENTS:
-				return parseSourceCode(parser, ASTParser.K_STATEMENTS, false);
-			case K_EXPRESSION:
-				return parseSourceCode(parser, ASTParser.K_EXPRESSION, false);
-//{ObjectTeams: own kind:
-			case K_PARAMETER_MAPPING:
-				return parseSourceCode(parser, ASTParser.K_PARAMETER_MAPPING, false);
-// SH}
-			case K_UNKNOWN:
-				int[] parserModes = { ASTParser.K_COMPILATION_UNIT, ASTParser.K_EXPRESSION,
-						ASTParser.K_CLASS_BODY_DECLARATIONS, ASTParser.K_STATEMENTS };
-				for (int parserMode : parserModes) {
-					ASTNode astNode = parseSourceCode(parser, parserMode, false);
-					if (astNode != null)
-						return astNode;
-					parser.setCompilerOptions(parserOptions); // parser loses compiler options after every use
-				}
-				return null;
-			default:
-				throw new IllegalArgumentException();
-		}
+		return parser;
 	}
 
-	private ASTNode parseSourceCode(ASTParser parser, int parserMode, boolean ignoreErrors) {
-		parser.setKind(parserMode);
-		parser.setSource(this.sourceArray);
-		ASTNode astNode = parser.createAST(null);
-		if (ignoreErrors)
-			return astNode;
-
-		boolean hasErrors = false;
+	private boolean hasErrors(ASTNode astNode) {
 		CompilationUnit root = (CompilationUnit) astNode.getRoot();
 		for (IProblem problem : root.getProblems()) {
-			if (problem.isError()) {
-				hasErrors = true;
-				break;
-			}
+			if (problem.isError())
+				return true;
 		}
-		return hasErrors ? null : astNode;
+		return false;
 	}
 
-	private void tokenizeSource() {
+	private void tokenizeSource(int kind) {
 		this.tokens.clear();
 		Scanner scanner = new Scanner(true, false, false/* nls */, CompilerOptions.versionToJdkLevel(this.sourceLevel),
 				null/* taskTags */, null/* taskPriorities */, false/* taskCaseSensitive */);
@@ -366,6 +383,7 @@
 		scanner.parseOTJonly = this.isOTJCode;
 // SH}
 		scanner.setSource(this.sourceArray);
+		scanner.fakeInModule = (kind & K_MODULE_INFO) != 0;
 		while (true) {
 			try {
 				int tokenType = scanner.getNextToken();
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
index 66e577a..7d56326 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
@@ -132,6 +132,7 @@
 	public int alignment_for_expressions_in_array_initializer;
 	public int alignment_for_expressions_in_for_loop_header;
 	public int alignment_for_method_declaration;
+	public int alignment_for_module_statements;
 	// TODO following option cannot be set in preferences dialog (but it's used by old.CodeFormatter)
 	public int alignment_for_multiple_fields;
 	public int alignment_for_parameterized_type_references;
@@ -468,6 +469,7 @@
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER, getAlignment(this.alignment_for_expressions_in_array_initializer));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_FOR_LOOP_HEADER, getAlignment(this.alignment_for_expressions_in_for_loop_header));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_METHOD_DECLARATION, getAlignment(this.alignment_for_method_declaration));
+		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_MODULE_STATEMENTS, getAlignment(this.alignment_for_module_statements));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_MULTIPLE_FIELDS, getAlignment(this.alignment_for_multiple_fields));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERIZED_TYPE_REFERENCES, getAlignment(this.alignment_for_parameterized_type_references));
 		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_CONSTRUCTOR_DECLARATION, getAlignment(this.alignment_for_parameters_in_constructor_declaration));
@@ -914,6 +916,10 @@
 				this.alignment_for_method_declaration = Alignment.M_COMPACT_SPLIT;
 			}
 		}
+		final Object alignmentForModuleStatementsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_MODULE_STATEMENTS);
+		if (alignmentForModuleStatementsOption != null)
+			this.alignment_for_module_statements = toInt(alignmentForModuleStatementsOption, Alignment.M_COMPACT_SPLIT);
+
 		final Object alignmentForMultipleFieldsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_MULTIPLE_FIELDS);
 		if (alignmentForMultipleFieldsOption != null) {
 			try {
@@ -2447,6 +2453,7 @@
 		this.alignment_for_expressions_in_array_initializer = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_expressions_in_for_loop_header = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_method_declaration = Alignment.M_NO_ALIGNMENT;
+		this.alignment_for_module_statements = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_multiple_fields = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_parameterized_type_references = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_parameters_in_constructor_declaration = Alignment.M_COMPACT_SPLIT;
@@ -2761,6 +2768,7 @@
 		this.alignment_for_expressions_in_array_initializer = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_expressions_in_for_loop_header = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_method_declaration = Alignment.M_NO_ALIGNMENT;
+		this.alignment_for_module_statements = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_multiple_fields = Alignment.M_COMPACT_SPLIT;
 		this.alignment_for_parameterized_type_references = Alignment.M_NO_ALIGNMENT;
 		this.alignment_for_parameters_in_constructor_declaration = Alignment.M_COMPACT_SPLIT;
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java
index 32709b0..4d01ea7 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java
@@ -65,6 +65,8 @@
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.ModuleDeclaration;
+import org.eclipse.jdt.core.dom.ModuleDirective;
 import org.eclipse.jdt.core.dom.NormalAnnotation;
 import org.eclipse.jdt.core.dom.PackageDeclaration;
 import org.eclipse.jdt.core.dom.ReturnStatement;
@@ -735,6 +737,28 @@
 	}
 // SH}
 
+	@Override
+	public boolean visit(ModuleDeclaration node) {
+		// using settings for type declaration and fields for now, add new settings if necessary
+		breakLineBefore(node);
+		handleBracedCode(node, node.getName(), this.options.brace_position_for_type_declaration,
+				this.options.indent_body_declarations_compare_to_type_header,
+				this.options.insert_new_line_in_empty_type_declaration);
+
+		List<ModuleDirective> statements = node.moduleStatements();
+		ModuleDirective previous = null;
+		for (ModuleDirective statement : statements) {
+			int blankLines = previous == null ? this.options.blank_lines_before_first_class_body_declaration
+					: previous.getClass().equals(statement.getClass()) ? this.options.blank_lines_before_field
+							: this.options.blank_lines_before_new_chunk;
+			putBlankLinesBefore(statement, blankLines);
+			previous = statement;
+		}
+
+		this.declarationModifierVisited = false;
+		return true;
+	}
+
 	private void breakLineBefore(ASTNode node) {
 		this.tm.firstTokenIn(node, -1).breakBefore();
 	}
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java
index a45c484..02e1060 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java
@@ -43,6 +43,7 @@
 import org.eclipse.jdt.core.dom.EnhancedForStatement;
 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
 import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.ExportsDirective;
 import org.eclipse.jdt.core.dom.Expression;
 import org.eclipse.jdt.core.dom.ExpressionMethodReference;
 import org.eclipse.jdt.core.dom.ExpressionStatement;
@@ -61,7 +62,10 @@
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.MethodSpec;
+import org.eclipse.jdt.core.dom.ModuleDeclaration;
+import org.eclipse.jdt.core.dom.Name;
 import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.OpensDirective;
 import org.eclipse.jdt.core.dom.PackageDeclaration;
 import org.eclipse.jdt.core.dom.ParameterMapping;
 import org.eclipse.jdt.core.dom.ParameterizedType;
@@ -69,6 +73,7 @@
 import org.eclipse.jdt.core.dom.PostfixExpression;
 import org.eclipse.jdt.core.dom.PrefixExpression;
 import org.eclipse.jdt.core.dom.RoleTypeDeclaration;
+import org.eclipse.jdt.core.dom.ProvidesDirective;
 import org.eclipse.jdt.core.dom.PrefixExpression.Operator;
 import org.eclipse.jdt.core.dom.ReturnStatement;
 import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
@@ -371,7 +376,7 @@
 
 	@Override
 	public boolean visit(TryStatement node) {
-		List<VariableDeclarationExpression> resources = node.resources();
+		List<Expression> resources = node.resources();
 		if (!resources.isEmpty()) {
 			handleToken(node, TokenNameLPAREN, this.options.insert_space_before_opening_paren_in_try,
 					this.options.insert_space_after_opening_paren_in_try);
@@ -1021,6 +1026,37 @@
 		return true;
 	}
 
+	@Override
+	public boolean visit(ModuleDeclaration node) {
+		handleToken(node.getName(), TokenNameLBRACE,
+				this.options.insert_space_before_opening_brace_in_type_declaration, false);
+		return true;
+	}
+
+	@Override
+	public boolean visit(ExportsDirective node) {
+		handleModuleStatementCommas(node.modules());
+		return true;
+	}
+	
+	@Override
+	public boolean visit(OpensDirective node) {
+		handleModuleStatementCommas(node.modules());
+		return true;
+	}
+
+	@Override
+	public boolean visit(ProvidesDirective node) {
+		handleModuleStatementCommas(node.implementations());
+		return true;
+	}
+
+	private void handleModuleStatementCommas(List<Name> names) {
+		// using settings for fields for now, add new settings if necessary
+		handleCommas(names, this.options.insert_space_before_comma_in_multiple_field_declarations,
+				this.options.insert_space_after_comma_in_multiple_field_declarations);
+	}
+
 	private void handleCommas(List<? extends ASTNode> nodes, boolean spaceBefore, boolean spaceAfter) {
 		if (spaceBefore || spaceAfter) {
 			for (int i = 1; i < nodes.size(); i++) {
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
index 3f98b11..a2637f8 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
@@ -35,6 +35,8 @@
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamesuper;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethis;
 import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethrows;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameto;
+import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamewith;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -57,6 +59,7 @@
 import org.eclipse.jdt.core.dom.CreationReference;
 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
 import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.ExportsDirective;
 import org.eclipse.jdt.core.dom.Expression;
 import org.eclipse.jdt.core.dom.ExpressionMethodReference;
 import org.eclipse.jdt.core.dom.FieldAccess;
@@ -70,8 +73,11 @@
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.MethodSpec;
+import org.eclipse.jdt.core.dom.Name;
 import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.OpensDirective;
 import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.ProvidesDirective;
 import org.eclipse.jdt.core.dom.QualifiedName;
 import org.eclipse.jdt.core.dom.RoleTypeDeclaration;
 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
@@ -814,6 +820,34 @@
 	// TODO(SH): smart wrapping of long method mappings
 // SH}
 
+	@Override
+	public boolean visit(ExportsDirective node) {
+		handleModuleStatement(node.modules(), TokenNameto);
+		return true;
+	}
+
+	@Override
+	public boolean visit(OpensDirective node) {
+		handleModuleStatement(node.modules(), TokenNameto);
+		return true;
+	}
+
+	@Override
+	public boolean visit(ProvidesDirective node) {
+		handleModuleStatement(node.implementations(), TokenNamewith);
+		return true;
+	}
+
+	private void handleModuleStatement(List<Name> names, int joiningTokenType) {
+		if (names.isEmpty())
+			return;
+		int joiningTokenIndex = this.tm.firstIndexBefore(names.get(0), joiningTokenType);
+		this.wrapParentIndex = this.tm.firstIndexBefore(names.get(0), TokenNameIdentifier);
+		this.wrapIndexes.add(joiningTokenIndex);
+		prepareElementsList(names, TokenNameCOMMA, -1);
+		handleWrap(this.options.alignment_for_module_statements, PREFERRED);
+	}
+
 	/**
 	 * Makes sure all new lines within given node will have wrap policy so that
 	 * wrap executor will fix their indentation if necessary.
@@ -1065,7 +1099,7 @@
 		int endingBreaks = getLineBreaksToPreserve(last, null, false);
 		if (endingBreaks > 0) {
 			last.putLineBreaksAfter(endingBreaks);
-		} else if ((this.kind & CodeFormatter.K_COMPILATION_UNIT) != 0
+		} else if ((this.kind & (CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.K_MODULE_INFO)) != 0
 				&& this.options.insert_new_line_at_end_of_file_if_missing) {
 			last.breakAfter();
 		}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java
index 1c5ccc2..1946d36 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -129,6 +129,12 @@
 	 */
 	int INTERFACE_AND_ANNOTATION= 11;
 
+	/**
+	 * The searched element is a module.
+	 * @since 3.14
+	 * @category searchFor
+	 */
+	int MODULE= 12;
 	/* Nature of match */
 
 	/**
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/ModuleDeclarationMatch.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/ModuleDeclarationMatch.java
new file mode 100644
index 0000000..dbfe616
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/ModuleDeclarationMatch.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.search;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IJavaElement;
+
+/**
+ * A Java search match that represents a module declaration.
+ * The element is an <code>IType</code>.
+ * <p>
+ * This class is intended to be instantiated and subclassed by clients.
+ * </p>
+ *
+ * @since 3.13
+ */
+public class ModuleDeclarationMatch extends SearchMatch {
+
+	/**
+	 * Creates a new type declaration match.
+	 *
+	 * @param element the module declaration
+	 * @param accuracy one of A_ACCURATE or A_INACCURATE
+	 * @param offset the offset the match starts at, or -1 if unknown
+	 * @param length the length of the match, or -1 if unknown
+	 * @param participant the search participant that created the match
+	 * @param resource the resource of the element
+	 */
+	public ModuleDeclarationMatch(IJavaElement element, int accuracy, int offset, int length, SearchParticipant participant, IResource resource) {
+		super(element, accuracy, offset, length, participant, resource);
+	}
+
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/ModuleReferenceMatch.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/ModuleReferenceMatch.java
new file mode 100644
index 0000000..e1b6f56
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/ModuleReferenceMatch.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.search;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IJavaElement;
+
+/**
+ * A Java search match that represents a module reference.
+ * The element is the innermost enclosing member (mostly module declaration) that references this module reference.
+ * <p>
+ * This class is intended to be instantiated and subclassed by clients.
+ * </p>
+ * @since 3.14
+ */
+public class ModuleReferenceMatch extends ReferenceMatch {
+
+	/**
+	 * Creates a new module reference match.
+	 *
+	 * @param enclosingElement the inner-most enclosing member that references this module reference
+	 * @param accuracy one of {@link #A_ACCURATE} or {@link #A_INACCURATE}
+	 * @param offset the offset the match starts at, or -1 if unknown
+	 * @param length the length of the match, or -1 if unknown
+	 * @param insideDocComment <code>true</code> if this search match is inside a doc
+	 * 				comment, and <code>false</code> otherwise
+	 * @param participant the search participant that created the match
+	 * @param resource the resource of the element
+	 */
+	public ModuleReferenceMatch(IJavaElement enclosingElement, int accuracy, int offset, int length,
+			boolean insideDocComment, SearchParticipant participant, IResource resource) {
+		super(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource);
+	}
+
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java
index 2700951..958a621 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -1377,6 +1377,10 @@
 	}
 }
 
+private static SearchPattern createModulePattern(String patternString, int limitTo, int matchRule) {
+	return new ModulePattern(patternString.toCharArray(), limitTo, matchRule);
+}
+
 /**
  * Returns a search pattern that combines the given two patterns into an
  * "or" pattern. The search result will match either the left pattern or the
@@ -1644,6 +1648,8 @@
 			return createFieldPattern(stringPattern, limitTo, matchRule);
 		case IJavaSearchConstants.PACKAGE:
 			return createPackagePattern(stringPattern, limitTo, matchRule);
+		case IJavaSearchConstants.MODULE :
+			return createModulePattern(stringPattern, limitTo, matchRule);
 	}
 	return null;
 }
@@ -2149,6 +2155,9 @@
 		case IJavaElement.PACKAGE_FRAGMENT :
 			searchPattern = createPackagePattern(element.getElementName(), maskedLimitTo, matchRule);
 			break;
+		case IJavaElement.JAVA_MODULE :
+			searchPattern = createModulePattern(element.getElementName(), maskedLimitTo, matchRule);
+			break;
 	}
 	if (searchPattern != null)
 		MatchLocator.setFocus(searchPattern, element);
@@ -2329,6 +2338,8 @@
 	IJavaElement parent = type.getParent();
 	switch (parent.getElementType()) {
 		case IJavaElement.CLASS_FILE:
+			if (parent instanceof IModularClassFile)
+				return null;
 			// For a binary type, the parent is not the enclosing type, but the declaring type is.
 			// (see bug 20532  Declaration of member binary type not found)
 			IType declaringType = type.getDeclaringType();
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/HierarchicalASTVisitor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/HierarchicalASTVisitor.java
index 66f82de..f847d71 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/HierarchicalASTVisitor.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/HierarchicalASTVisitor.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
@@ -1135,5 +1135,86 @@
 	}
 
 //---- End VariableDeclaration Hierarchy -----------------------------
+//---- Begin Module Hierarchy -----------------------------------------
+	@Override
+	public boolean visit(ModuleDeclaration node) {
+		return visit((ASTNode) node);
+	}
+
+	@Override
+	public void endVisit(ModuleDeclaration node) {
+		endVisit((ASTNode) node);
+	}
+
+	@Override
+	public boolean visit(ModuleModifier node) {
+		return visit((ASTNode) node);
+	}
+
+	@Override
+	public void endVisit(ModuleModifier node) {
+		endVisit((ASTNode) node);
+	}
+
+	public boolean visit(ModuleDirective node) {
+		return visit((ASTNode) node);
+	}
+
+	public void endVisit(ModuleDirective node) {
+		endVisit((ASTNode) node);
+	}
+
+	@Override
+	public boolean visit(ExportsDirective node) {
+		return visit((ModuleDirective) node);
+	}
+
+	@Override
+	public void endVisit(ExportsDirective node) {
+		endVisit((ModuleDirective) node);
+	}
+
+	@Override
+	public boolean visit(OpensDirective node) {
+		return visit((ModuleDirective) node);
+	}
+
+	@Override
+	public void endVisit(OpensDirective node) {
+		endVisit((ModuleDirective) node);
+	}
+
+	@Override
+	public boolean visit(RequiresDirective node) {
+		return visit((ModuleDirective) node);
+	}
+
+	@Override
+	public void endVisit(RequiresDirective node) {
+		endVisit((ModuleDirective) node);
+	}
+
+	@Override
+	public boolean visit(ProvidesDirective node) {
+		return visit((ModuleDirective) node);
+	}
+
+	@Override
+	public void endVisit(ProvidesDirective node) {
+		endVisit((ModuleDirective) node);
+	}
+
+	@Override
+	public boolean visit(UsesDirective node) {
+		return visit((ModuleDirective) node);
+	}
+
+	@Override
+	public void endVisit(UsesDirective node) {
+		endVisit((ModuleDirective) node);
+	}
+
+//---- End Module Hierarchy -------------------------------------------
+
 //---- End ASTNode Hierarchy -----------------------------------------
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Indexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Indexer.java
index 0eeeb3c..96991ee 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Indexer.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Indexer.java
@@ -51,17 +51,18 @@
 import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
 import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
 import org.eclipse.core.runtime.preferences.InstanceScope;
-import org.eclipse.jdt.core.IClassFile;
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaElementDelta;
 import org.eclipse.jdt.core.IJavaModelStatusConstants;
 import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IOrdinaryClassFile;
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
 import org.eclipse.jdt.internal.compiler.env.IDependent;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
 import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
 import org.eclipse.jdt.internal.core.JavaElementDelta;
@@ -746,7 +747,7 @@
 								}
 								resourceFile.addZipEntry(fileName);
 
-								if (fileName.equals("META-INF/MANIFEST.MF")) { //$NON-NLS-1$
+								if (fileName.equals(TypeConstants.META_INF_MANIFEST_MF)) {
 									try (InputStream inputStream = zipFile.getInputStream(member)) {
 										char[] chars = getInputStreamAsCharArray(inputStream, -1, UTF_8);
 
@@ -809,8 +810,8 @@
 				Package.logInfo("The path " + element.getPath() + " contained no class files"); //$NON-NLS-1$ //$NON-NLS-2$
 			}
 			return classesIndexed;
-		} else if (element instanceof IClassFile) {
-			IClassFile classFile = (IClassFile)element;
+		} else if (element instanceof IOrdinaryClassFile) {
+			IOrdinaryClassFile classFile = (IOrdinaryClassFile) element;
 
 			SubMonitor iterationMonitor = subMonitor.split(1);
 			BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(classFile);
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaNames.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaNames.java
index 28f6348..5849b7a 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaNames.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaNames.java
@@ -151,12 +151,12 @@
 	}
 
 	/**
-	 * Returns a method id (suitable for constructing a {@link NdMethodId}) given a field descriptor for its parent type
+	 * Returns a method id given a field descriptor for its parent type
 	 * and a combined method selector and method descriptor for the method
 	 *
 	 * @param parentTypeBinaryName a field descriptor of the sort returned by the other *ToFieldDescriptor methods.
 	 * @param methodSelectorAndDescriptor a method selector and descriptor of the form returned by {@link IBinaryType#getEnclosingMethod()}
-	 * @return a method id suitable for looking up a {@link NdMethodId}
+	 * @return a method id
 	 */
 	public static char[] getMethodId(char[] parentTypeBinaryName, char[] methodSelectorAndDescriptor) {
 		return CharArrayUtils.concat(FIELD_DESCRIPTOR_PREFIX, parentTypeBinaryName, METHOD_ID_SEPARATOR,
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryModuleDescriptor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryModuleDescriptor.java
new file mode 100644
index 0000000..01043cf
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryModuleDescriptor.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2017 GK Software AG, 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:
+ *     Stephan Herrmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core.nd.java.model;
+
+import org.eclipse.jdt.internal.compiler.env.IDependent;
+import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils;
+
+/**
+ * Holds a lightweight identifier for an IBinaryModule, with sufficient information to either read it from
+ * disk or read it from the index.
+ */
+public class BinaryModuleDescriptor {
+	public final char[] indexPath;
+	public final char[] moduleName; // TODO: not sure if this will be needed once wired to the index?
+	public final char[] location;
+	public final char[] workspacePath;
+
+	/**
+	 * Constructs a new descriptor
+	 * 
+	 * @param location
+	 *            location where the archive (.jar or .class) can be found in the local filesystem
+	 * @param moduleName
+	 *            name of the module
+	 * @param workspacePath
+	 *            location where the archive (.jar or class) can be found in the workspace. If it is not in the
+	 *            workspace, this is the path where it can be found on the local filesystem.
+	 * @param indexPath
+	 *            index path for the new module (workspace-or-local path to jar optionally followed by a | and a relative
+	 *            path within the .jar)
+	 */
+	public BinaryModuleDescriptor(char[] location, char[] moduleName, char[] workspacePath, char[] indexPath) {
+		super();
+		this.location = location;
+		this.moduleName = moduleName;
+		this.indexPath = indexPath;
+		this.workspacePath = workspacePath;
+	}
+
+	public boolean isInJarFile() {
+		return CharArrayUtils.indexOf(IDependent.JAR_FILE_ENTRY_SEPARATOR, this.indexPath) != -1;
+	}
+
+	/**
+	 * For debugging purposes only.
+	 */
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append(this.workspacePath);
+		builder.append('|');
+		builder.append(this.moduleName);
+		return builder.toString();
+	}
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryModuleFactory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryModuleFactory.java
new file mode 100644
index 0000000..4db9ae2
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryModuleFactory.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2017 GK Software AG, 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:
+ *     Stephan Herrmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core.nd.java.model;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IJavaModelStatusConstants;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.env.IBinaryModule;
+import org.eclipse.jdt.internal.compiler.env.IDependent;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
+import org.eclipse.jdt.internal.core.JavaModelManager;
+import org.eclipse.jdt.internal.core.ModularClassFile;
+import org.eclipse.jdt.internal.core.PackageFragmentRoot;
+import org.eclipse.jdt.internal.core.nd.java.JavaIndex;
+
+/**
+ * <strong>FIXME:</strong> this class is a stub as of now, it does not support modules in the new index.
+ */
+public class BinaryModuleFactory {
+
+	public static BinaryModuleDescriptor createDescriptor(ModularClassFile modularClassFile) {
+		return createDescriptor(modularClassFile.getPackageFragmentRoot(), modularClassFile);
+	}
+
+	/**
+	 * Returns a descriptor for the given class within the given package fragment, or null if the fragment doesn't have
+	 * a location on the filesystem.
+	 */
+	private static BinaryModuleDescriptor createDescriptor(PackageFragmentRoot root, ModularClassFile classFile) {
+		IPath location = JavaIndex.getLocationForElement(root);
+		if (location == null) {
+			return null;
+		}
+		String entryName = TypeConstants.MODULE_INFO_CLASS_NAME_STRING;
+		IPath workspacePath = root.getPath();
+		String indexPath;
+
+		if (root instanceof JarPackageFragmentRoot) {
+			entryName = ((JarPackageFragmentRoot) root).getClassFilePath(entryName);
+			indexPath = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName;
+			// see additional comments in BinaryTypeFactor.createDescriptor()
+		} else {
+			location = location.append(entryName);
+			indexPath = workspacePath.append(entryName).toString();
+			workspacePath = classFile.resource().getFullPath();
+		}
+
+		return new BinaryModuleDescriptor(location.toString().toCharArray(), null, // TODO: module name not know at this point
+				workspacePath.toString().toCharArray(), indexPath.toCharArray());
+	}
+
+	/**
+	 * Reads the given binary module. If the module can be found in the index with a fingerprint that exactly matches
+	 * the file on disk, the type is read from the index. Otherwise the type is read from disk. Returns null if
+	 * no such type exists.
+	 * <strong>caveat</strong> modules are not yet supported in the index.
+	 * 
+	 * @throws ClassFormatException 
+	 */
+	public static IBinaryModule readModule(BinaryModuleDescriptor descriptor, IProgressMonitor monitor) throws JavaModelException, ClassFormatException {
+// FIXME: support module in the new index
+//		if (JavaIndex.isEnabled()) {
+//			try {
+//				return readFromIndex(JavaIndex.getIndex(), descriptor, monitor);
+//			} catch (NotInIndexException e) {
+//				// fall back to reading the zip file, below
+//			}
+//		}
+		return rawReadModule(descriptor, true);
+	}
+	
+	public static IBinaryModule rawReadModule(BinaryModuleDescriptor descriptor, boolean fullyInitialize) throws JavaModelException, ClassFormatException {
+		try {
+			return rawReadModuleTestForExists(descriptor, fullyInitialize, true);
+		} catch (FileNotFoundException e) {
+			throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
+		}
+	}
+
+	/**
+	 * Read the class file from disk, circumventing the index's cache. This should only be used by callers
+	 * that need to read information from the class file which aren't present in the index (such as method bodies).
+	 * 
+	 * @return the newly-created IBinaryModule or null if the given class file does not exist.
+	 * @throws ClassFormatException if the class file existed but was corrupt
+	 * @throws JavaModelException if unable to read the class file due to a transient failure
+	 * @throws FileNotFoundException if the file does not exist
+	 */
+	public static IBinaryModule rawReadModuleTestForExists(BinaryModuleDescriptor descriptor, boolean fullyInitialize,
+			boolean useInvalidArchiveCache) throws JavaModelException, ClassFormatException, FileNotFoundException {
+		if (descriptor == null) {
+			return null;
+		}
+		if (descriptor.isInJarFile()) {
+			ZipFile zip = null;
+			try {
+				zip = JavaModelManager.getJavaModelManager().getZipFile(new Path(new String(descriptor.workspacePath)),
+						useInvalidArchiveCache);
+				String entryName = TypeConstants.MODULE_INFO_CLASS_NAME_STRING;
+				ZipEntry ze = zip.getEntry(entryName);
+				if (ze != null) {
+					byte contents[];
+					try {
+						contents = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip);
+					} catch (IOException ioe) {
+						throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION);
+					}
+					ClassFileReader classFileReader = new ClassFileReader(contents, descriptor.indexPath, fullyInitialize);
+					return classFileReader.getModuleDeclaration();
+				}
+			} catch (CoreException e) {
+				throw new JavaModelException(e);
+			} finally {
+				JavaModelManager.getJavaModelManager().closeZipFile(zip);
+			}
+		} else {
+			IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(new String(descriptor.workspacePath)));
+			byte[] contents;
+			try (InputStream stream = file.getContents(true)) {
+				contents = org.eclipse.jdt.internal.compiler.util.Util.getInputStreamAsByteArray(stream, -1);
+			} catch (CoreException e) {
+				IStatus status = e.getStatus();
+				if (status.getCode() == IResourceStatus.RESOURCE_NOT_FOUND) {
+					throw new FileNotFoundException();
+				}
+				throw new JavaModelException(e);
+			} catch (IOException e) {
+				throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
+			}
+			ClassFileReader classFileReader = new ClassFileReader(contents, file.getFullPath().toString().toCharArray(), fullyInitialize);
+			return classFileReader.getModuleDeclaration();
+		}
+		return null;
+	}
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java
index f46fd1f..61e2990 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java
@@ -25,7 +25,6 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.jdt.core.IClassFile;
-import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaModelStatusConstants;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
@@ -38,6 +37,7 @@
 import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
 import org.eclipse.jdt.internal.core.JavaModelManager;
 import org.eclipse.jdt.internal.core.PackageFragment;
+import org.eclipse.jdt.internal.core.PackageFragmentRoot;
 import org.eclipse.jdt.internal.core.nd.IReader;
 import org.eclipse.jdt.internal.core.nd.Nd;
 import org.eclipse.jdt.internal.core.nd.db.IndexException;
@@ -64,19 +64,20 @@
 	 */
 	private static BinaryTypeDescriptor createDescriptor(PackageFragment pkg, ClassFile classFile) {
 		String name = classFile.getName();
-		IJavaElement root = pkg.getParent();
+		PackageFragmentRoot root = (PackageFragmentRoot) pkg.getParent();
 		IPath location = JavaIndex.getLocationForElement(root);
-		String entryName = Util.concatWith(pkg.names, classFile.getElementName(), '/');
-		char[] fieldDescriptor = CharArrayUtils.concat(new char[] { 'L' },
-				Util.concatWith(pkg.names, name, '/').toCharArray(), new char[] { ';' });
-		IPath workspacePath = root.getPath();
-		String indexPath;
-
 		if (location == null) {
 			return null;
 		}
+		name = root.getClassFilePath(Util.concatWith(pkg.names, name, '/'));
+		String entryName = Util.concatWith(pkg.names, classFile.getElementName(), '/');
+		char[] fieldDescriptor = CharArrayUtils.concat(new char[] { 'L' },
+				name.toCharArray(), new char[] { ';' });
+		IPath workspacePath = root.getPath();
+		String indexPath;
 
 		if (root instanceof JarPackageFragmentRoot) {
+			entryName = ((JarPackageFragmentRoot) root).getClassFilePath(entryName);
 			// The old version returned this, but it doesn't conform to the spec on IBinaryType.getFileName():
 			indexPath = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName;
 			// Version that conforms to the JavaDoc spec on IBinaryType.getFileName() -- note that this breaks
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryType.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryType.java
index 850f41d..a36d79b 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryType.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryType.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 Google, Inc and others.
+ * Copyright (c) 2016, 2017 Google, Inc 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
@@ -698,4 +698,10 @@
 		return this; // could not improve
 	}
 // SH}
+
+	@Override
+	public char[] getModule() {
+		// TODO Auto-generated method stub
+		return null;
+	}
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/IndexSelector.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/IndexSelector.java
index 53684c9..6ef28f6 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/IndexSelector.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/IndexSelector.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -31,6 +31,7 @@
 import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
 import org.eclipse.jdt.internal.core.search.matching.MatchLocator;
 import org.eclipse.jdt.internal.core.search.matching.MethodPattern;
+import org.eclipse.jdt.internal.core.search.matching.ModulePattern;
 
 /**
  * Selects the indexes that correspond to projects in a given search scope
@@ -200,7 +201,7 @@
 	IndexManager manager = JavaModelManager.getIndexManager();
 	// use a linked set to preserve the order during search: see bug 348507
 	LinkedHashSet locations = new LinkedHashSet();
-	IJavaElement focus = MatchLocator.projectOrJarFocus(this.pattern);
+	IJavaElement focus = this.pattern instanceof ModulePattern ? null : MatchLocator.projectOrJarFocus(this.pattern);
 	if (focus == null) {
 		for (int i = 0; i < projectsAndJars.length; i++) {
 			IPath path = projectsAndJars[i];
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java
index d451933..4148356 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -32,7 +32,11 @@
 		super(documentPath, participant);
 	}
 	public JavaSearchDocument(java.util.zip.ZipEntry zipEntry, IPath zipFilePath, byte[] contents, SearchParticipant participant) {
-		super(zipFilePath + IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR + zipEntry.getName(), participant);
+		this(zipFilePath + IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR + zipEntry.getName(), contents, participant);
+	}
+
+	public JavaSearchDocument(String documentPath, byte[] contents, SearchParticipant participant) {
+		super(documentPath, participant);
 		this.byteContents = contents;
 	}
 
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/NameMatchRequestorWrapper.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/NameMatchRequestorWrapper.java
index 67f8083..3359867 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/NameMatchRequestorWrapper.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/NameMatchRequestorWrapper.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 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
@@ -12,10 +12,10 @@
 package org.eclipse.jdt.internal.core.search;
 
 import org.eclipse.core.runtime.Path;
-import org.eclipse.jdt.core.IClassFile;
 import org.eclipse.jdt.core.ICompilationUnit;
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IOrdinaryClassFile;
 import org.eclipse.jdt.core.IPackageFragment;
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.IType;
@@ -66,22 +66,19 @@
 		if (this.handleFactory != null) {
 			Openable openable = this.handleFactory.createOpenable(path, this.scope);
 			if (openable == null) return type;
-			switch (openable.getElementType()) {
-				case IJavaElement.COMPILATION_UNIT:
-					ICompilationUnit cu = (ICompilationUnit) openable;
-					if (enclosingTypeNames != null && enclosingTypeNames.length > 0) {
-						type = cu.getType(new String(enclosingTypeNames[0]));
-						for (int j=1, l=enclosingTypeNames.length; j<l; j++) {
-							type = type.getType(new String(enclosingTypeNames[j]));
-						}
-						type = type.getType(new String(simpleTypeName));
-					} else {
-						type = cu.getType(new String(simpleTypeName));
+			if (openable instanceof ICompilationUnit) {
+				ICompilationUnit cu = (ICompilationUnit) openable;
+				if (enclosingTypeNames != null && enclosingTypeNames.length > 0) {
+					type = cu.getType(new String(enclosingTypeNames[0]));
+					for (int j=1, l=enclosingTypeNames.length; j<l; j++) {
+						type = type.getType(new String(enclosingTypeNames[j]));
 					}
-					break;
-				case IJavaElement.CLASS_FILE:
-					type = ((IClassFile)openable).getType();
-					break;
+					type = type.getType(new String(simpleTypeName));
+				} else {
+					type = cu.getType(new String(simpleTypeName));
+				}
+			} else if (openable instanceof IOrdinaryClassFile) {
+				type = ((IOrdinaryClassFile)openable).getType();
 			}
 		} else {
 			int separatorIndex= path.indexOf(IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR);
@@ -110,6 +107,9 @@
 	}
 	// create handle
 	String classFilePath= resourcePath.substring(separatorIndex + 1);
+	int actualClassIndexSeparator = classFilePath.indexOf(IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR);
+	String moduleName = actualClassIndexSeparator == -1 ? null : classFilePath.substring(0, actualClassIndexSeparator);
+	classFilePath = moduleName != null ? classFilePath.substring(actualClassIndexSeparator + 1, classFilePath.length()) : classFilePath;
 	String[] simpleNames = new Path(classFilePath).segments();
 	String[] pkgName;
 	int length = simpleNames.length-1;
@@ -121,7 +121,7 @@
 	}
 	IPackageFragment pkgFragment= (IPackageFragment) this.packageHandles.get(pkgName);
 	if (pkgFragment == null) {
-		pkgFragment= ((PackageFragmentRoot) this.lastPkgFragmentRoot).getPackageFragment(pkgName);
+		pkgFragment= ((PackageFragmentRoot) this.lastPkgFragmentRoot).getPackageFragment(pkgName, moduleName); //BUG 478143
 		// filter org.apache.commons.lang.enum package for projects above 1.5 
 		// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=317264
 		if (length == 5 && pkgName[4].equals("enum")) { //$NON-NLS-1$
@@ -136,7 +136,7 @@
 		} 
 		this.packageHandles.put(pkgName, pkgFragment);
 	}
-	return pkgFragment.getClassFile(simpleNames[length]).getType();
+	return pkgFragment.getOrdinaryClassFile(simpleNames[length]).getType();
 }
 private IType createTypeFromPath(String resourcePath, String simpleTypeName, char[][] enclosingTypeNames) throws JavaModelException {
 	// path to a file in a directory
@@ -181,7 +181,7 @@
 		}
 		return type;
 	} else if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(simpleName)){
-		IClassFile classFile= pkgFragment.getClassFile(simpleName);
+		IOrdinaryClassFile classFile= pkgFragment.getOrdinaryClassFile(simpleName);
 		return classFile.getType();
 	}
 	return null;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AbstractIndexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AbstractIndexer.java
index a808fd3..2de124b 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AbstractIndexer.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AbstractIndexer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -251,6 +251,17 @@
 	public void addMethodReference(char[] methodName, int argCount) {
 		addIndexEntry(METHOD_REF, MethodPattern.createIndexKey(methodName, argCount));
 	}
+	public void addModuleDeclaration(char[] moduleName) {
+		addIndexEntry(MODULE_DECL, ModulePattern.createIndexKey(moduleName));
+	}
+	public void addModuleExportedPackages(char[] packageName) {
+		char[][] tokens = CharOperation.splitOn('.', packageName);
+		for (int i = 0, l = tokens.length; i < l; ++i)
+			addNameReference(tokens[i]);
+	}
+	public void addModuleReference(char[] moduleName) {
+		addIndexEntry(MODULE_REF, ModulePattern.createIndexKey(moduleName));
+	}
 	public void addNameReference(char[] name) {
 		addIndexEntry(REF, name);
 	}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java
index d0c89f5..a4254b8 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -23,15 +23,10 @@
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.Path;
-import org.eclipse.jdt.core.compiler.InvalidInputException;
 import org.eclipse.jdt.core.search.IJavaSearchScope;
 import org.eclipse.jdt.core.search.SearchEngine;
 import org.eclipse.jdt.core.search.SearchParticipant;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
-import org.eclipse.jdt.internal.compiler.parser.Scanner;
-import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
 import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
-import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
 import org.eclipse.jdt.internal.compiler.util.Util;
 import org.eclipse.jdt.internal.core.JavaModelManager;
 import org.eclipse.jdt.internal.core.index.Index;
@@ -40,11 +35,10 @@
 import org.eclipse.jdt.internal.core.search.processing.JobManager;
 
 @SuppressWarnings("rawtypes")
-class AddJarFileToIndex extends IndexRequest {
+class AddJarFileToIndex extends BinaryContainer {
 
 	private static final char JAR_SEPARATOR = IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR.charAt(0);
 	IFile resource;
-	Scanner scanner;
 	private IndexLocation indexFileURL;
 	private final boolean forceIndexUpdate;
 
@@ -180,7 +174,7 @@
 						// iterate each entry to index it
 						ZipEntry ze = (ZipEntry) e.nextElement();
 						String zipEntryName = ze.getName();
-						if (Util.isClassFileName(zipEntryName) && isValidPackageNameForClass(zipEntryName))
+						if (Util.isClassFileName(zipEntryName) && isValidPackageNameForClassOrisModule(zipEntryName))
 								// the class file may not be there if the package name is not valid
 							indexedFileNames.put(zipEntryName, EXISTS);
 					}
@@ -229,7 +223,7 @@
 					ZipEntry ze = (ZipEntry) e.nextElement();
 					String zipEntryName = ze.getName();
 					if (Util.isClassFileName(zipEntryName) && 
-							isValidPackageNameForClass(zipEntryName)) {
+							isValidPackageNameForClassOrisModule(zipEntryName)) {
 						// index only classes coming from valid packages - https://bugs.eclipse.org/bugs/show_bug.cgi?id=293861
 						final byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip);
 						JavaSearchDocument entryDocument = new JavaSearchDocument(ze, zipFilePath, classFileBytes, participant);
@@ -276,47 +270,6 @@
 			return super.getJobFamily();
 		return this.containerPath.toOSString(); // external jar
 	}	
-	private boolean isIdentifier() throws InvalidInputException {
-		switch(this.scanner.scanIdentifier()) {
-			// assert and enum will not be recognized as java identifiers 
-			// in 1.7 mode, which are in 1.3.
-			case TerminalTokens.TokenNameIdentifier:
-			case TerminalTokens.TokenNameassert:
-			case TerminalTokens.TokenNameenum:
-				return true;
-			default:
-				return false;
-		}
-	}
-	private  boolean isValidPackageNameForClass(String className) {
-		char[] classNameArray = className.toCharArray();
-		// use 1.7 as the source level as there are more valid identifiers in 1.7 mode
-		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=376673
-		if (this.scanner == null)
-			this.scanner = new Scanner(false /* comment */, true /* whitespace */, false /* nls */,
-					ClassFileConstants.JDK1_7/* sourceLevel */, null/* taskTag */, null/* taskPriorities */, true /* taskCaseSensitive */);
-//{ObjectTeams: don't exclude OT keywords appearing as package names:
-		this.scanner.forceBaseIsIdentifier();
-		this.scanner.parsePureJavaOnly = true;
-		this.scanner.parseOTJonly = false;
-// SH}
-		this.scanner.setSource(classNameArray); 
-		this.scanner.eofPosition = classNameArray.length - SuffixConstants.SUFFIX_CLASS.length;
-		try {
-			if (isIdentifier()) {
-				while (this.scanner.eofPosition > this.scanner.currentPosition) {
-					if (this.scanner.getNextChar() != '/' || this.scanner.eofPosition <= this.scanner.currentPosition) {
-						return false;
-					}
-					if (!isIdentifier()) return false;
-				}
-				return true;
-			}
-		} catch (InvalidInputException e) {
-			// invalid class name
-		}
-		return false;
-	}
 	protected Integer updatedIndexState() {
 
 		Integer updateState = null;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJrtToIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJrtToIndex.java
new file mode 100644
index 0000000..e20aaa7
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJrtToIndex.java
@@ -0,0 +1,310 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.core.search.indexing;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileVisitResult;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.search.IJavaSearchScope;
+import org.eclipse.jdt.core.search.SearchEngine;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.util.JRTUtil;
+import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
+import org.eclipse.jdt.internal.compiler.util.Util;
+import org.eclipse.jdt.internal.core.JavaModelManager;
+import org.eclipse.jdt.internal.core.index.Index;
+import org.eclipse.jdt.internal.core.index.IndexLocation;
+import org.eclipse.jdt.internal.core.search.JavaSearchDocument;
+import org.eclipse.jdt.internal.core.search.processing.JobManager;
+
+public class AddJrtToIndex extends BinaryContainer {
+
+	IFile resource;
+	private IndexLocation indexFileURL;
+	private final boolean forceIndexUpdate;
+	static final char JAR_SEPARATOR = IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR.charAt(0);
+	
+	enum FILE_INDEX_STATE {
+		EXISTS,
+		DELETED
+	}
+
+	public AddJrtToIndex(IFile resource, IndexLocation indexFile, IndexManager manager, final boolean updateIndex) {
+		super(resource.getFullPath(), manager);
+		this.resource = resource;
+		this.indexFileURL = indexFile;
+		this.forceIndexUpdate = updateIndex;
+	}
+	public AddJrtToIndex(IPath jrtPath, IndexLocation indexFile, IndexManager manager, final boolean updateIndex) {
+		// external JAR scenario - no resource
+		super(jrtPath, manager);
+		this.indexFileURL = indexFile;
+		this.forceIndexUpdate = updateIndex;
+	}
+	public boolean equals(Object o) {
+		if (o instanceof AddJrtToIndex) {
+			if (this.resource != null)
+				return this.resource.equals(((AddJrtToIndex) o).resource);
+			if (this.containerPath != null)
+				return this.containerPath.equals(((AddJrtToIndex) o).containerPath);
+		}
+		return false;
+	}
+	public int hashCode() {
+		if (this.resource != null)
+			return this.resource.hashCode();
+		if (this.containerPath != null)
+			return this.containerPath.hashCode();
+		return -1;
+	}
+	
+	private class JrtTraverser implements org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor<java.nio.file.Path> {
+		
+		SimpleLookupTable indexedFileNames;
+		public JrtTraverser() {
+		}
+		public JrtTraverser(SimpleLookupTable indexedFileNames) {
+			this.indexedFileNames = indexedFileNames;
+		}
+
+		@Override
+		public FileVisitResult visitPackage(java.nio.file.Path dir, java.nio.file.Path mod, BasicFileAttributes attrs)
+				throws IOException {
+			return FileVisitResult.CONTINUE;
+		}
+
+		@Override
+		public FileVisitResult visitFile(java.nio.file.Path path, java.nio.file.Path mod, BasicFileAttributes attrs)
+				throws IOException {
+			String name = path.getFileName().toString();
+			if (Util.isClassFileName(name) && 
+					isValidPackageNameForClassOrisModule(name)) {
+				this.indexedFileNames.put(name, FILE_INDEX_STATE.EXISTS);
+			}
+			return FileVisitResult.CONTINUE;
+		}
+		@Override
+		public FileVisitResult visitModule(java.nio.file.Path mod) throws IOException {
+			return FileVisitResult.CONTINUE;
+		}
+	}
+	
+	private class JrtIndexer extends JrtTraverser {
+		final SearchParticipant participant;
+		final IPath indexPath;
+		final IndexManager indexManager;
+		final IPath container;
+		final Index index;
+		final File jrt;
+
+		public JrtIndexer(File jrt, SearchParticipant participant, Index index, IPath container, IndexManager indexManager) {
+			this.jrt = jrt;
+			this.participant = (participant != null) ? participant : SearchEngine.getDefaultSearchParticipant();
+			this.index = index;
+			IndexLocation indexLocation = index.getIndexLocation();
+			this.indexPath = indexLocation != null ? new Path(indexLocation.getCanonicalFilePath()) : null;
+			this.container = container;
+			this.indexManager = indexManager;
+		}
+
+		public FileVisitResult visitFile(java.nio.file.Path path, java.nio.file.Path mod, BasicFileAttributes attrs)
+				throws IOException {
+			String name = path.getFileName().toString();
+			if (Util.isClassFileName(name) && 
+					isValidPackageNameForClassOrisModule(name)) {
+				try {
+					String fullPath = path.toString();
+					byte[] classFileBytes;
+					classFileBytes = JRTUtil.getClassfileContent(this.jrt, fullPath, mod.toString());
+					String docFullPath =  this.container.toString() + JAR_SEPARATOR + mod.toString() + JAR_SEPARATOR + fullPath;
+					JavaSearchDocument entryDocument = new JavaSearchDocument(docFullPath, classFileBytes, this.participant);
+					this.indexManager.indexDocument(entryDocument, this.participant, this.index, this.indexPath);
+				} catch (IOException | ClassFormatException e) {
+					e.printStackTrace();
+				}
+			}
+			return FileVisitResult.CONTINUE;
+		}
+	}
+
+	public boolean execute(IProgressMonitor progressMonitor) {
+
+		if (this.isCancelled || progressMonitor != null && progressMonitor.isCanceled()) return true;
+
+		if (hasPreBuiltIndex()) {
+			boolean added = this.manager.addIndex(this.containerPath, this.indexFileURL);
+			if (added) return true;	
+			this.indexFileURL = null;
+		}
+
+		try {
+			// if index is already cached, then do not perform any check
+			// MUST reset the IndexManager if a jar file is changed
+			if (this.manager.getIndexForUpdate(this.containerPath, false, /*do not reuse index file*/ false /*do not create if none*/) != null) {
+				if (JobManager.VERBOSE)
+					org.eclipse.jdt.internal.core.util.Util.verbose("-> no indexing required (index already exists) for " + this.containerPath); //$NON-NLS-1$
+				return true;
+			}
+
+			final Index index = this.manager.getIndexForUpdate(this.containerPath, true, /*reuse index file*/ true /*create if none*/);
+			if (index == null) {
+				if (JobManager.VERBOSE)
+					org.eclipse.jdt.internal.core.util.Util.verbose("-> index could not be created for " + this.containerPath); //$NON-NLS-1$
+				return true;
+			}
+			index.separator = JAR_SEPARATOR;
+			ReadWriteMonitor monitor = index.monitor;
+			if (monitor == null) {
+				if (JobManager.VERBOSE)
+					org.eclipse.jdt.internal.core.util.Util.verbose("-> index for " + this.containerPath + " just got deleted"); //$NON-NLS-1$//$NON-NLS-2$
+				return true; // index got deleted since acquired
+			}
+			try {
+				final String fileName;
+				final IPath container;
+				monitor.enterWrite(); // ask permission to write
+
+				if (this.resource != null) {
+					URI location = this.resource.getLocationURI();
+					if (location == null) return false;
+					if (JavaModelManager.JRT_ACCESS_VERBOSE)
+						System.out.println("(" + Thread.currentThread() + ") [AddJrtFileToIndex.execute()] Creating ZipFile on " + location.getPath()); //$NON-NLS-1$	//$NON-NLS-2$
+					File file = null;
+					try {
+						file = org.eclipse.jdt.internal.core.util.Util.toLocalFile(location, progressMonitor);
+					} catch (CoreException e) {
+						if (JobManager.VERBOSE) {
+							org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + location.getPath() + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$
+							e.printStackTrace();
+						}
+					}
+					if (file == null) {
+						if (JobManager.VERBOSE)
+							org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + location.getPath() + " because the file could not be fetched"); //$NON-NLS-1$ //$NON-NLS-2$
+						return false;
+					}
+					fileName = file.getAbsolutePath();
+					container =  this.resource.getFullPath().makeRelative();
+					// absolute path relative to the workspace
+				} else {
+					
+					fileName = this.containerPath.toOSString();
+					container = this.containerPath;
+				}
+
+
+				if (JobManager.VERBOSE)
+					org.eclipse.jdt.internal.core.util.Util.verbose("-> indexing " + fileName); //$NON-NLS-1$
+				long initialTime = System.currentTimeMillis();
+				String[] paths = index.queryDocumentNames(""); // all file names //$NON-NLS-1$
+				if (paths != null) {
+					int max = paths.length;
+					/* check integrity of the existing index file
+					 * if the length is equal to 0, we want to index the whole jrt again
+					 * If not, then we want to check that there is no missing entry, if
+					 * one entry is missing then we recreate the index
+					 */
+					
+					final SimpleLookupTable indexedFileNames = new SimpleLookupTable(max == 0 ? 33 : max + 11);
+					for (int i = 0; i < max; i++)
+						indexedFileNames.put(paths[i], FILE_INDEX_STATE.DELETED);
+					
+					org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(new File(fileName), 
+							new JrtTraverser(indexedFileNames), JRTUtil.NOTIFY_FILES);
+
+					boolean needToReindex = indexedFileNames.elementSize != max; // a new file was added
+					if (!needToReindex) {
+						Object[] valueTable = indexedFileNames.valueTable;
+						for (int i = 0, l = valueTable.length; i < l; i++) {
+							if (valueTable[i] == FILE_INDEX_STATE.DELETED) {
+								needToReindex = true; // a file was deleted so re-index
+								break;
+							}
+						}
+						if (!needToReindex) {
+							if (JobManager.VERBOSE)
+								org.eclipse.jdt.internal.core.util.Util.verbose("-> no indexing required (index is consistent with library) for " //$NON-NLS-1$
+								+ fileName + " (" //$NON-NLS-1$
+								+ (System.currentTimeMillis() - initialTime) + "ms)"); //$NON-NLS-1$
+							this.manager.saveIndex(index); // to ensure its placed into the saved state
+							return true;
+						}
+					}
+				}
+
+				// Index the jrt for the first time or reindex the jrt in case the previous index file has been corrupted
+				// index already existed: recreate it so that we forget about previous entries
+				if (!this.manager.resetIndex(this.containerPath)) {
+					// failed to recreate index, see 73330
+					this.manager.removeIndex(this.containerPath);
+					return false;
+				}
+				
+				File jrt = new File(fileName);
+				org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(jrt, 
+						new JrtIndexer(jrt, SearchEngine.getDefaultSearchParticipant(), index, container, this.manager), JRTUtil.NOTIFY_FILES);
+
+				if(this.forceIndexUpdate) {
+					this.manager.savePreBuiltIndex(index);
+				}
+				else {
+					this.manager.saveIndex(index);
+				}
+				if (JobManager.VERBOSE)
+					org.eclipse.jdt.internal.core.util.Util.verbose("-> done indexing of " //$NON-NLS-1$
+						+ fileName + " (" //$NON-NLS-1$
+						+ (System.currentTimeMillis() - initialTime) + "ms)"); //$NON-NLS-1$
+			} finally {
+				monitor.exitWrite();
+			}
+		} catch (IOException e ) {
+			if (JobManager.VERBOSE) {
+				org.eclipse.jdt.internal.core.util.Util.verbose("-> failed to index " + this.containerPath + " because of the following exception:"); //$NON-NLS-1$ //$NON-NLS-2$
+				e.printStackTrace();
+			}
+			this.manager.removeIndex(this.containerPath);
+			return false;
+		}
+		return true;
+	}
+	public String getJobFamily() {
+		if (this.resource != null)
+			return super.getJobFamily();
+		return this.containerPath.toOSString(); // external jar
+	}	
+	protected Integer updatedIndexState() {
+
+		Integer updateState = null;
+		if(hasPreBuiltIndex()) {
+			updateState = IndexManager.REUSE_STATE;
+		}
+		else {
+			updateState = IndexManager.REBUILDING_STATE;
+		}
+		return updateState;
+	}
+	public String toString() {
+		return "indexing " + this.containerPath.toString(); //$NON-NLS-1$
+	}
+
+	protected boolean hasPreBuiltIndex() {
+		return !this.forceIndexUpdate && (this.indexFileURL != null && this.indexFileURL.exists());
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryContainer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryContainer.java
new file mode 100644
index 0000000..a3da044
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryContainer.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.internal.core.search.indexing;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.compiler.InvalidInputException;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.parser.Scanner;
+import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
+import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
+
+public abstract class BinaryContainer extends IndexRequest {
+
+	Scanner scanner;
+	public BinaryContainer(IPath containerPath, IndexManager manager) {
+		super(containerPath, manager);
+	}
+
+	private boolean isIdentifier() throws InvalidInputException {
+		switch(this.scanner.scanIdentifier()) {
+			// assert and enum will not be recognized as java identifiers 
+			// in 1.7 mode, which are in 1.3.
+			case TerminalTokens.TokenNameIdentifier:
+			case TerminalTokens.TokenNameassert:
+			case TerminalTokens.TokenNameenum:
+				return true;
+			default:
+				return false;
+		}
+	}
+	protected boolean isValidPackageNameForClassOrisModule(String className) {
+		if (className.substring(0, className.length() - (SuffixConstants.SUFFIX_CLASS.length)).equals(new String(IIndexConstants.MODULE_INFO))) 
+			return true;
+		char[] classNameArray = className.toCharArray();
+		// use 1.7 as the source level as there are more valid identifiers in 1.7 mode
+		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=376673
+		if (this.scanner == null)
+			this.scanner = new Scanner(false /* comment */, true /* whitespace */, false /* nls */,
+					ClassFileConstants.JDK1_7/* sourceLevel */, null/* taskTag */, null/* taskPriorities */, true /* taskCaseSensitive */);
+		
+		this.scanner.setSource(classNameArray); 
+		this.scanner.eofPosition = classNameArray.length - SuffixConstants.SUFFIX_CLASS.length;
+		try {
+			if (isIdentifier()) {
+				while (this.scanner.eofPosition > this.scanner.currentPosition) {
+					if (this.scanner.getNextChar() != '/' || this.scanner.eofPosition <= this.scanner.currentPosition) {
+						return false;
+					}
+					if (!isIdentifier()) return false;
+				}
+				return true;
+			}
+		} catch (InvalidInputException e) {
+			// invalid class name
+		}
+		return false;
+	}
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java
index 1c62339..e7493a7 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/BinaryIndexer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -26,6 +26,10 @@
 import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature;
 import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
 import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair;
+import org.eclipse.jdt.internal.compiler.env.IModule;
+import org.eclipse.jdt.internal.compiler.env.IModule.IModuleReference;
+import org.eclipse.jdt.internal.compiler.env.IModule.IPackageExport;
+import org.eclipse.jdt.internal.compiler.env.IModule.IService;
 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
@@ -146,6 +150,13 @@
 			}
 			addFieldReference(TypeConstants.TYPE);
 		}
+		if ((bits & TagBits.AnnotationForModule) != 0) {
+			if (compoundName == null) {
+				compoundName = TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
+				addTypeReference(compoundName[compoundName.length-1]);
+			}
+			addFieldReference(TypeConstants.UPPER_MODULE);
+		}
 	}
 	private void addBinaryRetentionAnnotation(long bits) {
 		char[][] compoundName = TypeConstants.JAVA_LANG_ANNOTATION_RETENTIONPOLICY;
@@ -633,6 +644,12 @@
 			if (contents == null) return;
 			final String path = this.document.getPath();
 			ClassFileReader reader = new ClassFileReader(contents, path == null ? null : path.toCharArray());
+			
+			IModule module = reader.getModuleDeclaration();
+			if (module != null) {
+				indexModule(module);
+				return;
+			}
 	
 			// first add type references
 			char[] className = replace('/', '.', reader.getName()); // looks like java/lang/String
@@ -834,6 +851,56 @@
 		}
 	}
 	
+	private void indexModule(IModule module) {
+		addModuleDeclaration(module.name());
+		IModuleReference[] requiredModules = module.requires();
+		if (requiredModules != null) {
+			for (IModuleReference req : requiredModules) {
+				addModuleReference(req.name());
+			}
+		}
+		indexPackageVisibilityDirective(module.exports());
+		indexPackageVisibilityDirective(module.opens());
+		char[][] refUsed = module.uses();
+		if (refUsed != null) {
+			for (char[] ref : refUsed) {
+				indexTypeReference(ref);
+			}
+		}
+		IService[] services = module.provides();
+		if (services != null) {
+			for (IService service : services) {
+				indexTypeReference(service.name());
+				indexTypeReferences(service.with());
+			}
+		}
+	}
+	private void indexPackageVisibilityDirective(IPackageExport[] exportedPackages) {
+		if (exportedPackages != null) {
+			for (IPackageExport pack : exportedPackages) {
+				addModuleExportedPackages(pack.name());
+				char[][] tgts = pack.targets();
+				if (tgts == null || tgts.equals(CharOperation.NO_CHAR_CHAR)) continue;
+				for (char[] tgt : tgts) {
+					if (tgt != null && !tgt.equals(CharOperation.NO_CHAR)) 
+						addModuleReference(tgt);
+				}
+			}
+		}
+	}
+	private void indexTypeReferences(char[][] ref) {
+		if (ref == null || ref.equals(CharOperation.NO_CHAR))
+			return;
+		for (int i = 0; i < ref.length; i++) {
+			addTypeReference(ref[i]);
+		}
+	}
+	private void indexTypeReference(char[] ref) {
+		if (ref == null || ref.equals(CharOperation.NO_CHAR))
+			return;
+		addTypeReference(ref);
+	}
+	
 	private char[] removeFirstSyntheticParameter(char[] descriptor) {
 		if (descriptor == null) return null;
 		if (descriptor.length < 3) return descriptor;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IIndexConstants.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IIndexConstants.java
index f06b5d1..866c2f7 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IIndexConstants.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IIndexConstants.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -28,7 +28,10 @@
 	char[] METHOD_DECL_PLUS= "methodDeclPlus".toCharArray(); //$NON-NLS-1$
 	char[] CONSTRUCTOR_DECL= "constructorDecl".toCharArray(); //$NON-NLS-1$
 	char[] FIELD_DECL= "fieldDecl".toCharArray(); //$NON-NLS-1$
+	char[] MODULE_DECL= "moduleDecl".toCharArray(); //$NON-NLS-1$
+	char[] MODULE_REF= "moduleRef".toCharArray(); //$NON-NLS-1$
 	char[] OBJECT = "Object".toCharArray(); //$NON-NLS-1$
+	char[] MODULE_INFO = "module-info".toCharArray(); ////$NON-NLS-1$
 //{ObjectTeams: needed for indexing Team and Role types
 	// OT index categories
 	char [] TEAM_DECL = "teamDecl".toCharArray(); //$NON-NLS-1$
@@ -87,6 +90,7 @@
 	int TYPE_PARAM_PATTERN = 0x0400;
 	int AND_PATTERN = 0x0800;
 	int ANNOT_REF_PATTERN = 0x1000;
+	int MODULE_PATTERN = 0x2000;
 //{ObjectTeams
 	// leave some room to Eclipse so we don't clash upon the next release
 	int TEAM_DECL_PATTERN          = 0x10000;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
index b0b6186..d1a8304 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
@@ -42,6 +42,7 @@
 import org.eclipse.jdt.internal.compiler.SourceElementParser;
 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+import org.eclipse.jdt.internal.compiler.util.JRTUtil;
 import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
 import org.eclipse.jdt.internal.compiler.util.SimpleSet;
 import org.eclipse.jdt.internal.core.ClasspathEntry;
@@ -592,6 +593,14 @@
 	this.indexLibrary(path, requestingProject, indexURL, false);
 }
 
+private IndexRequest getRequest(Object target, IPath jPath, IndexLocation indexFile, IndexManager manager, boolean updateIndex) {
+	return isJrt(((File) target).getName()) ? new AddJrtToIndex(jPath, indexFile, this, updateIndex) :
+		new AddJarFileToIndex(jPath, indexFile, this, updateIndex);
+}
+
+private boolean isJrt(String fileName) {
+	return fileName != null && fileName.endsWith(JRTUtil.JRT_FS_JAR);
+}
 /**
  * Trigger addition of a library to an index
  * Note: the actual operation is performed in background
@@ -619,9 +628,11 @@
 	IndexRequest request = null;
 	Object target = JavaModel.getTarget(path, true);
 	if (target instanceof IFile) {
-		request = new AddJarFileToIndex((IFile) target, indexFile, this, forceIndexUpdate);
+		request = isJrt(((IFile) target).getFullPath().toOSString()) ? 
+				new AddJrtToIndex((IFile) target, indexFile, this, forceIndexUpdate) :
+					new AddJarFileToIndex((IFile) target, indexFile, this, forceIndexUpdate);
 	} else if (target instanceof File) {
-		request = new AddJarFileToIndex(path, indexFile, this, forceIndexUpdate);
+		request = getRequest(target, path, indexFile, this, forceIndexUpdate);
 	} else if (target instanceof IContainer) {
 		request = new IndexBinaryFolder((IContainer) target, this);
 	} else {
@@ -727,9 +738,11 @@
 	} else if (target instanceof IFolder) {
 		request = new IndexBinaryFolder((IFolder) target, this);
 	} else if (target instanceof IFile) {
-		request = new AddJarFileToIndex((IFile) target, null, this, updateIndex);
+		request = isJrt(((IFile) target).getFullPath().toOSString()) ? 
+				new AddJrtToIndex((IFile) target, null, this, updateIndex) :
+					new AddJarFileToIndex((IFile) target, null, this, updateIndex);
 	} else if (target instanceof File) {
-		request = new AddJarFileToIndex(containerPath, null, this, updateIndex);
+		request = getRequest(target, containerPath, null, this, updateIndex);
 	}
 	if (request != null)
 		request(request);
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexerRequestor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexerRequestor.java
index 77ba0b3..f446204 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexerRequestor.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexerRequestor.java
@@ -321,6 +321,33 @@
 	addDefaultConstructorIfNecessary(typeInfo);
 	pushTypeName(typeInfo.name);
 }
+
+public void enterModule(ModuleInfo moduleInfo) {
+	this.indexer.addModuleDeclaration(moduleInfo.moduleName);
+	if (moduleInfo.requires != null) {
+		for (ISourceElementRequestor.RequiresInfo req : moduleInfo.requires) {
+			if (req == null || req.moduleName == null || req.moduleName.equals(CharOperation.NO_CHAR)) continue;
+			this.indexer.addModuleReference(req.moduleName);
+		}
+	}
+	enterPackageVisibilityInfo(moduleInfo.exports);
+	enterPackageVisibilityInfo(moduleInfo.opens); 
+	/* note: provides and uses directives processed automatically on IParser (SEParser) */
+}
+private void enterPackageVisibilityInfo(ISourceElementRequestor.PackageExportInfo[] packInfos) {
+	if (packInfos == null)
+		return;
+	for (ISourceElementRequestor.PackageExportInfo packInfo : packInfos) {
+		if (packInfo == null || packInfo.pkgName == null || packInfo.pkgName.equals(CharOperation.NO_CHAR)) continue;
+		this.indexer.addModuleExportedPackages(packInfo.pkgName);
+		char[][] tgts = packInfo.targets;
+		if (tgts == null || tgts.equals(CharOperation.NO_CHAR_CHAR)) continue;
+		for (char[] tgt : tgts) {
+			if (tgt != null && !tgt.equals(CharOperation.NO_CHAR)) 
+				this.indexer.addModuleReference(tgt);
+		}
+	}
+}
 /**
  * @see ISourceElementRequestor#enterMethod(ISourceElementRequestor.MethodInfo)
  */
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java
index 48e2819..1cd4db2 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java
@@ -34,7 +34,8 @@
 	TagBits.AnnotationForLocalVariable |
 	TagBits.AnnotationForField |
 	TagBits.AnnotationForConstructor |
-	TagBits.AnnotationForAnnotationType;
+	TagBits.AnnotationForAnnotationType |
+	TagBits.AnnotationForModule;
 private static final char[] JAVA_LANG_ANNOTATION_ELEMENTTYPE = CharOperation.concatWith(TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE, '.');
 public static char[] convertClassFileFormat(char[] name) {
 	return CharOperation.replaceOnCopy(name, '/', '.');
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClasspathSourceDirectory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClasspathSourceDirectory.java
index 64ac3ce..eb93f83 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClasspathSourceDirectory.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClasspathSourceDirectory.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -23,6 +23,7 @@
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+import org.eclipse.jdt.internal.compiler.env.IModulePathEntry;
 import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
 import org.eclipse.jdt.internal.core.JavaModelManager;
 import org.eclipse.jdt.internal.core.builder.ClasspathLocation;
@@ -30,7 +31,7 @@
 import org.eclipse.jdt.internal.core.util.Util;
 
 @SuppressWarnings("rawtypes")
-public class ClasspathSourceDirectory extends ClasspathLocation {
+public class ClasspathSourceDirectory extends ClasspathLocation implements IModulePathEntry {
 
 	IContainer sourceFolder;
 	SimpleLookupTable directoryCache;
@@ -106,12 +107,16 @@
 	return this.sourceFolder.equals(((ClasspathSourceDirectory) o).sourceFolder);
 } 
 
-public NameEnvironmentAnswer findClass(String sourceFileWithoutExtension, String qualifiedPackageName, String qualifiedSourceFileWithoutExtension) {
+public NameEnvironmentAnswer findClass(String typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName, boolean asBinaryOnly) {
+	return findClass(typeName, qualifiedPackageName, moduleName, qualifiedBinaryFileName);
+}
+public NameEnvironmentAnswer findClass(String sourceFileWithoutExtension, String qualifiedPackageName, String moduleName, String qualifiedSourceFileWithoutExtension) {
 	SimpleLookupTable dirTable = directoryTable(qualifiedPackageName);
 	if (dirTable != null && dirTable.elementSize > 0) {
 		IFile file = (IFile) dirTable.get(sourceFileWithoutExtension);
 		if (file != null) {
-			return new NameEnvironmentAnswer(new ResourceCompilationUnit(file), null /* no access restriction */);
+			return new NameEnvironmentAnswer(new ResourceCompilationUnit(file, 
+					this.module == null ? null : this.module.name()), null /* no access restriction */);
 		}
 	}
 	return null;
@@ -125,9 +130,20 @@
 	return this.sourceFolder == null ? super.hashCode() : this.sourceFolder.hashCode();
 }
 
-public boolean isPackage(String qualifiedPackageName) {
+public boolean isPackage(String qualifiedPackageName, String moduleName) {
+	if (moduleName != null) {
+		if (this.module == null || !moduleName.equals(String.valueOf(this.module.name())))
+			return false;
+	}
 	return directoryTable(qualifiedPackageName) != null;
 }
+@Override
+public boolean hasCompilationUnit(String qualifiedPackageName, String moduleName) {
+	SimpleLookupTable dirTable = directoryTable(qualifiedPackageName);
+	if (dirTable != null && dirTable.elementSize > 0)
+		return true;
+	return false;
+}
 
 public void reset() {
 	this.directoryCache = new SimpleLookupTable(5);
@@ -140,5 +156,4 @@
 public String debugPathString() {
 	return this.sourceFolder.getFullPath().toString();
 }
-
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java
index 9077075..7f9b1d1 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2016 Google, Inc and others.
+ * Copyright (c) 2015, 2017 Google, Inc 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
@@ -195,6 +195,7 @@
 					answer = location.findClass(
 						sourceFileName, // doesn't include the file extension
 						qPackageName,
+						null, // TODO(SHMOD): don't have a module name, but while looking in unindexed classpath locations, this is probably OK
 						qSourceFileName);  // doesn't include the file extension
 				}
 			} else {
@@ -212,6 +213,7 @@
 					location.findClass(
 						binaryFileName,
 						qPackageName,
+						null,  // TODO(SHMOD): don't have a module name, but while looking in unindexed classpath locations, this is probably OK
 						qBinaryFileName);
 			}
 			if (answer != null) {
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java
index 0c7de15..4847167 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.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
@@ -15,43 +15,64 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IModuleDescription;
 import org.eclipse.jdt.core.IPackageDeclaration;
 import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
-import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.env.IModule;
+import org.eclipse.jdt.internal.compiler.env.IModuleAwareNameEnvironment;
 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
 import org.eclipse.jdt.internal.core.ClasspathEntry;
+import org.eclipse.jdt.internal.core.JavaElement;
+import org.eclipse.jdt.internal.core.JavaElementRequestor;
 import org.eclipse.jdt.internal.core.JavaModel;
 import org.eclipse.jdt.internal.core.JavaModelManager;
 import org.eclipse.jdt.internal.core.JavaProject;
+import org.eclipse.jdt.internal.core.ModuleDescriptionInfo;
+import org.eclipse.jdt.internal.core.NameLookup;
 import org.eclipse.jdt.internal.core.PackageFragmentRoot;
 import org.eclipse.jdt.internal.core.builder.ClasspathJar;
+import org.eclipse.jdt.internal.core.builder.ClasspathJrt;
 import org.eclipse.jdt.internal.core.builder.ClasspathLocation;
 import org.eclipse.jdt.internal.core.util.Util;
 
 /*
  * A name environment based on the classpath of a Java project.
  */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public class JavaSearchNameEnvironment implements INameEnvironment, SuffixConstants {
+public class JavaSearchNameEnvironment implements IModuleAwareNameEnvironment, SuffixConstants {
 
 	LinkedHashSet<ClasspathLocation> locationSet;
-	
+	Map<String, IModuleDescription> modules;
+	private boolean modulesComputed = false;
+	Map<String,ClasspathLocation> moduleLocations;
+	Map<String,LinkedHashSet<ClasspathLocation>> moduleToClassPathLocations;
+
 	/*
 	 * A map from the fully qualified slash-separated name of the main type (String) to the working copy
 	 */
 	Map<String, org.eclipse.jdt.core.ICompilationUnit> workingCopies;
 
 public JavaSearchNameEnvironment(IJavaProject javaProject, org.eclipse.jdt.core.ICompilationUnit[] copies) {
+	if (CompilerOptions.versionToJdkLevel(javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true)) >= ClassFileConstants.JDK9) {
+		this.moduleLocations = new HashMap<>();
+		this.moduleToClassPathLocations = new HashMap<>();
+	}
+	this.modules = new HashMap<>();
 	this.locationSet = computeClasspathLocations((JavaProject) javaProject);
 	this.workingCopies = getWorkingCopyMap(copies);
 }
@@ -70,6 +91,7 @@
 				String mainTypeName = Util.getNameWithoutJavaLikeExtension(cuName);
 				String qualifiedMainTypeName = pkg.length() == 0 ? mainTypeName : pkg.replace('.', '/') + '/' + mainTypeName;
 				result.put(qualifiedMainTypeName, workingCopy);
+				// TODO : JAVA 9 - module-info.java has the same name across modules - Any issues here?
 			}
 		}
 	} catch (JavaModelException e) {
@@ -95,23 +117,52 @@
 	} catch (JavaModelException e) {
 		return null;// project doesn't exist
 	}
+	IModuleDescription imd = null;
+	try {
+		imd = javaProject.getModuleDescription();
+	} catch (JavaModelException e) {
+		// e.printStackTrace(); // ignore
+	}
+
 	LinkedHashSet<ClasspathLocation> locations = new LinkedHashSet<ClasspathLocation>();
 	int length = roots.length;
 	JavaModelManager manager = JavaModelManager.getJavaModelManager();
 	for (int i = 0; i < length; i++) {
-		ClasspathLocation cp = mapToClassPathLocation(manager, (PackageFragmentRoot) roots[i]);
+		ClasspathLocation cp = mapToClassPathLocation(manager, (PackageFragmentRoot) roots[i], imd);
 		if (cp != null) locations.add(cp);
 	}
 	return locations;
 }
 
-private ClasspathLocation mapToClassPathLocation( JavaModelManager manager, PackageFragmentRoot root) {
+private void computeModules() {
+	if (!this.modulesComputed) {
+		this.modulesComputed = true;
+		JavaElementRequestor requestor = new JavaElementRequestor();
+		try {
+			JavaModelManager.getModulePathManager().seekModule(CharOperation.ALL_PREFIX, true, requestor);
+			IModuleDescription[] mods = requestor.getModules();
+			for (IModuleDescription mod : mods) {
+				this.modules.putIfAbsent(mod.getElementName(), mod);
+			}
+		} catch (JavaModelException e) {
+			// do nothing
+		}
+	}
+}
+
+private ClasspathLocation mapToClassPathLocation(JavaModelManager manager, PackageFragmentRoot root, IModuleDescription defaultModule) {
 	ClasspathLocation cp = null;
 	IPath path = root.getPath();
 	try {
 		if (root.isArchive()) {
 			ClasspathEntry rawClasspathEntry = (ClasspathEntry) root.getRawClasspathEntry();
-			cp = new ClasspathJar(manager.getZipFile(path), rawClasspathEntry.getAccessRuleSet(), ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, ((IJavaProject)root.getParent()).getProject(), true));
+			cp = JavaModelManager.isJrt(path) ? 
+					new ClasspathJrt(path.toOSString(), 
+							ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, ((IJavaProject)root.getParent()).getProject(), true)) :
+						new ClasspathJar(manager.getZipFile(path), rawClasspathEntry.getAccessRuleSet(),
+								ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry,
+										((IJavaProject) root.getParent()).getProject(), true),
+								rawClasspathEntry.isModular());
 		} else {
 			Object target = JavaModel.getTarget(path, true);
 			if (target != null) {
@@ -120,7 +171,8 @@
 				} else {
 					ClasspathEntry rawClasspathEntry = (ClasspathEntry) root.getRawClasspathEntry();
 					cp = ClasspathLocation.forBinaryFolder((IContainer) target, false, rawClasspathEntry.getAccessRuleSet(),
-														ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, ((IJavaProject)root.getParent()).getProject(), true));
+														ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, ((IJavaProject)root.getParent()).getProject(), true),
+														rawClasspathEntry.isModular());
 				}
 			}
 		}
@@ -128,18 +180,53 @@
 		// problem opening zip file or getting root kind
 		// consider root corrupt and ignore
 	}
+	IModuleDescription imd = root.getModuleDescription();
+	if (imd != null) {
+		String moduleName = addModuleClassPathInfo(cp, imd);
+		if (moduleName != null)
+			this.modules.put(moduleName, imd);
+		if (this.moduleLocations != null)
+			this.moduleLocations.put(moduleName, cp);
+	} else if (defaultModule != null) {
+		addModuleClassPathInfo(cp, defaultModule);
+	}
 	return cp;
 }
+private String addModuleClassPathInfo(ClasspathLocation cp, IModuleDescription imd) {
+	IModule mod = NameLookup.getModuleDescriptionInfo(imd);
+	String moduleName = null;
+	if (mod != null) {
+		char[] name = mod.name();
+		if (name != null) {
+			moduleName = new String(name);
+			cp.setModule(mod);
+			addClassPathToModule(moduleName, cp);
+		}
+	}
+	return moduleName;
+}
+private void addClassPathToModule(String moduleName, ClasspathLocation cp) {
+	if (this.moduleToClassPathLocations != null) {
+		LinkedHashSet<ClasspathLocation> l = this.moduleToClassPathLocations.get(moduleName);
+		if (l == null) {
+			l = new LinkedHashSet<>();
+			this.moduleToClassPathLocations.put(moduleName, l);
+		}
+		l.add(cp);
+	}
+}
 
-private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName) {
-	String 
-		binaryFileName = null, qBinaryFileName = null, 
+private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName, LookupStrategy strategy, /*@Nullable*/String moduleName) {
+	String
+		binaryFileName = null, qBinaryFileName = null,
 		sourceFileName = null, qSourceFileName = null,
 		qPackageName = null;
 	NameEnvironmentAnswer suggestedAnswer = null;
-	Iterator <ClasspathLocation> iter = this.locationSet.iterator();
+	Iterator<ClasspathLocation> iter = getLocationsFor(moduleName);
 	while (iter.hasNext()) {
 		ClasspathLocation location = iter.next();
+		if (!strategy.matches(location, ClasspathLocation::hasModule))
+			continue;
 		NameEnvironmentAnswer answer;
 		if (location instanceof ClasspathSourceDirectory) {
 			if (sourceFileName == null) {
@@ -159,6 +246,7 @@
 				answer = location.findClass(
 					sourceFileName, // doesn't include the file extension
 					qPackageName,
+					moduleName,
 					qSourceFileName);  // doesn't include the file extension
 			}
 		} else {
@@ -172,10 +260,11 @@
 					binaryFileName = qBinaryFileName.substring(typeNameStart);
 				}
 			}
-			answer = 
+			answer =
 				location.findClass(
-					binaryFileName, 
-					qPackageName, 
+					binaryFileName,
+					qPackageName,
+					moduleName,
 					qBinaryFileName);
 		}
 		if (answer != null) {
@@ -193,32 +282,109 @@
 	return null;
 }
 
-public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
+private Iterator<ClasspathLocation> getLocationsFor(/*@Nullable*/String moduleName) {
+	if (moduleName != null) {
+		LinkedHashSet<ClasspathLocation> l = this.moduleToClassPathLocations.get(moduleName);
+		if (l != null && l.size() > 0)
+			return l.iterator();
+	}
+	return this.locationSet.iterator();
+}
+
+@Override
+public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName, char[] moduleName) {
 	if (typeName != null)
 		return findClass(
 			new String(CharOperation.concatWith(packageName, typeName, '/')),
-			typeName);
+			typeName,
+			LookupStrategy.get(moduleName),
+			LookupStrategy.getStringName(moduleName));
 	return null;
 }
 
-public NameEnvironmentAnswer findType(char[][] compoundName) {
+@Override
+public NameEnvironmentAnswer findType(char[][] compoundName, char[] moduleName) {
 	if (compoundName != null)
 		return findClass(
 			new String(CharOperation.concatWith(compoundName, '/')),
-			compoundName[compoundName.length - 1]);
+			compoundName[compoundName.length - 1],
+			LookupStrategy.get(moduleName),
+			LookupStrategy.getStringName(moduleName));
 	return null;
 }
 
-public boolean isPackage(char[][] compoundName, char[] packageName) {
-	return isPackage(new String(CharOperation.concatWith(compoundName, packageName, '/')));
+@Override
+public char[][] getModulesDeclaringPackage(char[][] parentPackageName, char[] packageName, char[] moduleName) {
+	String qualifiedPackageName = String.valueOf(CharOperation.concatWith(parentPackageName, packageName, '/'));
+	LookupStrategy strategy = LookupStrategy.get(moduleName);
+	if (strategy == LookupStrategy.Named) {
+		if (this.moduleToClassPathLocations != null) {
+			String moduleNameString = String.valueOf(moduleName);
+			LinkedHashSet<ClasspathLocation> cpl = this.moduleToClassPathLocations.get(moduleNameString);
+			List<ClasspathLocation> l = cpl != null ? cpl.stream().collect(Collectors.toList()): null;
+			if (l != null) {
+				for (ClasspathLocation cp : l) {
+					if (cp.isPackage(qualifiedPackageName, moduleNameString))
+						return new char[][] { moduleName };
+				}
+			}
+		}
+		return null;
+	}
+	char[][] moduleNames = CharOperation.NO_CHAR_CHAR;
+	for (ClasspathLocation location : this.locationSet) {
+		if (strategy.matches(location, ClasspathLocation::hasModule) ) {
+			if (location.isPackage(qualifiedPackageName, null)) {
+				char[][] mNames = location.getModulesDeclaringPackage(qualifiedPackageName, null);
+				if (mNames == null || mNames.length == 0) continue;
+				moduleNames = CharOperation.arrayConcat(moduleNames, mNames);
+			}
+		}
+	}
+	return moduleNames == CharOperation.NO_CHAR_CHAR ? null : moduleNames;
 }
 
-public boolean isPackage(String qualifiedPackageName) {
-	Iterator<ClasspathLocation> iter = this.locationSet.iterator();
-	while (iter.hasNext()) {
-		if (iter.next().isPackage(qualifiedPackageName)) return true;
+@Override
+public boolean hasCompilationUnit(char[][] qualifiedPackageName, char[] moduleName, boolean checkCUs) {
+	String qualifiedPackageNameString = String.valueOf(CharOperation.concatWith(qualifiedPackageName, '/'));
+	LookupStrategy strategy = LookupStrategy.get(moduleName);
+	String moduleNameString = LookupStrategy.getStringName(moduleName);
+	if (strategy == LookupStrategy.Named) {
+		if (this.moduleLocations != null) {
+			ClasspathLocation location = this.moduleLocations.get(moduleNameString);
+			if (location != null)
+				return location.hasCompilationUnit(qualifiedPackageNameString, moduleNameString);
+		}
+	} else {
+		for (ClasspathLocation location : this.locationSet) {
+			if (strategy.matches(location, ClasspathLocation::hasModule) )
+				if (location.hasCompilationUnit(qualifiedPackageNameString, moduleNameString))
+					return true;
+		}
 	}
 	return false;
 }
 
+@Override
+public IModule getModule(char[] moduleName) {
+	computeModules();
+	IModuleDescription moduleDesc = this.modules.get(new String(moduleName));
+	IModule module = null;
+	try {
+		if (moduleDesc != null)
+			module =  ((ModuleDescriptionInfo)((JavaElement) moduleDesc).getElementInfo());
+	} catch (JavaModelException e) {
+		// do nothing
+	}
+	return module;
+}
+
+@Override
+public char[][] getAllAutomaticModules() {
+	if (this.moduleLocations == null || this.moduleLocations.size() == 0)
+		return CharOperation.NO_CHAR_CHAR;
+	Set<char[]> set = this.moduleLocations.values().stream().map(e -> e.getModule()).filter(m -> m != null && m.isAutomatic())
+			.map(m -> m.name()).collect(Collectors.toSet());
+	return set.toArray(new char[set.size()][]);
+}
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
index b82733a..c4e23a8 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.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
@@ -14,6 +14,7 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -37,11 +38,14 @@
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IMember;
 import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IModuleDescription;
 import org.eclipse.jdt.core.IOpenable;
+import org.eclipse.jdt.core.IOrdinaryClassFile;
 import org.eclipse.jdt.core.IPackageFragment;
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.ISourceRange;
 import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeRoot;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jdt.core.compiler.*;
@@ -75,6 +79,7 @@
 import org.eclipse.jdt.internal.core.JavaProject;
 import org.eclipse.jdt.internal.core.LambdaFactory;
 import org.eclipse.jdt.internal.core.LocalVariable;
+import org.eclipse.jdt.internal.core.ModularClassFile;
 import org.eclipse.jdt.internal.core.NameLookup;
 import org.eclipse.jdt.internal.core.Openable;
 import org.eclipse.jdt.internal.core.PackageFragment;
@@ -307,24 +312,27 @@
 		if (!root.isArchive())
 			return Util.newClassFileReader(((JavaElement) type).resource());
 
-		ZipFile zipFile = null;
-		try {
-			IPath zipPath = root.getPath();
-			if (JavaModelManager.ZIP_ACCESS_VERBOSE)
-				System.out.println("(" + Thread.currentThread() + ") [MatchLocator.classFileReader()] Creating ZipFile on " + zipPath); //$NON-NLS-1$	//$NON-NLS-2$
-			zipFile = manager.getZipFile(zipPath);
+		String rootPath = root.getPath().toOSString();
+		if (org.eclipse.jdt.internal.compiler.util.Util.isJrt(rootPath)) {
 			String classFileName = classFile.getElementName();
 			String path = Util.concatWith(pkg.names, classFileName, '/');
-			return ClassFileReader.read(zipFile, path);
-		} finally {
-			manager.closeZipFile(zipFile);
+			return ClassFileReader.readFromJrt(new File(rootPath), null, path);
+		} else {
+			ZipFile zipFile = null;
+			try {
+				IPath zipPath = root.getPath();
+				if (JavaModelManager.ZIP_ACCESS_VERBOSE)
+					System.out.println("(" + Thread.currentThread() + ") [MatchLocator.classFileReader()] Creating ZipFile on " + zipPath); //$NON-NLS-1$	//$NON-NLS-2$
+				zipFile = manager.getZipFile(zipPath);
+				String classFileName = classFile.getElementName();
+				String path = Util.concatWith(pkg.names, classFileName, '/');
+				return ClassFileReader.read(zipFile, path);
+			} finally {
+				manager.closeZipFile(zipFile);
+			}
 		}
-	} catch (ClassFormatException e) {
+	} catch (ClassFormatException | CoreException | IOException e) {
 		// invalid class file: return null
-	} catch (CoreException e) {
-		// cannot read class file: return null
-	} catch (IOException e) {
-		// cannot read class file: return null
 	}
 	return null;
 }
@@ -820,6 +828,8 @@
 	if (openable instanceof CompilationUnit)
 		return ((CompilationUnit) openable).getImport(new String(importName));
 
+	if (openable instanceof ModularClassFile)
+		return openable;
 	// binary types do not contain import statements so just answer the top-level type as the element
 	IType binaryType = ((ClassFile) openable).getType();
 	String typeName = binaryType.getElementName();
@@ -855,7 +865,7 @@
 
 	// type name may be null for anonymous (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=164791)
 	String classFileName = simpleTypeName.length() == 0 ? binaryTypeQualifiedName : simpleTypeName;
-	IClassFile classFile = binaryType.getPackageFragment().getClassFile(classFileName + SuffixConstants.SUFFIX_STRING_class);
+	IOrdinaryClassFile classFile = binaryType.getPackageFragment().getOrdinaryClassFile(classFileName + SuffixConstants.SUFFIX_STRING_class);
 	return classFile.getType();
 }
 //{ObjectTeams: accessible for ReferenceToTeamLocator:
@@ -1012,7 +1022,7 @@
 	TypeBinding typeBinding = this.unitScope.getType(compoundName, compoundName.length);
 	this.unitScopeTypeBinding = typeBinding; //cache.
 	if (typeBinding == null || !typeBinding.isValidBinding()) {
-		typeBinding = this.lookupEnvironment.getType(compoundName);
+		typeBinding = this.lookupEnvironment.getType(compoundName, this.unitScope.module());
 	}
 	this.bindings.put(typeKey, typeBinding);
 	return typeBinding != null && typeBinding.isValidBinding() ? typeBinding : null;
@@ -1194,6 +1204,13 @@
 	this.bindings.put(methodPattern, result != null ? result : new ProblemMethodBinding(methodPattern.selector, null, ProblemReasons.NotFound));
 	return result;
 }
+private boolean matchParams(MethodPattern methodPattern, int index, TypeBinding binding) {
+	char[] qualifier = CharOperation.concat(methodPattern.parameterQualifications[index], methodPattern.parameterSimpleNames[index], '.');
+	int offset = (qualifier.length > 0 && qualifier[0] == '*') ? 1 : 0;
+	String s1 = new String(qualifier, offset, qualifier.length - offset);
+	char[] s2 = CharOperation.concat(binding.qualifiedPackageName(), binding.qualifiedSourceName(), '.');
+	return new String(s2).endsWith(s1);
+}
 
 private MethodBinding getMethodBinding(MethodPattern methodPattern, TypeBinding declaringTypeBinding) {
 	MethodBinding result;
@@ -1214,7 +1231,8 @@
 		boolean found = false;
 		if (methodParameters != null && paramLength == paramTypeslength) {
 			for (int p=0; p<paramLength; p++) {
-				if (CharOperation.equals(methodParameters[p].sourceName(), parameterTypes[p])) {
+				TypeBinding parameter = methodParameters[p];
+				if (matchParams(methodPattern, p, parameter)) {
 					// param erasure match
 					found = true;
 				} else {
@@ -1763,6 +1781,8 @@
 			return new PackageDeclarationMatch(element, accuracy, offset, length, participant, resource);
 		case IJavaElement.TYPE_PARAMETER:
 			return new TypeParameterDeclarationMatch(element, accuracy, offset, length, participant, resource);
+		case IJavaElement.JAVA_MODULE:
+			return new ModuleDeclarationMatch(binding == null ? element : ((JavaElement) element).resolved(binding), accuracy, offset, length, participant, resource);
 		default:
 			return null;
 	}
@@ -1869,7 +1889,7 @@
 		ASTNode reference) {
 	SearchParticipant participant = getParticipant();
 	IResource resource = this.currentPossibleMatch.resource;
-	boolean insideDocComment = (reference.bits & ASTNode.InsideJavadoc) != 0;
+	boolean insideDocComment = reference != null && (reference.bits & ASTNode.InsideJavadoc) != 0;
 	return new PackageReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource);
 }
 
@@ -1895,7 +1915,7 @@
 		ASTNode reference) {
 	SearchParticipant participant = getParticipant();
 	IResource resource = this.currentPossibleMatch.resource;
-	boolean insideDocComment = (reference.bits & ASTNode.InsideJavadoc) != 0;
+	boolean insideDocComment = reference != null && (reference.bits & ASTNode.InsideJavadoc) != 0;
 	if (enclosingBinding != null)
 		enclosingElement = ((JavaElement) enclosingElement).resolved(enclosingBinding);
 	return new TypeReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource);
@@ -1909,6 +1929,28 @@
 	return newTypeReferenceMatch(enclosingElement, enclosingBinding, accuracy, reference.sourceStart, reference.sourceEnd-reference.sourceStart+1, reference);
 }
 
+public ModuleReferenceMatch newModuleReferenceMatch(
+		IJavaElement enclosingElement,
+		Binding enclosingBinding,
+		int accuracy,
+		int offset,
+		int length,
+		ASTNode reference) {
+	SearchParticipant participant = getParticipant();
+	IResource resource = this.currentPossibleMatch.resource;
+	boolean insideDocComment = reference != null ? (reference.bits & ASTNode.InsideJavadoc) != 0 : false;
+	if (enclosingBinding != null)
+		enclosingElement = ((JavaElement) enclosingElement).resolved(enclosingBinding);
+	return new ModuleReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource);
+}
+
+public ModuleReferenceMatch newModuleReferenceMatch(
+		IJavaElement enclosingElement,
+		Binding enclosingBinding,
+		int accuracy,
+		ASTNode reference) {
+	return newModuleReferenceMatch(enclosingElement, enclosingBinding, accuracy, reference.sourceStart, reference.sourceEnd-reference.sourceStart+1, reference);
+}
 /**
  * Add the possibleMatch to the loop
  *  ->  build compilation unit declarations, their bindings and record their results.
@@ -1935,7 +1977,11 @@
 		CompilationResult unitResult = new CompilationResult(possibleMatch, 1, 1, this.options.maxProblemsPerUnit);
 		CompilationUnitDeclaration parsedUnit = this.parser.dietParse(possibleMatch, unitResult);
 		if (parsedUnit != null) {
-			if (!parsedUnit.isEmpty()) {
+			if (parsedUnit.isModuleInfo()) {
+				if (mustResolve) {
+					this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
+				}				
+			} else if (!parsedUnit.isEmpty()) {
 				if (mustResolve) {
 					this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
 				}
@@ -2001,8 +2047,20 @@
 						this.patternLocator.mayBeGeneric = mayBeGeneric;
 					}
 				}
+			} else if (this.currentPossibleMatch.openable instanceof ModularClassFile &&
+					unit.moduleDeclaration == null) { // no source
+				boolean mayBeGeneric = this.patternLocator.mayBeGeneric;
+				this.patternLocator.mayBeGeneric = false; // there's no longer generic in class files
+				try {
+					new ModularClassFileMatchLocator().locateMatches(this, (ModularClassFile) this.currentPossibleMatch.openable);
+				}
+				finally {
+					this.patternLocator.mayBeGeneric = mayBeGeneric;
+				}
+				return;
 			}
-			return;
+			if (!unit.isModuleInfo())
+				return;
 		}
 		if (hasAlreadyDefinedType(unit)) return; // skip type has it is hidden so not visible
 
@@ -2036,6 +2094,13 @@
   :giro */
 				Dependencies.ensureState(unit, ITranslationStates.STATE_LATE_ELEMENTS_COPIED);
 // km (based on SH,carp)}
+			} else if (unit.isModuleInfo()) {
+				if (BasicSearchEngine.VERBOSE)
+					System.out.println("Resolving " + this.currentPossibleMatch.openable.toStringWithAncestors()); //$NON-NLS-1$
+				this.lookupEnvironment.unitBeingCompleted = unit;
+				if (unit.scope != null && unit.moduleDeclaration != null) {
+					unit.moduleDeclaration.resolveTypeDirectives(unit.scope);
+				}
 			}
 		}
 		reportMatching(unit, mustResolve);
@@ -2498,7 +2563,6 @@
 	SearchMatch match = newDeclarationMatch(binaryMember, binaryMemberBinding, accuracy, range.getOffset(), range.getLength(), getParticipant(), resource);
 	report(match);
 }
-
 protected void reportMatching(LambdaExpression lambdaExpression,  IJavaElement parent, int accuracy, MatchingNodeSet nodeSet, boolean typeInHierarchy) throws CoreException {
 	IJavaElement enclosingElement = null;
 	// Report the lambda declaration itself.
@@ -2794,10 +2858,16 @@
 				if (this.hierarchyResolver != null) continue;
 
 				ImportReference importRef = (ImportReference) node;
-				Binding binding = (importRef.bits & ASTNode.OnDemand) != 0
+				boolean inModule = (importRef.bits & ASTNode.inModule) != 0;
+				boolean getOnDemand = (importRef.bits & ASTNode.OnDemand) != 0 || inModule;
+				Binding binding = getOnDemand
 					? this.unitScope.getImport(CharOperation.subarray(importRef.tokens, 0, importRef.tokens.length), true, importRef.isStatic())
 					: this.unitScope.getImport(importRef.tokens, false, importRef.isStatic());
-				this.patternLocator.matchLevelAndReportImportRef(importRef, binding, this);
+				if (inModule) {
+					nodeSet.addMatch(node, this.patternLocator.resolveLevel(binding)); // report all module-info together
+				} else {
+					this.patternLocator.matchLevelAndReportImportRef(importRef, binding, this);
+				}
 			} else {
 				nodeSet.addMatch(node, this.patternLocator.resolveLevel(node));
 			}
@@ -2876,6 +2946,11 @@
 			this.inTypeOccurrencesCounts = new HashtableOfIntValues();
 			reportMatching(type, null, accuracy, nodeSet, 1);
 		}
+	} else if (unit.moduleDeclaration != null) {
+		ModuleDeclaration mod = unit.moduleDeclaration;
+		Integer level = (Integer) nodeSet.matchingNodes.removeKey(mod);
+		int accuracy = (level != null && matchedUnitContainer) ? level.intValue() : -1;
+		reportMatching(mod, null, accuracy, nodeSet, 1);
 	}
 
 	// Clear handle cache
@@ -3012,6 +3087,109 @@
 	}
 }
 /**
+ * Visit the given module declaration and report the nodes that match exactly the
+ * search pattern (i.e. the ones in the matching nodes set)
+ */
+protected void reportMatching(ModuleDeclaration module, IJavaElement parent, int accuracy, MatchingNodeSet nodeSet, int occurrenceCount) throws CoreException {
+	IModuleDescription moduleDesc =  null;
+	Openable openable = this.currentPossibleMatch.openable;
+	if (openable instanceof ITypeRoot) {
+		ITypeRoot typeRoot = (ITypeRoot) openable;
+		try {
+			moduleDesc =  typeRoot.getModule();
+		} catch (JavaModelException e) {
+			// do nothing
+		}
+	}
+	if (moduleDesc == null) // could theoretically happen if openable is ICompilationUnit, but logically having a module should prevent this from happening
+		return;
+	if (accuracy > -1) { // report module declaration
+		SearchMatch match = this.patternLocator.newDeclarationMatch(module, moduleDesc, module.binding, accuracy, module.moduleName.length, this);
+		report(match);
+	}
+	reportMatching(module.requires, module,  nodeSet, moduleDesc);
+	reportMatching(module.exports, nodeSet, moduleDesc);
+	reportMatching(module.opens, nodeSet, moduleDesc);
+	reportMatching(module.services, module, nodeSet, moduleDesc);
+	reportMatching(module.uses, module, nodeSet, moduleDesc);
+}
+
+private void reportMatching(RequiresStatement[] reqs, ModuleDeclaration module, MatchingNodeSet nodeSet, IModuleDescription moduleDesc) {
+	if (reqs == null || reqs.length == 0)
+		return;
+	try {
+		for (RequiresStatement req : reqs) {
+			Integer level = (Integer) nodeSet.matchingNodes.removeKey(req.module);
+			if (level != null) {
+				this.patternLocator.matchReportReference(req.module, moduleDesc, req.resolvedBinding, level.intValue(), this);
+			}
+		}
+	} catch (CoreException e) {
+		// do nothing
+	}
+}
+
+private void reportMatching(PackageVisibilityStatement[] psvs, MatchingNodeSet nodeSet, IModuleDescription moduleDesc)
+		throws JavaModelException, CoreException {
+	if (psvs != null && psvs.length > 0) {
+		for (PackageVisibilityStatement psv : psvs) {
+			ImportReference importRef = psv.pkgRef;
+			Integer level = (Integer) nodeSet.matchingNodes.removeKey(importRef);
+			if (level != null) {
+				Binding binding = this.unitScope.getImport(CharOperation.subarray(importRef.tokens, 0, importRef.tokens.length), true, false);
+				this.patternLocator.matchReportImportRef(importRef, binding, moduleDesc, level.intValue(), this);
+			}
+			ModuleReference[] tgts = psv.targets;
+			if (tgts == null || tgts.length == 0) continue;
+			for (ModuleReference tgt : tgts) {
+				level = (Integer) nodeSet.matchingNodes.removeKey(tgt);
+				if (level != null) {
+					this.patternLocator.matchReportReference(tgt, moduleDesc, tgt.resolve(this.unitScope), level.intValue(), this);
+				}
+			}
+		}
+	}
+}
+private void reportMatching(ProvidesStatement[] provides, ModuleDeclaration module, MatchingNodeSet nodeSet, IModuleDescription moduleDesc) throws JavaModelException, CoreException {
+	if (provides != null && provides.length > 0) {
+		for (ProvidesStatement service : provides) {
+			TypeReference intf = service.serviceInterface;
+			if (intf != null) {
+				Integer level = (Integer) nodeSet.matchingNodes.removeKey(intf);
+				if (level != null)
+					this.patternLocator.matchReportReference(intf, moduleDesc, null, null, module.binding, level.intValue(), this);
+			}
+			TypeReference[] impls = service.implementations;
+			for (TypeReference impl : impls) {
+				if (impl != null) {
+					Integer level = (Integer) nodeSet.matchingNodes.removeKey(impl);
+					if (level != null)
+						this.patternLocator.matchReportReference(impl, moduleDesc, null, null, module.binding, level.intValue(), this);
+				}
+			}
+		}
+	}
+}
+private void reportMatching(UsesStatement[] uses, ModuleDeclaration module, MatchingNodeSet nodeSet, IModuleDescription moduleDesc) {
+	if (uses != null && uses.length > 0) {
+		try {
+			for (UsesStatement service : uses) {
+				TypeReference intf = service.serviceInterface;
+				if (intf != null) {
+					Integer level = (Integer) nodeSet.matchingNodes.removeKey(intf);
+					if (level != null) {
+						this.patternLocator.matchReportReference(intf, moduleDesc, null, null, module.binding, level.intValue(), this);
+					}
+				}
+			}
+		} catch (CoreException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+}
+
+/**
  * Visit the given type declaration and report the nodes that match exactly the
  * search pattern (i.e. the ones in the matching nodes set)
  */
@@ -3056,7 +3234,7 @@
 				if ((type.bits & ASTNode.IsAnonymousType) != 0) {
 					if (fileName != null) {
 						if (fileName.endsWith("jar") || fileName.endsWith(SuffixConstants.SUFFIX_STRING_class)) { //$NON-NLS-1$
-							IClassFile classFile= binaryType.getPackageFragment().getClassFile(binaryType.getTypeQualifiedName() + 
+							IOrdinaryClassFile classFile= binaryType.getPackageFragment().getOrdinaryClassFile(binaryType.getTypeQualifiedName() + 
 									"$" + Integer.toString(occurrenceCount) + SuffixConstants.SUFFIX_STRING_class);//$NON-NLS-1$
 							anonType =  classFile.getType();
 						}
@@ -3065,7 +3243,7 @@
 					}
 				}
 			}
-			enclosingElement = anonType != null ? anonType : ((IClassFile)this.currentPossibleMatch.openable).getType() ;
+			enclosingElement = anonType != null ? anonType : ((IOrdinaryClassFile)this.currentPossibleMatch.openable).getType() ;
 		} else {
 			enclosingElement = member.getType(new String(type.name), occurrenceCount);
 		}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java
index ee77d19..b7f1693 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java
@@ -1,10 +1,10 @@
 /*******************************************************************************
- * 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
- * 
+ *
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Fraunhofer FIRST - extended API and implementation
@@ -378,6 +378,10 @@
 	super.consumeExplicitConstructorInvocationWithTypeArguments(flag, recFlag);
 	this.patternLocator.match(this.astStack[this.astPtr], this.nodeSet);
 }
+protected void consumeExportsHeader() {
+	super.consumeExportsHeader();
+	this.patternLocator.match(((ExportsStatement) this.astStack[this.astPtr]).pkgRef, this.nodeSet);
+}
 protected void consumeFieldAccess(boolean isSuperAccess) {
 	super.consumeFieldAccess(isSuperAccess);
 
@@ -652,6 +656,11 @@
 	}
 }
 
+@Override
+protected void consumeModuleHeader() {
+	super.consumeModuleHeader();
+	this.patternLocator.match(((ModuleDeclaration) this.astStack[this.astPtr]), this.nodeSet);
+}
 protected void consumeNormalAnnotation(boolean isTypeAnnotation) {
 	super.consumeNormalAnnotation(isTypeAnnotation);
 	if (this.patternFineGrain == 0 || (this.patternFineGrain & IJavaSearchConstants.ANNOTATION_TYPE_REFERENCE) != 0) {
@@ -673,7 +682,26 @@
 		}
 	}
 }
-
+@Override
+protected void consumeOpensHeader() {
+	super.consumeOpensHeader();
+	this.patternLocator.match(((OpensStatement) this.astStack[this.astPtr]).pkgRef, this.nodeSet);
+}
+@Override
+protected void consumeProvidesInterface() {
+	super.consumeProvidesInterface();
+	ProvidesStatement ref = (ProvidesStatement) this.astStack[this.astPtr];
+	this.patternLocator.match(ref.serviceInterface, this.nodeSet);
+}
+@Override
+protected void consumeProvidesStatement() {
+	super.consumeProvidesStatement();
+	ProvidesStatement ref = (ProvidesStatement) this.astStack[this.astPtr];
+	TypeReference[] impls = ref.implementations;
+	for (TypeReference impl : impls) {
+		this.patternLocator.match(impl, this.nodeSet);
+	}
+}
 protected void consumePrimaryNoNewArrayWithName() {
 	// PrimaryNoNewArray ::=  PushLPAREN Expression PushRPAREN
 	pushOnExpressionStack(getUnspecifiedReferenceOptimized());
@@ -713,6 +741,24 @@
 		this.patternLocator.match(annotation, this.nodeSet);
 	}
 }
+@Override
+protected void consumeSingleRequiresModuleName() {
+	super.consumeSingleRequiresModuleName();
+	RequiresStatement req = (RequiresStatement) this.astStack[this.astPtr];
+	this.patternLocator.match(req.module, this.nodeSet);
+}
+private void setTarget(boolean flag) {
+	if (this.patternLocator instanceof ModuleLocator) {
+		((ModuleLocator) this.patternLocator).target = flag;
+	}
+}
+@Override
+protected void consumeSingleTargetModuleName() {
+	super.consumeSingleTargetModuleName();
+	setTarget(true);
+	this.patternLocator.match((ModuleReference)this.astStack[this.astPtr], this.nodeSet);
+	setTarget(false);
+}
 
 protected void consumeStatementCatch() {
 	super.consumeStatementCatch();
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModularClassFileMatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModularClassFileMatchLocator.java
new file mode 100644
index 0000000..49be4f5
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModularClassFileMatchLocator.java
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ * 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
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core.search.matching;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IModuleDescription;
+import org.eclipse.jdt.core.search.ModuleReferenceMatch;
+import org.eclipse.jdt.core.search.PackageReferenceMatch;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.TypeReferenceMatch;
+import org.eclipse.jdt.internal.compiler.env.IBinaryModule;
+import org.eclipse.jdt.internal.compiler.env.IModule.IPackageExport;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.core.ModularClassFile;
+import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
+
+public class ModularClassFileMatchLocator implements IIndexConstants {
+
+	private IBinaryModule binaryModule;
+	private ModularClassFile modularClassFile;
+	private IModuleDescription moduleDesc;
+	private char[] moduleName;
+	private ModuleBinding module;
+
+	public void locateMatches(MatchLocator locator, ModularClassFile mClassFile) throws CoreException {
+		SearchPattern pattern = locator.pattern;
+		this.modularClassFile = mClassFile;
+		this.binaryModule = this.modularClassFile.getBinaryModuleInfo();
+		if (this.binaryModule == null) return;
+
+		// cache all the details
+		this.moduleDesc = mClassFile.getModule();
+		this.moduleName = this.binaryModule.name();
+		this.module = locator.lookupEnvironment.getModule(this.moduleName);
+
+		matchModuleDeclaration(pattern, locator);
+		matchModuleReferences(pattern, locator);
+		matchPackageReferences(pattern, locator);
+		matchTypeReferences(pattern, locator);
+	}
+	private void matchModuleDeclaration(SearchPattern pattern, MatchLocator locator) throws CoreException {
+		switch (pattern.kind) {
+			case MODULE_PATTERN:
+				break;
+			case OR_PATTERN:
+				SearchPattern[] patterns = ((OrPattern) pattern).patterns;
+				for (int i = 0, length = patterns.length; i < length; i++) {
+					SearchPattern p = patterns[i];
+					if (p.kind == MODULE_PATTERN)
+						matchModuleReferences(patterns[i], locator);
+				}
+				// $FALL-THROUGH$ - fall through default to return
+			default:
+				return;
+		}
+		ModulePattern modulePattern  = (ModulePattern) pattern;
+		if (!locator.patternLocator.matchesName(modulePattern.name, this.moduleName))
+			return;
+		ModuleBinding moduleBinding = null;
+		int level = PatternLocator.ACCURATE_MATCH;
+		if (locator.patternLocator.mustResolve) {
+			moduleBinding = locator.lookupEnvironment.getModule(this.moduleName);
+			level = locator.patternLocator.resolveLevel(moduleBinding);
+		}
+		if (level == PatternLocator.IMPOSSIBLE_MATCH)
+			return;
+		int accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE;
+		SearchMatch match = locator.newDeclarationMatch(this.moduleDesc, moduleBinding, accuracy, -1, 0);
+		locator.report(match);
+	}
+	private void matchModuleReferences(SearchPattern pattern, MatchLocator locator) throws CoreException {
+		// Only process Module patterns
+		switch (pattern.kind) {
+			case MODULE_PATTERN:
+				break;
+			case OR_PATTERN:
+				SearchPattern[] patterns = ((OrPattern) pattern).patterns;
+				for (int i = 0, length = patterns.length; i < length; i++) {
+					SearchPattern p = patterns[i];
+					if (p.kind == MODULE_PATTERN)
+						matchModuleReferences(patterns[i], locator);
+				}
+				// $FALL-THROUGH$ - fall through default to return
+			default:
+				return;
+		}
+		ModulePattern modulePattern  = (ModulePattern) pattern;
+		matchModuleReferences(locator, modulePattern, this.binaryModule.exports());
+		matchModuleReferences(locator, modulePattern, this.binaryModule.opens());
+		matchModuleReferences(locator, modulePattern, this.module.getAllRequiredModules());
+	}
+	private void matchModuleReference(MatchLocator locator, ModulePattern modulePattern,
+			char[][] modules, boolean isTarget) throws CoreException {
+		if (modules == null)
+			return;
+		for (char[] module1 : modules) {
+			if (module1 == null || module1.length == 0) continue;
+			if (!locator.patternLocator.matchesName(modulePattern.name, module1)) continue;
+			// no resolve for target modules - report accurate match else resolve
+			ModuleReferenceMatch match = locator.newModuleReferenceMatch(this.moduleDesc, null, isTarget ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE, -1, 0, null);
+			locator.report(match);
+		}
+	}
+	private void matchModuleReferences(MatchLocator locator, ModulePattern modulePattern,
+			IPackageExport[] pvs) throws CoreException {
+		if (pvs == null) return;
+		for (IPackageExport pv : pvs) {
+			matchModuleReference(locator, modulePattern, pv.targets(), true /* isTarget */);
+		}
+	}
+	private void matchModuleReferences(MatchLocator locator, ModulePattern modulePattern,
+			ModuleBinding[] refs) throws CoreException {
+		if (refs == null) return;
+		for (ModuleBinding ref : refs) {
+			char[] name = ref.name();
+			if (name == null) continue;
+			int level =  locator.patternLocator.resolveLevel(ref);
+			if (level == PatternLocator.IMPOSSIBLE_MATCH) continue;
+			int accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE;
+			ModuleReferenceMatch match = locator.newModuleReferenceMatch(this.moduleDesc, null, accuracy, -1, 0, null);
+			locator.report(match);
+		}
+	}
+	private void matchPackageReferences(SearchPattern pattern, MatchLocator locator) throws CoreException {
+		// Only process PackageReference patterns
+		switch (pattern.kind) {
+			case PKG_REF_PATTERN:
+				break;
+			case OR_PATTERN:
+				SearchPattern[] patterns = ((OrPattern) pattern).patterns;
+				for (int i = 0, length = patterns.length; i < length; i++) {
+					SearchPattern p = patterns[i];
+					if (p.kind == PKG_REF_PATTERN)
+						matchPackageReferences(patterns[i], locator);
+				}
+				// $FALL-THROUGH$ - fall through default to return
+			default:
+				return;
+		}
+		matchPackReferences(locator, (PackageReferencePattern) pattern, this.module.getExports());
+		matchPackReferences(locator, (PackageReferencePattern) pattern, this.module.getOpens());
+		matchPackReferences(locator, (PackageReferencePattern) pattern, this.module.getUses());
+		TypeBinding[] services = this.module.getServices();
+		if (services != null) {
+			matchPackReferences(locator, (PackageReferencePattern) pattern, services);
+			for (TypeBinding service : services) {
+				matchPackReferences(locator, (PackageReferencePattern) pattern, this.module.getImplementations(service));
+			}
+		}
+	}
+	private void matchPackReferences(MatchLocator locator, PackageReferencePattern packReferencePattern,
+			PackageBinding[] packBindings) throws CoreException {
+		if (packBindings == null) return;
+		for (PackageBinding pb : packBindings) {
+			reportPackageMatch(locator, pb);
+		}
+	}
+	private void reportPackageMatch(MatchLocator locator, PackageBinding packageBinding) throws CoreException{
+		if (packageBinding == null) return;
+		int level =  locator.patternLocator.resolveLevel(packageBinding);
+		if (level == PatternLocator.IMPOSSIBLE_MATCH) return;
+		int accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE;
+		PackageReferenceMatch match = locator.newPackageReferenceMatch(this.moduleDesc, accuracy, -1, 0, null);
+		locator.report(match);
+	}
+	private void matchPackReferences(MatchLocator locator, PackageReferencePattern packReferencePattern,
+			TypeBinding[] types) throws CoreException {
+		if (types == null) return;
+		for (TypeBinding type : types) {
+			reportPackageMatch(locator, type.getPackage());
+		}
+	}
+	private void matchTypeReferences(SearchPattern pattern, MatchLocator locator) throws CoreException {
+		// Only process TypeReference patterns
+		switch (pattern.kind) {
+			case TYPE_REF_PATTERN:
+				break;
+			case OR_PATTERN:
+				SearchPattern[] patterns = ((OrPattern) pattern).patterns;
+				for (int i = 0, length = patterns.length; i < length; i++) {
+					SearchPattern p = patterns[i];
+					if (p.kind == TYPE_REF_PATTERN)
+						matchTypeReferences(patterns[i], locator);
+				}
+				// $FALL-THROUGH$ - fall through default to return
+			default:
+				return;
+		}
+		matchTypeReferences(locator, (TypeReferencePattern) pattern, this.module.getUses());
+		TypeBinding[] services = this.module.getServices();
+		if (services != null) {
+			matchTypeReferences(locator, (TypeReferencePattern) pattern, services);
+			for (TypeBinding service : services) {
+				matchTypeReferences(locator, (TypeReferencePattern) pattern, this.module.getImplementations(service));
+			}
+		}
+	}
+	private void matchTypeReferences(MatchLocator locator, TypeReferencePattern typeReferencePattern,
+			TypeBinding[] types) throws CoreException {
+		if (types == null) 	return;
+		for (TypeBinding type : types) {
+			int level =  locator.patternLocator.resolveLevel(type);
+			if (level == PatternLocator.IMPOSSIBLE_MATCH) continue;
+			int accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE : SearchMatch.A_INACCURATE;
+			TypeReferenceMatch match = locator.newTypeReferenceMatch(this.moduleDesc, null, accuracy, -1, 0, null);
+			locator.report(match);
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModuleLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModuleLocator.java
new file mode 100644
index 0000000..183fa0f
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModuleLocator.java
@@ -0,0 +1,84 @@
+package org.eclipse.jdt.internal.core.search.matching;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+
+/*******************************************************************************
+ * 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
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     
+ *******************************************************************************/
+public class ModuleLocator extends PatternLocator {
+
+	private ModulePattern pattern;
+	/* package */ boolean target = false;
+
+	public ModuleLocator(ModulePattern pattern) {
+		super(pattern);
+		this.pattern = pattern;
+	}
+	public int match(ModuleDeclaration node, MatchingNodeSet nodeSet) {
+		if (!this.pattern.findDeclarations) return IMPOSSIBLE_MATCH;
+		if (!matchesName(this.pattern.name, node.moduleName)) return IMPOSSIBLE_MATCH;
+		nodeSet.mustResolve = true;
+		return nodeSet.addMatch(node, POSSIBLE_MATCH);
+	}
+	@Override
+	protected int match(ModuleReference node, MatchingNodeSet nodeSet) {
+		if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH;
+		if (!matchesName(this.pattern.name, node.moduleName)) return IMPOSSIBLE_MATCH;
+		if (this.target) {
+			return nodeSet.addMatch(node, ACCURATE_MATCH);
+		}
+		nodeSet.mustResolve = true;
+		return nodeSet.addMatch(node, POSSIBLE_MATCH);
+	}
+	@Override
+	protected int matchContainer() {
+		return COMPILATION_UNIT_CONTAINER;
+	}
+	public int resolveLevel(ASTNode possibleMatchingNode) {
+		if (this.pattern.findDeclarations && possibleMatchingNode instanceof ModuleDeclaration) {
+			return resolveLevel(((ModuleDeclaration) possibleMatchingNode).binding);
+		}
+		if (this.pattern.findReferences && possibleMatchingNode instanceof ModuleReference) {
+			return resolveLevel(((ModuleReference) possibleMatchingNode).resolve(null));
+		}
+		return IMPOSSIBLE_MATCH;
+	}
+	@Override
+	protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException {
+		super.matchReportReference(reference, element, elementBinding, accuracy, locator);
+	}
+	@Override
+	protected void matchReportReference(ASTNode reference, IJavaElement element, IJavaElement localElement, IJavaElement[] otherElements, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException {
+		matchReportReference(reference, element, elementBinding, accuracy, locator);
+	}
+
+	@Override
+	public SearchMatch newDeclarationMatch(ASTNode node, IJavaElement element, Binding elementBinding, int accuracy, int length, MatchLocator locator) {
+		return super.newDeclarationMatch(node, element, elementBinding, accuracy, length, locator);
+	}
+	@Override
+	protected int referenceType() {
+		return IJavaElement.JAVA_MODULE;
+	}
+	@Override
+	public int resolveLevel(Binding binding) {
+		if (binding == null) return INACCURATE_MATCH;
+		if (!(binding instanceof ModuleBinding)) return IMPOSSIBLE_MATCH;
+		return (matchesName(this.pattern.name, binding.readableName())) ? ACCURATE_MATCH : IMPOSSIBLE_MATCH;
+	}
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModulePattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModulePattern.java
new file mode 100644
index 0000000..cb92fad
--- /dev/null
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ModulePattern.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * 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
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core.search.matching;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.internal.core.index.EntryResult;
+import org.eclipse.jdt.internal.core.index.Index;
+
+public class ModulePattern extends JavaSearchPattern {
+
+	boolean findDeclarations = true; /* package visible */
+	boolean findReferences = true; /* package visible */
+	char[] name; /* package visible */
+
+	protected static char[][] REF_CATEGORIES = { MODULE_REF };
+	protected static char[][] REF_AND_DECL_CATEGORIES = { MODULE_REF, MODULE_DECL };
+	protected static char[][] DECL_CATEGORIES = { MODULE_DECL };
+
+	public static char[] createIndexKey(char[] name) {
+		return name; // until a need arises, let the name itself be the index key.
+	}
+	protected ModulePattern(int matchRule) {
+		super(MODULE_PATTERN, matchRule);
+	}
+	public ModulePattern(char[] name, int limitTo, int matchRule) {
+		this(matchRule);
+		this.name = name;
+		switch (limitTo & 0xF) {
+			case IJavaSearchConstants.DECLARATIONS :
+				this.findReferences = false;
+				break;
+			case IJavaSearchConstants.REFERENCES :
+				this.findDeclarations = false;
+				break;
+			case IJavaSearchConstants.ALL_OCCURRENCES :
+				break;
+		}
+		this.mustResolve = mustResolve();
+	}
+	public void decodeIndexKey(char[] key) {
+		this.name = key;
+	}
+	public SearchPattern getBlankPattern() {
+		return new ModulePattern(R_EXACT_MATCH);
+	}
+	public char[][] getIndexCategories() {
+		if (this.findReferences)
+			return this.findDeclarations ? REF_AND_DECL_CATEGORIES : REF_CATEGORIES;
+		if (this.findDeclarations)
+			return DECL_CATEGORIES;
+		return CharOperation.NO_CHAR_CHAR;
+	}
+	public boolean matchesDecodedKey(SearchPattern decodedPattern) {
+		return matchesName(this.name, ((ModulePattern) decodedPattern).name);
+	}
+	public EntryResult[] queryIn(Index index) throws IOException {
+		char[] key = this.name; // can be null
+		int matchRule = getMatchRule();
+
+		switch(getMatchMode()) {
+			case R_EXACT_MATCH :
+				if (this.name != null) {
+					key = createIndexKey(this.name);
+				} else { // do a prefix query with the selector
+					matchRule &= ~R_EXACT_MATCH;
+					matchRule |= R_PREFIX_MATCH;
+				}
+				break;
+			case R_PREFIX_MATCH :
+				// do a prefix query with the selector
+				break;
+			case R_PATTERN_MATCH :
+				if (this.name != null) {
+					key = createIndexKey(this.name);
+				}
+				// else do a pattern query with just the selector
+				break;
+			case R_REGEXP_MATCH :
+				// TODO implement regular expression match
+				break;
+			case R_CAMELCASE_MATCH:
+			case R_CAMELCASE_SAME_PART_COUNT_MATCH:
+				// do a prefix query with the selector
+				break;
+		}
+
+		return index.query(getIndexCategories(), key, matchRule); // match rule is irrelevant when the key is null
+	}
+
+	protected boolean mustResolve() {
+		return true;
+	}
+	protected StringBuffer print(StringBuffer output) {
+		if (this.findDeclarations) {
+			output.append(this.findReferences
+				? "ModuleCombinedPattern: " //$NON-NLS-1$
+				: "ModuleDeclarationPattern: "); //$NON-NLS-1$
+		} else {
+			output.append("ModuleReferencePattern: "); //$NON-NLS-1$
+		}
+		output.append("module "); //$NON-NLS-1$
+		output.append(this.name);
+		return super.print(output);
+	}
+}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java
index a57e5f7..b734428 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java
@@ -179,7 +179,7 @@
 			if (binding instanceof PackageBinding)
 				last = ((PackageBinding) binding).compoundName.length;
 			int start = (int) (positions[0] >>> 32);
-			int end = (int) positions[last - 1];
+			int end = (int) positions[last > 0 ? last - 1 : 0];
 			this.match = locator.newPackageReferenceMatch(element, accuracy, start, end-start+1, importRef);
 			locator.report(this.match);
 		}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java
index 39518cb..264cf18 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.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
@@ -126,7 +126,9 @@
 		    return new RoleDeclarationLocator((RoleTypePattern) pattern);
 		case IIndexConstants.REF_TO_TEAMPACKAGE_PATTERN : 
 		    return new ReferenceToTeamLocator((ReferenceToTeamPackagePattern) pattern);
-//carp}			
+//carp}
+		case IIndexConstants.MODULE_PATTERN:
+			return new ModuleLocator((ModulePattern) pattern);
 	}
 	return null;
 }
@@ -289,6 +291,12 @@
 	// each subtype should override if needed
 	return IMPOSSIBLE_MATCH;
 }
+protected int match(ModuleDeclaration node, MatchingNodeSet nodeSet) {
+	return IMPOSSIBLE_MATCH;
+}
+protected int match(ModuleReference node, MatchingNodeSet nodeSet) {
+	return IMPOSSIBLE_MATCH;
+}
 public int match(Reference node, MatchingNodeSet nodeSet) {
 	// each subtype should override if needed
 	return IMPOSSIBLE_MATCH;
@@ -490,7 +498,10 @@
 		case IJavaElement.TYPE_PARAMETER:
 			this.match = locator.newTypeParameterReferenceMatch(element, accuracy, offset, reference.sourceEnd-offset+1, reference);
 			break;
-		//TODO (carp): suppose we need to extend this for OT elements			
+		//TODO (carp): suppose we need to extend this for OT elements
+		case IJavaElement.JAVA_MODULE:
+			this.match = locator.newModuleReferenceMatch(element, elementBinding, accuracy, offset, reference.sourceEnd-offset+1, reference);
+			break;
 	}
 	if (this.match != null) {
 		locator.report(this.match);
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java
index 88f084a..58c35af 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.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
@@ -17,6 +17,7 @@
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
 import org.eclipse.jdt.internal.compiler.env.IBinaryType;
 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
 import org.eclipse.jdt.internal.core.*;
 import org.eclipse.jdt.internal.core.util.Util;
 
@@ -63,14 +64,23 @@
 public char[] getContents() {
 	char[] contents = (this.source == NO_SOURCE_FILE) ? null : this.source;
 	if (this.source == null) {
-		if (this.openable instanceof ClassFile) {
+		if (this.openable instanceof AbstractClassFile) {
 			String fileName = getSourceFileName();
 			if (fileName == NO_SOURCE_FILE_NAME) return CharOperation.NO_CHAR;
-	
+
 			SourceMapper sourceMapper = this.openable.getSourceMapper();
 			if (sourceMapper != null) {
-			IType type = ((ClassFile) this.openable).getType();
-			contents = sourceMapper.findSource(type, fileName);
+				if (this.openable instanceof ClassFile) {
+					IType type = ((ClassFile) this.openable).getType();
+					contents = sourceMapper.findSource(type, fileName);
+				} else if (this.openable instanceof ModularClassFile) {
+					try {
+						IModuleDescription module = ((ModularClassFile) this.openable).getModule();
+						contents = module != null ? sourceMapper.findSource(module) : CharOperation.NO_CHAR; // FIXME(SHMOD)
+					} catch (JavaModelException e) {
+						return CharOperation.NO_CHAR;
+					}
+				}
 			}
 		} else {
 			contents = this.document.getCharContents();
@@ -119,6 +129,11 @@
 		String simpleName = index==-1 ? fileName : fileName.substring(0, index);
 		PackageFragment pkg = (PackageFragment) this.openable.getParent();
 		return Util.concatWith(pkg.names, simpleName, '.').toCharArray();
+	} else if (this.openable instanceof ModularClassFile) {
+		// FIXME(SHMOD): not useful https://bugs.eclipse.org/501162#c30
+		String simpleName = TypeConstants.MODULE_INFO_NAME_STRING;
+		PackageFragment pkg = (PackageFragment) this.openable.getParent();
+		return Util.concatWith(pkg.names, simpleName, '.').toCharArray();
 	}
 	return null;
 }
@@ -134,17 +149,22 @@
 
 	this.sourceFileName = NO_SOURCE_FILE_NAME; 
 	if (this.openable.getSourceMapper() != null) {
-		BinaryType type = (BinaryType) ((ClassFile) this.openable).getType();
-		IBinaryType reader = MatchLocator.classFileReader(type);
-		if (reader != null) {
-			String fileName = type.sourceFileName(reader);
-			this.sourceFileName = fileName == null ? NO_SOURCE_FILE_NAME : fileName;
+		if (this.openable instanceof ClassFile) {
+			BinaryType type = (BinaryType) ((ClassFile) this.openable).getType();
+			IBinaryType reader = MatchLocator.classFileReader(type);
+			if (reader != null) {
+				String fileName = type.sourceFileName(reader);
+				this.sourceFileName = fileName == null ? NO_SOURCE_FILE_NAME : fileName;
+			}
+		} else if (this.openable instanceof ModularClassFile) {
+			// FIXME(SHMOD): premature https://bugs.eclipse.org/501162#c31
+			this.sourceFileName = TypeConstants.MODULE_INFO_FILE_NAME_STRING;
 		}
 	}
 	return this.sourceFileName;
 }	
 boolean hasSimilarMatch() {
-	return this.similarMatch != null && this.source == NO_SOURCE_FILE;
+	return this.similarMatch != null && (this.source == NO_SOURCE_FILE || isModuleInfo(this));
 }
 public int hashCode() {
 	if (this.compoundName == null) return super.hashCode();
@@ -157,13 +177,28 @@
 public boolean ignoreOptionalProblems() {
 	return false;
 }
+private boolean isModuleInfo(PossibleMatch possibleMatch) {
+	return CharOperation.equals(getMainTypeName(), TypeConstants.MODULE_INFO_NAME);
+}
 void setSimilarMatch(PossibleMatch possibleMatch) {
 	// source does not matter on similar match as it is read on
 	// the first stored possible match
-	possibleMatch.source = NO_SOURCE_FILE;
+	possibleMatch.source = isModuleInfo(possibleMatch) ? null : NO_SOURCE_FILE;
 	this.similarMatch = possibleMatch;
 }
 public String toString() {
 	return this.openable == null ? "Fake PossibleMatch" : this.openable.toString(); //$NON-NLS-1$
 }
+@Override
+public char[] getModuleName() {
+	if (this.openable instanceof CompilationUnit) {
+		return ((CompilationUnit) this.openable).getModuleName();
+	} else if (this.openable instanceof ClassFile) {
+		IModuleDescription moduleDescription = this.openable.getPackageFragmentRoot().getModuleDescription();
+		if (moduleDescription != null) {
+			return moduleDescription.getElementName().toCharArray();
+		}
+	}
+	return null;
+}
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatchSet.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatchSet.java
index df8ef79..ad5b939 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatchSet.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatchSet.java
@@ -10,6 +10,8 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
+import java.util.HashSet;
+
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.internal.compiler.util.ObjectVector;
@@ -45,11 +47,14 @@
 public PossibleMatch[] getPossibleMatches(IPackageFragmentRoot[] roots) {
 	PossibleMatch[] result = new PossibleMatch[this.elementCount];
 	int index = 0;
+	HashSet<IPath> processedHash = new HashSet<>();
 	for (int i = 0, length = roots.length; i < length; i++) {
-		ObjectVector possibleMatches = (ObjectVector) this.rootsToPossibleMatches.get(roots[i].getPath());
-		if (possibleMatches != null) {
+		IPath path = roots[i].getPath();
+		ObjectVector possibleMatches = (ObjectVector) this.rootsToPossibleMatches.get(path);
+		if (possibleMatches != null && !processedHash.contains(path)) {
 			possibleMatches.copyInto(result, index);
 			index += possibleMatches.size();
+			processedHash.add(path);
 		}
 	}
 	if (index < this.elementCount)
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeNamesCollector.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeNamesCollector.java
index 3bd424b..c03cf20 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeNamesCollector.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeNamesCollector.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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
@@ -221,8 +221,8 @@
 				CompilationUnitDeclaration parsedUnit = buildBindings(unit, true /*only top level and member types are visible to the focus type*/);
 				if (parsedUnit != null)
 					parsedUnit.traverse(new TypeDeclarationVisitor(), parsedUnit.scope);
-			} else if (openable instanceof IClassFile) {
-				IClassFile classFile = (IClassFile) openable;
+			} else if (openable instanceof IOrdinaryClassFile) {
+				IOrdinaryClassFile classFile = (IOrdinaryClassFile) openable;
 				BinaryTypeBinding binding = this.locator.cacheBinaryType(classFile.getType(), null);
 				if (matches(binding))
 					collectSuperTypeNames(binding, binding.compoundName);