update jdt.core to I20221202-1800 for move of ecj to own project
+ move of ot-compiler sources
  - adjust otdt package exports
+ add jdt.annotation optional requirement
diff --git a/org.eclipse.jdt.core.compiler.batch/.classpath b/org.eclipse.jdt.core.compiler.batch/.classpath
new file mode 100644
index 0000000..ae1b359
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="module" value="true"/>
+			<attribute name="limit-modules" value="java.base"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.core.compiler.batch/.project b/org.eclipse.jdt.core.compiler.batch/.project
new file mode 100644
index 0000000..c5f33a4
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jdt.core.compiler.batch</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.core.compiler.batch/.settings/.api_filters b/org.eclipse.jdt.core.compiler.batch/.settings/.api_filters
new file mode 100644
index 0000000..a69e00e
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.settings/.api_filters
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jdt.core.compiler.batch" version="2">
+    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jdt.core.BuildJarIndex">
+        <filter id="305324134">
+            <message_arguments>
+                <message_argument value="org.eclipse.jdt.core.BuildJarIndex"/>
+                <message_argument value="org.eclipse.jdt.core.compiler.batch_3.33.0"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jdt.core.CheckDebugAttributes">
+        <filter id="305324134">
+            <message_arguments>
+                <message_argument value="org.eclipse.jdt.core.CheckDebugAttributes"/>
+                <message_argument value="org.eclipse.jdt.core.compiler.batch_3.33.0"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jdt/core/compiler/batch/BatchCompiler.java" type="org.eclipse.jdt.core.compiler.batch.BatchCompiler">
+        <filter id="1108344834">
+            <message_arguments>
+                <message_argument value="3.4"/>
+                <message_argument value="3.33"/>
+                <message_argument value="org.eclipse.jdt.core.compiler.batch.BatchCompiler"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
diff --git a/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..796937c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding//scripts/ecj.1=ISO-8859-1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..5a0ad22
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..19d7cb9
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,395 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,.svn/
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.uninternedIdentityComparison=enabled
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning
+org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=11
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..d05be54
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,113 @@
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=true
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.format_comment=false
+cleanup.format_javadoc=true
+cleanup.format_multi_line_comment=true
+cleanup.format_single_line_comment=true
+cleanup.format_source_code=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=true
+cleanup.qualify_static_field_accesses_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=true
+cleanup.use_this_for_non_static_field_access_only_if_necessary=false
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=_Numbat
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Jdtcore [built-in + Indent switch body + LineWidth\:120]
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.keywordthis=true
+org.eclipse.jdt.ui.overrideannotation=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=false
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=false
+sp_cleanup.add_missing_override_annotations_interface_methods=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=false
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.update_ibm_copyright_to_current_year=true
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..6f7536a
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,94 @@
+#Fri May 21 10:24:07 EDT 2010
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_JAVADOC_TAG=Warning
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Warning
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.pde.prefs b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..9251e60
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,35 @@
+#Fri May 21 10:24:08 EDT 2010
+compilers.f.unresolved-features=1
+compilers.f.unresolved-plugins=1
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.build.bin.includes=1
+compilers.p.build.encodings=2
+compilers.p.build.java.compiler=2
+compilers.p.build.java.compliance=2
+compilers.p.build.missing.output=2
+compilers.p.build.output.library=2
+compilers.p.build.source.library=2
+compilers.p.build.src.includes=1
+compilers.p.deprecated=1
+compilers.p.discouraged-class=1
+compilers.p.internal=1
+compilers.p.missing-bundle-classpath-entries=2
+compilers.p.missing-packages=2
+compilers.p.missing-version-export-package=2
+compilers.p.missing-version-import-package=2
+compilers.p.missing-version-require-bundle=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=2
+compilers.p.unknown-attribute=1
+compilers.p.unknown-class=1
+compilers.p.unknown-element=1
+compilers.p.unknown-identifier=1
+compilers.p.unknown-resource=1
+compilers.p.unresolved-ex-points=1
+compilers.p.unresolved-import=0
+compilers.s.create-docs=false
+compilers.s.doc-folder=doc
+compilers.s.open-tags=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/org.eclipse.jdt.core.compiler.batch/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.compiler.batch/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..0f72df9
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/META-INF/MANIFEST.MF
@@ -0,0 +1,48 @@
+Manifest-Version: 1.0
+Main-Class: org.eclipse.jdt.internal.compiler.batch.Main
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse Compiler for Java(TM)
+Bundle-SymbolicName: org.eclipse.jdt.core.compiler.batch
+Bundle-Version: 3.33.0.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: Eclipse.org
+Automatic-Module-Name: org.eclipse.jdt.core.compiler.batch
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-11
+Require-Bundle: org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional;visibility:=reexport
+Export-Package: org.eclipse.jdt.core,
+ org.eclipse.jdt.core.compiler,
+ org.eclipse.jdt.core.compiler.batch,
+ org.eclipse.jdt.internal.antadapter;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.apt.dispatch;x-friends:="org.eclipse.jdt.apt.pluggable.core,org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.apt.model;x-friends:="org.eclipse.jdt.apt.pluggable.core,org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.apt.util;x-friends:="org.eclipse.jdt.apt.pluggable.core,org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.ast;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.batch;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.classfmt;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.codegen;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.env;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.flow;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.impl;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.lookup;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.parser;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.parser.diagnose;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.problem;x-friends:="org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.tool;x-friends:="org.eclipse.jdt.compiler.tool.tests,org.eclipse.jdt.core",
+ org.eclipse.jdt.internal.compiler.util;x-friends:="org.eclipse.jdt.core.internal.tools,org.eclipse.jdt.core",
+ org.eclipse.objectteams.otdt.internal.core.compiler.ast;x-friends:="org.eclipse.jdt.core,org.eclipse.jdt.core.tests.compiler",
+ org.eclipse.objectteams.otdt.internal.core.compiler.bytecode;x-friends:="org.eclipse.objectteams.otdt.compiler.adaptor",
+ org.eclipse.objectteams.otdt.internal.core.compiler.control;x-friends:="org.eclipse.objectteams.otdt.tests,org.eclipse.objectteams.otdt.apt",
+ org.eclipse.objectteams.otdt.internal.core.compiler.lifting;x-friends:="org.eclipse.objectteams.otdt.compiler.adaptor",
+ org.eclipse.objectteams.otdt.internal.core.compiler.lookup;x-friends:="org.eclipse.objectteams.otdt.compiler.adaptor",
+ org.eclipse.objectteams.otdt.internal.core.compiler.mappings;x-internal:=true,
+ org.eclipse.objectteams.otdt.internal.core.compiler.model;x-friends:="org.eclipse.objectteams.otdt.apt",
+ org.eclipse.objectteams.otdt.internal.core.compiler.problem;x-internal:=true,
+ org.eclipse.objectteams.otdt.internal.core.compiler.smap;x-friends:="org.eclipse.objectteams.otdt.tests",
+ org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.copyinheritance;x-friends:="org.eclipse.objectteams.otdt.compiler.adaptor",
+ org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer;x-internal:=true,
+ org.eclipse.objectteams.otdt.internal.core.compiler.util;x-internal:=true,
+ org.eclipse.objectteams.otdt.core.compiler;x-friends:="org.eclipse.jdt.core,org.eclipse.objectteams.otdt",
+ org.eclipse.objectteams.otdt.core.exceptions;x-friends:="org.eclipse.jdt.core",
+ META-INF.services
diff --git a/org.eclipse.jdt.core.compiler.batch/META-INF/eclipse.inf b/org.eclipse.jdt.core.compiler.batch/META-INF/eclipse.inf
new file mode 100644
index 0000000..4dc37a0
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/META-INF/eclipse.inf
@@ -0,0 +1,2 @@
+jarprocessor.exclude.children=true
+jarprocessor.exclude.pack=true
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.compiler.batch/META-INF/services/javax.tools.JavaCompiler b/org.eclipse.jdt.core.compiler.batch/META-INF/services/javax.tools.JavaCompiler
new file mode 100644
index 0000000..7fb91cf
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/META-INF/services/javax.tools.JavaCompiler
@@ -0,0 +1 @@
+org.eclipse.jdt.internal.compiler.tool.EclipseCompiler #Eclipse compiler
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.compiler.batch/about.html b/org.eclipse.jdt.core.compiler.batch/about.html
new file mode 100644
index 0000000..164f781
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/about.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>About</title>
+</head>
+<body lang="EN-US">
+	<h2>About This Content</h2>
+
+	<p>November 30, 2017</p>
+	<h3>License</h3>
+
+	<p>
+		The Eclipse Foundation makes available all content in this plug-in
+		(&quot;Content&quot;). Unless otherwise indicated below, the Content
+		is provided to you under the terms and conditions of the Eclipse
+		Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is
+		available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+		For purposes of the EPL, &quot;Program&quot; will mean the Content.
+	</p>
+
+	<p>
+		If you did not receive this Content directly from the Eclipse
+		Foundation, the Content is being redistributed by another party
+		(&quot;Redistributor&quot;) and different terms and conditions may
+		apply to your use of any object code in the Content. Check the
+		Redistributor's license that was provided with the Content. If no such
+		license exists, contact the Redistributor. Unless otherwise indicated
+		below, the terms and conditions of the EPL still apply to any source
+		code in the Content and such source code may be obtained at <a
+			href="http://www.eclipse.org/">http://www.eclipse.org</a>.
+	</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.compiler.batch/build.properties b/org.eclipse.jdt.core.compiler.batch/build.properties
new file mode 100644
index 0000000..a57901f
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/build.properties
@@ -0,0 +1,24 @@
+###############################################################################
+# Copyright (c) 2000, 2022 IBM Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+customBuildCallbacks=customBuildCallbacks.xml
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               about.html
+src.includes = about.html,\
+               META-INF/services/,\
+               META-INF/eclipse.inf,\
+               grammar/
+jars.extra.classpath = lib/javax18api.jar,platform:/plugin/org.apache.ant/lib/ant.jar
diff --git a/org.eclipse.jdt.core/grammar/java.g b/org.eclipse.jdt.core.compiler.batch/grammar/java.g
similarity index 100%
rename from org.eclipse.jdt.core/grammar/java.g
rename to org.eclipse.jdt.core.compiler.batch/grammar/java.g
diff --git a/org.eclipse.jdt.core.compiler.batch/lib/javax18api.jar b/org.eclipse.jdt.core.compiler.batch/lib/javax18api.jar
new file mode 100644
index 0000000..65c0b11
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/lib/javax18api.jar
Binary files differ
diff --git a/org.eclipse.jdt.core.compiler.batch/pom.xml b/org.eclipse.jdt.core.compiler.batch/pom.xml
new file mode 100644
index 0000000..141fd75
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/pom.xml
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2012, 2021 Eclipse Foundation and others.
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Distribution License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/org/documents/edl-v10.php
+
+  Contributors:
+     Igor Fedorenko - initial implementation
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>eclipse.jdt.core</artifactId>
+    <groupId>org.eclipse.jdt</groupId>
+    <version>4.27.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>org.eclipse.jdt.core.compiler.batch</artifactId>
+  <version>3.33.0-SNAPSHOT</version>
+  <packaging>eclipse-plugin</packaging>
+
+  <properties>
+    <defaultSigning-excludeInnerJars>true</defaultSigning-excludeInnerJars>
+    <code.ignoredWarnings>-warn:+fieldHiding,-unavoidableGenericProblems</code.ignoredWarnings>
+  </properties>
+
+  <build>
+    <plugins>
+    <plugin>
+		<artifactId>maven-antrun-plugin</artifactId>
+		<executions>
+			<execution>
+				<phase>prepare-package</phase>
+				<configuration>
+					<target>
+						<replace token="bundle_qualifier," value="${buildQualifier}," dir="${project.build.directory}/classes">
+							<include name="org/eclipse/jdt/internal/compiler/batch/messages.properties"/>
+						</replace>
+						<replace token="bundle_version" value="${unqualifiedVersion}" dir="${project.build.directory}/classes">
+							<include name="org/eclipse/jdt/internal/compiler/batch/messages.properties"/>
+						</replace>
+					</target>
+				</configuration>
+				<goals>
+					<goal>run</goal>
+				</goals>
+			</execution>
+		</executions>
+	  </plugin>
+      <!-- XXX ???
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-batch-compiler-source</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/scripts/source</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>${project.basedir}/scripts/source</directory>
+                  <filtering>true</filtering>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      -->
+        <!-- XXX ???
+      <plugin>
+        <groupId>org.eclipse.tycho.extras</groupId>
+        <artifactId>tycho-custom-bundle-plugin</artifactId>
+        <version>${tycho.version}</version>
+        <executions>
+          <execution>
+            <id>batch-compiler-src</id>
+            <phase>package</phase>
+            <goals>
+              <goal>custom-bundle</goal>
+            </goals>
+            <configuration>
+				<archive>
+					<addMavenDescriptor>false</addMavenDescriptor>
+				</archive>
+              <bundleLocation>${project.build.directory}/scripts/source</bundleLocation>
+              <classifier>batch-compiler-src</classifier>
+              <fileSets>
+                <fileSet>
+                  <directory>${project.basedir}/batch</directory>
+                  <excludes>
+                    <exclude>org/eclipse/jdt/internal/compiler/batch/messages.properties</exclude>
+                  </excludes>
+                </fileSet>
+                <fileSet>
+                  <directory>${project.build.directory}/classes</directory>
+                  <includes>
+                    <include>org/eclipse/jdt/internal/compiler/batch/messages.properties</include>
+                  </includes>
+                </fileSet>
+                <fileSet>
+                  <directory>${project.basedir}/compiler</directory>
+                </fileSet>
+                <fileSet>
+                  <directory>${project.basedir}/antadapter</directory>
+                  <excludes>
+				    <exclude>org/eclipse/jdt/core/CheckDebugAttributes.java</exclude>
+					<exclude>org/eclipse/jdt/core/BuildJarIndex.java</exclude>
+                  </excludes>
+                </fileSet>
+				<fileSet>
+                  <directory>${project.basedir}/../org.eclipse.jdt.compiler.tool/src</directory>
+                </fileSet>
+				<fileSet>
+                  <directory>${project.basedir}/../org.eclipse.jdt.compiler.apt/src</directory>
+                </fileSet>
+				<fileSet>
+                  <directory>${project.basedir}/scripts</directory>
+                  <includes>
+				    <include>about.html</include>
+					<include>build.xml</include>
+					<include>ecj.1</include>
+                  </includes>
+                </fileSet>
+                <fileSet>
+                  <directory>${project.basedir}</directory>
+                  <includes>
+                    <include>scripts/binary/**</include>
+                  </includes>
+                </fileSet>
+                <fileSet>
+                  <directory>${project.basedir}/../org.eclipse.jdt.compiler.tool/lib</directory>
+                  <includes>
+                    <include>*.jar</include>
+                  </includes>
+                </fileSet>
+              </fileSets>
+            </configuration>
+          </execution>
+          <execution>
+            <id>batch-compiler</id>
+            <phase>package</phase>
+            <goals>
+              <goal>custom-bundle</goal>
+            </goals>
+            <configuration>
+				<archive>
+					<addMavenDescriptor>false</addMavenDescriptor>
+				</archive>
+              <bundleLocation>${project.basedir}/scripts/binary</bundleLocation>
+              <classifier>batch-compiler</classifier>
+              <fileSets>
+                <fileSet>
+                  <directory>${project.build.directory}/jdtCompilerAdapter.jar-classes</directory>
+                  <includes>
+				    <include>META-INF/eclipse.inf</include>
+                  </includes>
+                </fileSet>
+				<fileSet>
+                  <directory>${project.basedir}/scripts</directory>
+                  <includes>
+				    <include>about.html</include>
+				    <include>ecj.1</include>
+                  </includes>
+                </fileSet>
+                <fileSet>
+                  <directory>${project.build.directory}/classes</directory>
+                  <includes>
+                    <include>org/eclipse/jdt/internal/compiler/**</include>
+                    <include>org/eclipse/jdt/core/compiler/**</include>
+                  </includes>
+                  <excludes>
+                    <exclude>**/package.htm*</exclude>
+                    <exclude>org/eclipse/jdt/core/compiler/CompilationParticipant*.class</exclude>
+                    <exclude>org/eclipse/jdt/core/compiler/BuildContext.class</exclude>
+                    <exclude>org/eclipse/jdt/core/compiler/IScanner.class</exclude>
+                    <exclude>org/eclipse/jdt/core/compiler/ITerminalSymbols*.class</exclude>
+                    <exclude>org/eclipse/jdt/core/compiler/ReconcileContext*.class</exclude>
+                    <exclude>org/eclipse/jdt/internal/compiler/DocumentElementParser*.class</exclude>
+                    <exclude>org/eclipse/jdt/internal/compiler/IDocumentElementRequestor.class</exclude>
+                    <exclude>org/eclipse/jdt/internal/compiler/ISourceElementRequestor*.class</exclude>
+                    <exclude>org/eclipse/jdt/internal/compiler/SourceElementParser*.class</exclude>
+                    <exclude>org/eclipse/jdt/internal/compiler/SourceElementRequestorAdapter*.class</exclude>
+                    <exclude>org/eclipse/jdt/internal/compiler/SourceJavadocParser*.class</exclude>
+                    <exclude>org/eclipse/jdt/internal/compiler/parser/SourceTypeConverter*.class</exclude>
+                  </excludes>
+                </fileSet>
+              </fileSets>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+         -->
+      <plugin>
+        <groupId>org.eclipse.tycho</groupId>
+        <artifactId>tycho-p2-plugin</artifactId>
+        <version>${tycho.version}</version>
+        <configuration>
+          <baselineMode>warn</baselineMode>
+          <baselineReplace>common</baselineReplace>
+        </configuration>
+        <executions>
+          <execution>
+            <id>attached-p2-metadata</id>
+            <phase>package</phase>
+            <goals>
+              <goal>p2-metadata</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>3.2.0</version>
+        <executions>
+          <!--
+            Replace '\' Windows file separators by '/' in order to expand the new property 'compiler-message-properties'
+            into a string literal in Maven Enforcer rule 'evaluateBeanshell' further below
+          --> 
+          <execution>
+            <id>compiler-message-properties</id>
+            <goals>
+              <goal>regex-property</goal>
+            </goals>
+            <configuration>
+              <name>compiler-message-properties</name>
+              <value>${project.basedir}/src/org/eclipse/jdt/internal/compiler/batch/messages.properties</value>
+              <regex>\\</regex>
+              <replacement>/</replacement>
+              <failIfNoMatch>false</failIfNoMatch>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>no-tabs-in-compiler-messages</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <evaluateBeanshell>
+                  <message>
+                    Compiler message resource file ${compiler-message-properties} must not contain tab characters, please use spaces instead!
+                  </message>
+                  <condition><![CDATA[
+                    FileReader fileReader = new FileReader("${compiler-message-properties}");
+                    BufferedReader bufferReader = new BufferedReader(fileReader);
+                    boolean containsTab = false;
+                    String line;
+                    while((line = bufferReader.readLine()) != null) {
+                      if (line.contains("\t")) {
+                        if (!containsTab) {
+                          System.out.println("Lines containing tab characters detected in resource file:");
+                          containsTab = true;
+                        }
+                        System.out.println(line);
+                      }
+                    }
+                    fileReader.close();
+                    bufferReader.close();
+                    !containsTab
+                  ]]></condition>
+                </evaluateBeanshell>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.eclipse.jdt.core/scripts/GenerateParserScript.java b/org.eclipse.jdt.core.compiler.batch/scripts/GenerateParserScript.java
similarity index 98%
rename from org.eclipse.jdt.core/scripts/GenerateParserScript.java
rename to org.eclipse.jdt.core.compiler.batch/scripts/GenerateParserScript.java
index 8089bf8..d510395 100644
--- a/org.eclipse.jdt.core/scripts/GenerateParserScript.java
+++ b/org.eclipse.jdt.core.compiler.batch/scripts/GenerateParserScript.java
@@ -23,7 +23,7 @@
 
 	public static void main(String[] args) throws IOException, InterruptedException {
 		File grammarDir = new File("../grammar");
-		File parserDir = new File("../compiler/org/eclipse/jdt/internal/compiler/parser");
+		File parserDir = new File("../src/org/eclipse/jdt/internal/compiler/parser");
 		String jikespg = System.getProperty("JIKESPG");
 		assertTrue(jikespg != null);
 
diff --git a/org.eclipse.jdt.core/scripts/build-parser.launch b/org.eclipse.jdt.core.compiler.batch/scripts/build-parser.launch
similarity index 83%
rename from org.eclipse.jdt.core/scripts/build-parser.launch
rename to org.eclipse.jdt.core.compiler.batch/scripts/build-parser.launch
index 55abf6b..4896c04 100644
--- a/org.eclipse.jdt.core/scripts/build-parser.launch
+++ b/org.eclipse.jdt.core.compiler.batch/scripts/build-parser.launch
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <launchConfiguration type="org.eclipse.ant.AntLaunchConfigurationType">
     <booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
-    <stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
+    <stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
     <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-        <listEntry value="/eclipse.jdt.core/org.eclipse.jdt.core/scripts/build-parser.xml"/>
+        <listEntry value="/eclipse.jdt.core/org.eclipse.jdt.core.compiler.batch/scripts/build-parser.xml"/>
     </listAttribute>
     <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
         <listEntry value="1"/>
@@ -12,9 +12,9 @@
     <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
     <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
     <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.ant.internal.launching.remote.InternalAntRunner"/>
-    <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jdt.core"/>
+    <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jdt.core.compiler.batch"/>
     <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
-    <stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/org.eclipse.jdt.core/scripts/build-parser.xml}"/>
+    <stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/org.eclipse.jdt.core.compiler.batch/scripts/build-parser.xml}"/>
     <stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="-DJIKESPG=${JIKESPG}"/>
     <stringAttribute key="process_factory_id" value="org.eclipse.ant.ui.remoteAntProcessFactory"/>
 </launchConfiguration>
diff --git a/org.eclipse.jdt.core/scripts/build-parser.xml b/org.eclipse.jdt.core.compiler.batch/scripts/build-parser.xml
similarity index 98%
rename from org.eclipse.jdt.core/scripts/build-parser.xml
rename to org.eclipse.jdt.core.compiler.batch/scripts/build-parser.xml
index 8d7c2a1..db649fc 100644
--- a/org.eclipse.jdt.core/scripts/build-parser.xml
+++ b/org.eclipse.jdt.core.compiler.batch/scripts/build-parser.xml
@@ -24,7 +24,6 @@
  		<javac srcdir="${basedir}" includes="GenerateParserScript.java" destdir="${basedir}"
  					classpath="${basedir}/../bin"
 					debuglevel="lines,source"
- 					debug="true"
 					source="1.8"
 					target="1.8">
 			<compilerarg/>
diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/JDTCompilerAdapter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/JDTCompilerAdapter.java
similarity index 100%
rename from org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/JDTCompilerAdapter.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/JDTCompilerAdapter.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CategorizedProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/CategorizedProblem.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CategorizedProblem.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/CategorizedProblem.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/CharOperation.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/CharOperation.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CompilationProgress.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/CompilationProgress.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CompilationProgress.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/CompilationProgress.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/InvalidInputException.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/InvalidInputException.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/InvalidInputException.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/InvalidInputException.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/SubwordMatcher.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/SubwordMatcher.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/SubwordMatcher.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/SubwordMatcher.java
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/core/compiler/batch/BatchCompiler.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/batch/BatchCompiler.java
similarity index 100%
rename from org.eclipse.jdt.core/batch/org/eclipse/jdt/core/compiler/batch/BatchCompiler.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/batch/BatchCompiler.java
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/core/compiler/batch/package.html b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/batch/package.html
similarity index 100%
rename from org.eclipse.jdt.core/batch/org/eclipse/jdt/core/compiler/batch/package.html
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/batch/package.html
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/package.html b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/package.html
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/package.html
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/package.html
diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/AntAdapterMessages.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/antadapter/AntAdapterMessages.java
similarity index 100%
rename from org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/AntAdapterMessages.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/antadapter/AntAdapterMessages.java
diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/antadapter/messages.properties
similarity index 100%
rename from org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/messages.properties
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/antadapter/messages.properties
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ASTVisitor.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ASTVisitor.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ASTVisitor.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ASTVisitor.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/AbstractAnnotationProcessorManager.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/AbstractAnnotationProcessorManager.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/AbstractAnnotationProcessorManager.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/AbstractAnnotationProcessorManager.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFilePool.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFilePool.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFilePool.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFilePool.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/CompilationResult.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/CompilationResult.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/CompilationResult.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/CompilationResult.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/Compiler.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/Compiler.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ConfigurableOption.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ConfigurableOption.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ConfigurableOption.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ConfigurableOption.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/DefaultErrorHandlingPolicies.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/DefaultErrorHandlingPolicies.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/DefaultErrorHandlingPolicies.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/DefaultErrorHandlingPolicies.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/GenericAstVisitor.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/GenericAstVisitor.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/GenericAstVisitor.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/GenericAstVisitor.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ICompilerRequestor.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ICompilerRequestor.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ICompilerRequestor.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ICompilerRequestor.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/IDebugRequestor.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/IDebugRequestor.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/IDebugRequestor.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/IDebugRequestor.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/IErrorHandlingPolicy.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/IErrorHandlingPolicy.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/IErrorHandlingPolicy.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/IErrorHandlingPolicy.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/IProblemFactory.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/IProblemFactory.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/IProblemFactory.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/IProblemFactory.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ProcessTaskManager.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ProcessTaskManager.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ProcessTaskManager.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ProcessTaskManager.java
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ReadManager.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ReadManager.java
similarity index 100%
rename from org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ReadManager.java
rename to org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ReadManager.java
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/AnnotationDiscoveryVisitor.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/AnnotationDiscoveryVisitor.java
new file mode 100644
index 0000000..9e18a74
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/AnnotationDiscoveryVisitor.java
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.apt.model.ElementImpl;
+import org.eclipse.jdt.internal.compiler.apt.model.Factory;
+import org.eclipse.jdt.internal.compiler.apt.util.ManyToMany;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.RecordComponent;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
+import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.AptSourceLocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+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.RecordComponentBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+/**
+ * This class is used to visit the JDT compiler internal AST to discover annotations,
+ * in the course of dispatching to annotation processors.
+ */
+public class AnnotationDiscoveryVisitor extends ASTVisitor {
+	final BaseProcessingEnvImpl _env;
+	final Factory _factory;
+	/**
+	 * Collects a many-to-many map of annotation types to
+	 * the elements they appear on.
+	 */
+	final ManyToMany<TypeElement, Element> _annoToElement;
+
+	public AnnotationDiscoveryVisitor(BaseProcessingEnvImpl env) {
+		_env = env;
+		_factory = env.getFactory();
+		_annoToElement = new ManyToMany<>();
+	}
+
+	@Override
+	public boolean visit(Argument argument, BlockScope scope) {
+		Annotation[] annotations = argument.annotations;
+		ReferenceContext referenceContext = scope.referenceContext();
+		if (referenceContext instanceof AbstractMethodDeclaration) {
+			MethodBinding binding = ((AbstractMethodDeclaration) referenceContext).binding;
+			if (binding != null) {
+				TypeDeclaration typeDeclaration = scope.referenceType();
+				typeDeclaration.binding.resolveTypesFor(binding);
+				if (argument.binding != null) {
+					argument.binding = new AptSourceLocalVariableBinding(argument.binding, binding);
+				}
+			}
+			if (annotations != null && argument.binding != null) {
+				this.resolveAnnotations(
+						scope,
+						annotations,
+						argument.binding);
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
+		Annotation[] annotations = constructorDeclaration.annotations;
+		if (annotations != null) {
+			MethodBinding constructorBinding = constructorDeclaration.binding;
+			if (constructorBinding == null) {
+				return false;
+			}
+			((SourceTypeBinding) constructorBinding.declaringClass).resolveTypesFor(constructorBinding);
+			this.resolveAnnotations(
+					constructorDeclaration.scope,
+					annotations,
+					constructorBinding);
+		}
+
+		TypeParameter[] typeParameters = constructorDeclaration.typeParameters;
+		if (typeParameters != null) {
+			int typeParametersLength = typeParameters.length;
+			for (int i = 0; i < typeParametersLength; i++) {
+				typeParameters[i].traverse(this, constructorDeclaration.scope);
+			}
+		}
+
+		Argument[] arguments = constructorDeclaration.arguments;
+		if (arguments != null) {
+			int argumentLength = arguments.length;
+			for (int i = 0; i < argumentLength; i++) {
+				arguments[i].traverse(this, constructorDeclaration.scope);
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
+		Annotation[] annotations = fieldDeclaration.annotations;
+		if (annotations != null) {
+			FieldBinding fieldBinding = fieldDeclaration.binding;
+			if (fieldBinding == null) {
+				return false;
+			}
+			((SourceTypeBinding) fieldBinding.declaringClass).resolveTypeFor(fieldBinding);
+			if (fieldDeclaration.binding == null) {
+				return false;
+			}
+			this.resolveAnnotations(scope, annotations, fieldBinding);
+		}
+		return false;
+	}
+
+	@Override
+	public boolean visit(RecordComponent recordComponent, BlockScope scope) {
+		Annotation[] annotations = recordComponent.annotations;
+		if (annotations != null) {
+			RecordComponentBinding recordComponentBinding = recordComponent.binding;
+			if (recordComponentBinding == null) {
+				return false;
+			}
+			((SourceTypeBinding) recordComponentBinding.declaringRecord).resolveTypeFor(recordComponentBinding);
+			if (recordComponent.binding == null) {
+				return false;
+			}
+			this.resolveAnnotations(scope, annotations, recordComponentBinding);
+		}
+		return false;
+	}
+	@Override
+	public boolean visit(TypeParameter typeParameter, ClassScope scope) {
+		Annotation[] annotations = typeParameter.annotations;
+		if (annotations != null) {
+			TypeVariableBinding binding = typeParameter.binding;
+			if (binding == null) {
+				return false;
+			}
+			this.resolveAnnotations(scope.referenceContext.initializerScope, annotations, binding);
+		}
+		return false;
+	}
+
+	@Override
+	public boolean visit(TypeParameter typeParameter, BlockScope scope) {
+		Annotation[] annotations = typeParameter.annotations;
+		if (annotations != null) {
+			TypeVariableBinding binding = typeParameter.binding;
+			if (binding == null) {
+				return false;
+			}
+			// when we get here, it is guaranteed that class type parameters are connected, but method type parameters may not be.
+			MethodBinding methodBinding = (MethodBinding) binding.declaringElement;
+			((SourceTypeBinding) methodBinding.declaringClass).resolveTypesFor(methodBinding);
+			this.resolveAnnotations(scope, annotations, binding);
+		}
+		return false;
+	}
+
+	@Override
+	public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
+		Annotation[] annotations = methodDeclaration.annotations;
+		if (annotations != null) {
+			MethodBinding methodBinding = methodDeclaration.binding;
+			if (methodBinding == null) {
+				return false;
+			}
+			((SourceTypeBinding) methodBinding.declaringClass).resolveTypesFor(methodBinding);
+			this.resolveAnnotations(
+					methodDeclaration.scope,
+					annotations,
+					methodBinding);
+		}
+
+		TypeParameter[] typeParameters = methodDeclaration.typeParameters;
+		if (typeParameters != null) {
+			int typeParametersLength = typeParameters.length;
+			for (int i = 0; i < typeParametersLength; i++) {
+				typeParameters[i].traverse(this, methodDeclaration.scope);
+			}
+		}
+
+		Argument[] arguments = methodDeclaration.arguments;
+		if (arguments != null) {
+			int argumentLength = arguments.length;
+			for (int i = 0; i < argumentLength; i++) {
+				arguments[i].traverse(this, methodDeclaration.scope);
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
+		SourceTypeBinding binding = memberTypeDeclaration.binding;
+		if (binding == null) {
+			return false;
+		}
+		Annotation[] annotations = memberTypeDeclaration.annotations;
+		if (annotations != null) {
+			this.resolveAnnotations(
+					memberTypeDeclaration.staticInitializerScope,
+					annotations,
+					binding);
+		}
+		return true;
+	}
+
+	@Override
+	public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
+		SourceTypeBinding binding = typeDeclaration.binding;
+		if (binding == null) {
+			return false;
+		}
+		Annotation[] annotations = typeDeclaration.annotations;
+		if (annotations != null) {
+			this.resolveAnnotations(
+					typeDeclaration.staticInitializerScope,
+					annotations,
+					binding);
+		}
+		return true;
+	}
+	@Override
+	public boolean visit(ModuleDeclaration module, CompilationUnitScope scope) {
+		ModuleBinding binding = module.binding;
+		if (binding == null) {
+			return false;
+		}
+		//module.resolveTypeDirectives(scope);
+		// The above call also resolvesAnnotations <=== actually this doesn't populate _annoToElement which is what we need
+
+		Annotation[] annotations = module.annotations;
+		if (annotations != null) {
+			this.resolveAnnotations(module.scope, 
+					annotations, 
+					binding);
+		}
+		return true;
+	}
+
+	private void resolveAnnotations(BlockScope scope, Annotation[] annotations, Binding currentBinding) {
+
+		int length = annotations == null ? 0 : annotations.length;
+		if (length == 0)
+			return;
+
+		boolean old = scope.insideTypeAnnotation;
+		scope.insideTypeAnnotation = true;
+		currentBinding.getAnnotationTagBits();
+		scope.insideTypeAnnotation = old;
+		ElementImpl element = (ElementImpl) _factory.newElement(currentBinding);
+		AnnotationBinding [] annotationBindings = element.getPackedAnnotationBindings(); // discovery is never in terms of repeating annotation.
+		for (AnnotationBinding binding : annotationBindings) {
+			ReferenceBinding annotationType = binding.getAnnotationType();
+			if (binding != null
+					&& Annotation.isAnnotationTargetAllowed(scope, annotationType, currentBinding)
+					) { // binding should be resolved, but in case it's not, ignore it: it could have been wrapped into a container.
+				TypeElement anno = (TypeElement)_factory.newElement(annotationType);
+				_annoToElement.put(anno, element);
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/AptProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/AptProblem.java
new file mode 100644
index 0000000..ae557e3
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/AptProblem.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2001, 2007 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
+
+public class AptProblem extends DefaultProblem {
+
+	// The batch compiler does not depend on org.eclipse.jdt.apt.pluggable.core; this
+	// is just an arbitrary string to it, namespace notwithstanding.  However, the IDE
+	// cares about the fact that this string is registered as a marker ID by the
+	// org.eclipse.jdt.apt.pluggable.core plug-in.
+	private static final String MARKER_ID = "org.eclipse.jdt.apt.pluggable.core.compileProblem";  //$NON-NLS-1$
+
+	/** May be null, if it was not possible to identify problem context */
+	public final ReferenceContext _referenceContext;
+
+	public AptProblem(
+			ReferenceContext referenceContext,
+			char[] originatingFileName,
+			String message,
+			int id,
+			String[] stringArguments,
+			int severity,
+			int startPosition,
+			int endPosition,
+			int line,
+			int column)
+	{
+		super(originatingFileName,
+			message,
+			id,
+			stringArguments,
+			severity,
+			startPosition,
+			endPosition,
+			line,
+			column);
+		_referenceContext = referenceContext;
+	}
+
+	@Override
+	public int getCategoryID() {
+		return CAT_UNSPECIFIED;
+	}
+
+	@Override
+	public String getMarkerType() {
+		return MARKER_ID;
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseAnnotationProcessorManager.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseAnnotationProcessorManager.java
new file mode 100644
index 0000000..9976019
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseAnnotationProcessorManager.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2018 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager;
+import org.eclipse.jdt.internal.compiler.Compiler;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+
+/**
+ * This class is the central dispatch point for Java 6 annotation processing.
+ * This is created and configured by the JDT core; specifics depend on how
+ * compilation is being performed, ie from the command line, via the Tool
+ * interface, or within the IDE.  This class manages the discovery of annotation
+ * processors and other information spanning multiple rounds of processing;
+ * context that is valid only within a single round is managed by
+ * {@link RoundDispatcher}.  There may be multiple instances of this class;
+ * there is in general one of these for every Compiler that has annotation
+ * processing enabled.  Within the IDE there will typically be one for every
+ * Java project, because each project potentially has a separate processor path.
+ *
+ * TODO: do something useful with _supportedOptions and _supportedAnnotationTypes.
+ */
+public abstract class BaseAnnotationProcessorManager extends AbstractAnnotationProcessorManager
+		implements IProcessorProvider
+{
+
+	protected PrintWriter _out;
+	protected PrintWriter _err;
+	protected BaseProcessingEnvImpl _processingEnv;
+	public boolean _isFirstRound = true;
+
+	/**
+	 * The list of processors that have been loaded so far.  A processor on this
+	 * list has been initialized, but may not yet have been called to process().
+	 */
+	protected List<ProcessorInfo> _processors = new ArrayList<>();
+
+	// Tracing
+	protected boolean _printProcessorInfo = false;
+	protected boolean _printRounds = false;
+	protected int _round;
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager#configure(org.eclipse.jdt.internal.compiler.batch.Main, java.lang.String[])
+	 */
+	@Override
+	public void configure(Object batchCompiler, String[] options) {
+		// Implemented by BatchAnnotationProcessorManager.
+		throw new UnsupportedOperationException();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager#configureFromPlatform(org.eclipse.jdt.internal.compiler.Compiler, java.lang.Object)
+	 */
+	@Override
+	public void configureFromPlatform(Compiler compiler, Object compilationUnitLocator, Object javaProject, boolean isTestCode) {
+		// Implemented by IdeAnnotationProcessorManager.
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public List<ProcessorInfo> getDiscoveredProcessors() {
+		return _processors;
+	}
+
+	@Override
+	public ICompilationUnit[] getDeletedUnits() {
+		return _processingEnv.getDeletedUnits();
+	}
+
+	@Override
+	public ICompilationUnit[] getNewUnits() {
+		return _processingEnv.getNewUnits();
+	}
+
+	@Override
+	public ReferenceBinding[] getNewClassFiles() {
+		return _processingEnv.getNewClassFiles();
+	}
+
+	@Override
+	public void reset() {
+		_processingEnv.reset();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager#setErr(java.io.PrintWriter)
+	 */
+	@Override
+	public void setErr(PrintWriter err) {
+		_err = err;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager#setOut(java.io.PrintWriter)
+	 */
+	@Override
+	public void setOut(PrintWriter out) {
+		_out = out;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager#setProcessors(java.lang.Object[])
+	 */
+	@Override
+	public void setProcessors(Object[] processors) {
+		// Only meaningful in batch mode.
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * A single "round" of processing, in the sense implied in
+	 * {@link javax.annotation.processing.Processor}.
+	 * <p>
+	 * The Java 6 Processor spec contains ambiguities about how processors that support "*" are
+	 * handled. Eclipse tries to match Sun's implementation in javac. What that actually does is
+	 * analogous to inspecting the set of annotions found in the root units and adding an
+	 * "imaginary" annotation if the set is empty. Processors are then called in order of discovery;
+	 * for each processor, the intersection between the set of root annotations and the set of
+	 * annotations the processor supports is calculated, and if it is non-empty, the processor is
+	 * called. If the processor returns <code>true</code> then the intersection (including the
+	 * imaginary annotation if one exists) is removed from the set of root annotations and the loop
+	 * continues, until the set is empty. Of course, the imaginary annotation is not actually
+	 * included in the set of annotations passed in to the processor. A processor's process() method
+	 * is not called until its intersection set is non-empty, but thereafter it is called on every
+	 * round. Note that even if a processor is not called in the first round, if it is called in
+	 * subsequent rounds, it will be called in the order in which the processors were discovered,
+	 * rather than being added to the end of the list.
+	 */
+	@Override
+	public void processAnnotations(CompilationUnitDeclaration[] units, ReferenceBinding[] referenceBindings, boolean isLastRound) {
+		if (units != null) {
+			for (CompilationUnitDeclaration declaration : units) {
+				if (declaration != null && declaration.scope != null) {
+					ModuleBinding m = declaration.scope.module();
+					if (m != null) {
+						_processingEnv._current_module = m;
+						break;
+					}
+				}
+			}
+		}
+		RoundEnvImpl roundEnv = new RoundEnvImpl(units, referenceBindings, isLastRound, _processingEnv);
+		PrintWriter out = _out; // closable resource not manages in this class
+		PrintWriter traceProcessorInfo = _printProcessorInfo ? out : null;
+		PrintWriter traceRounds = _printRounds ? out : null;
+		if (traceRounds != null) {
+			traceRounds.println("Round " + ++_round + ':'); //$NON-NLS-1$
+		}
+		RoundDispatcher dispatcher = new RoundDispatcher(
+				this, roundEnv, roundEnv.getRootAnnotations(), traceProcessorInfo, traceRounds);
+		dispatcher.round();
+		if (_isFirstRound) {
+			_isFirstRound = false;
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseMessagerImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseMessagerImpl.java
new file mode 100644
index 0000000..647e8a4
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseMessagerImpl.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2020 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - derived base class from BatchMessagerImpl
+ *
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic.Kind;
+
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.apt.model.AnnotationMemberValue;
+import org.eclipse.jdt.internal.compiler.apt.model.AnnotationMirrorImpl;
+import org.eclipse.jdt.internal.compiler.apt.model.ExecutableElementImpl;
+import org.eclipse.jdt.internal.compiler.apt.model.ModuleElementImpl;
+import org.eclipse.jdt.internal.compiler.apt.model.TypeElementImpl;
+import org.eclipse.jdt.internal.compiler.apt.model.VariableElementImpl;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.AptSourceLocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+public class BaseMessagerImpl {
+
+	static final String[] NO_ARGUMENTS = new String[0];
+
+	/**
+	 * Create a CategorizedProblem that can be reported to an ICompilerRequestor, etc.
+	 *
+	 * @param e the element against which to report the message.  If the element is not
+	 * in the set of elements being compiled in the current round, the reference context
+	 * and filename will be set to null.
+	 * @return
+	 */
+	public static AptProblem createProblem(Kind kind, CharSequence msg, Element e,
+			AnnotationMirror a, AnnotationValue v) {
+		ReferenceContext referenceContext = null;
+		Annotation[] elementAnnotations = null;
+		int startPosition = 0;
+		int endPosition = 0;
+		if (e != null) {
+			switch(e.getKind()) {
+				case MODULE:
+					ModuleElementImpl moduleElementImpl = (ModuleElementImpl) e;
+					Binding moduleBinding = moduleElementImpl._binding;
+					if (moduleBinding instanceof SourceModuleBinding) {
+						SourceModuleBinding sourceModuleBinding = (SourceModuleBinding) moduleBinding;
+						CompilationUnitDeclaration unitDeclaration = (CompilationUnitDeclaration) sourceModuleBinding.scope.referenceContext();
+						referenceContext = unitDeclaration;
+						elementAnnotations = unitDeclaration.moduleDeclaration.annotations;
+						startPosition = unitDeclaration.moduleDeclaration.sourceStart;
+						endPosition = unitDeclaration.moduleDeclaration.sourceEnd;
+					}
+					break;
+				case ANNOTATION_TYPE :
+				case INTERFACE :
+				case CLASS :
+				case ENUM :
+					TypeElementImpl typeElementImpl = (TypeElementImpl) e;
+					Binding typeBinding = typeElementImpl._binding;
+					if (typeBinding instanceof SourceTypeBinding) {
+						SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) typeBinding;
+						TypeDeclaration typeDeclaration = (TypeDeclaration) sourceTypeBinding.scope.referenceContext();
+						referenceContext = typeDeclaration;
+						elementAnnotations = typeDeclaration.annotations;
+						startPosition = typeDeclaration.sourceStart;
+						endPosition = typeDeclaration.sourceEnd;
+					}
+					break;
+				case PACKAGE :
+					// nothing to do: there is no reference context for a package
+					break;
+				case CONSTRUCTOR :
+				case METHOD :
+					ExecutableElementImpl executableElementImpl = (ExecutableElementImpl) e;
+					Binding binding = executableElementImpl._binding;
+					if (binding instanceof MethodBinding) {
+						MethodBinding methodBinding = (MethodBinding) binding;
+						AbstractMethodDeclaration sourceMethod = methodBinding.sourceMethod();
+						if (sourceMethod != null) {
+							referenceContext = sourceMethod;
+							elementAnnotations = sourceMethod.annotations;
+							startPosition = sourceMethod.sourceStart;
+							endPosition = sourceMethod.sourceEnd;
+						}
+					}
+					break;
+				case ENUM_CONSTANT :
+					break;
+				case EXCEPTION_PARAMETER :
+					break;
+				case FIELD :
+				case PARAMETER :
+					VariableElementImpl variableElementImpl = (VariableElementImpl) e;
+					binding = variableElementImpl._binding;
+					if (binding instanceof FieldBinding) {
+						FieldBinding fieldBinding = (FieldBinding) binding;
+						FieldDeclaration fieldDeclaration = fieldBinding.sourceField();
+						if (fieldDeclaration != null) {
+							ReferenceBinding declaringClass = fieldBinding.declaringClass;
+							if (declaringClass instanceof SourceTypeBinding) {
+								SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) declaringClass;
+								TypeDeclaration typeDeclaration = (TypeDeclaration) sourceTypeBinding.scope.referenceContext();
+								referenceContext = typeDeclaration;
+							}
+							elementAnnotations = fieldDeclaration.annotations;
+							startPosition = fieldDeclaration.sourceStart;
+							endPosition = fieldDeclaration.sourceEnd;
+						}
+					} else if (binding instanceof AptSourceLocalVariableBinding){
+						AptSourceLocalVariableBinding parameterBinding = (AptSourceLocalVariableBinding) binding;
+						LocalDeclaration parameterDeclaration = parameterBinding.declaration;
+						if (parameterDeclaration != null) {
+							MethodBinding methodBinding = parameterBinding.methodBinding;
+							if (methodBinding != null) {
+								referenceContext = methodBinding.sourceMethod();
+							}
+							elementAnnotations = parameterDeclaration.annotations;
+							startPosition = parameterDeclaration.sourceStart;
+							endPosition = parameterDeclaration.sourceEnd;
+						}
+					}
+					break;
+				case INSTANCE_INIT :
+				case STATIC_INIT :
+					break;
+				case LOCAL_VARIABLE :
+					break;
+				case TYPE_PARAMETER :
+				default:
+					break;
+			}
+		}
+		StringBuilder builder = new StringBuilder();
+		if (msg != null) {
+			builder.append(msg);
+		}
+		if (a != null && elementAnnotations != null) {
+			AnnotationBinding annotationBinding = ((AnnotationMirrorImpl) a)._binding;
+			Annotation annotation = findAnnotation(elementAnnotations, annotationBinding);
+			if (annotation != null) {
+				startPosition = annotation.sourceStart;
+				endPosition = annotation.sourceEnd;
+				if (v != null && v instanceof AnnotationMemberValue) {
+					MethodBinding methodBinding = ((AnnotationMemberValue) v).getMethodBinding();
+					MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
+					MemberValuePair memberValuePair = null;
+					for (int i = 0; memberValuePair == null && i < memberValuePairs.length; i++) {
+						if (methodBinding == memberValuePairs[i].binding) {
+							memberValuePair = memberValuePairs[i];
+						}
+					}
+					if (memberValuePair != null) {
+						startPosition = memberValuePair.sourceStart;
+						endPosition = memberValuePair.sourceEnd;
+					}
+				}
+			}
+		}
+		int lineNumber = 0;
+		int columnNumber = 1;
+		char[] fileName = null;
+		if (referenceContext != null) {
+			CompilationResult result = referenceContext.compilationResult();
+			fileName = result.fileName;
+			int[] lineEnds = null;
+			lineNumber = startPosition >= 0
+					? Util.getLineNumber(startPosition, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1)
+					: 0;
+			columnNumber = startPosition >= 0
+					? Util.searchColumnNumber(result.getLineSeparatorPositions(), lineNumber,startPosition)
+					: 0;
+		}
+		int severity;
+		switch(kind) {
+			case ERROR :
+				severity = ProblemSeverities.Error;
+				break;
+			case NOTE :
+			case OTHER:
+				severity = ProblemSeverities.Info;
+				break;
+			default :
+				severity = ProblemSeverities.Warning;
+				break;
+		}
+		return new AptProblem(
+				referenceContext,
+				fileName,
+				String.valueOf(builder),
+				0,
+				NO_ARGUMENTS,
+				severity,
+				startPosition,
+				endPosition,
+				lineNumber,
+				columnNumber);
+	}
+
+	private static Annotation findAnnotation(Annotation[] elementAnnotations, AnnotationBinding annotationBinding) {
+		for (int i = 0; i < elementAnnotations.length; i++) {
+			Annotation annotation = findAnnotation(elementAnnotations[i], annotationBinding);
+			if (annotation != null) {
+				return annotation;
+			}
+		}
+		return null;
+	}
+
+	private static Annotation findAnnotation(Annotation elementAnnotation, AnnotationBinding annotationBinding) {
+		if (annotationBinding == elementAnnotation.getCompilerAnnotation()) {
+			return elementAnnotation;
+		}
+
+		MemberValuePair[] memberValuePairs = elementAnnotation.memberValuePairs();
+		for (MemberValuePair mvp : memberValuePairs) {
+			Expression v = mvp.value;
+			if (v instanceof Annotation) {
+				Annotation a = findAnnotation((Annotation) v, annotationBinding);
+				if (a != null) {
+					return a;
+				}
+			} else if (v instanceof ArrayInitializer) {
+				Expression[] expressions = ((ArrayInitializer) v).expressions;
+				for (Expression e : expressions) {
+					if (e instanceof Annotation) {
+						Annotation a = findAnnotation((Annotation) e, annotationBinding);
+						if (a != null) {
+							return a;
+						}
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	public BaseMessagerImpl() {
+		super();
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseProcessingEnvImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseProcessingEnvImpl.java
new file mode 100644
index 0000000..bb48a0b
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BaseProcessingEnvImpl.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2021 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - fix for 342598
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import org.eclipse.jdt.internal.compiler.Compiler;
+import org.eclipse.jdt.internal.compiler.apt.model.ElementsImpl;
+import org.eclipse.jdt.internal.compiler.apt.model.Factory;
+import org.eclipse.jdt.internal.compiler.apt.model.TypesImpl;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+
+/**
+ * Implementation of ProcessingEnvironment that is common to batch and IDE environments.
+ */
+public abstract class BaseProcessingEnvImpl implements ProcessingEnvironment {
+
+	// Initialized in subclasses:
+	protected Filer _filer;
+	protected Messager _messager;
+	protected Map<String, String> _processorOptions;
+	protected Compiler _compiler;
+
+	// Initialized in this base class:
+	protected Elements _elementUtils;
+	protected Types _typeUtils;
+	private List<ICompilationUnit> _addedUnits;
+	private List<ReferenceBinding> _addedClassFiles;
+	private List<ICompilationUnit> _deletedUnits;
+	private boolean _errorRaised;
+	private Factory _factory;
+	public ModuleBinding _current_module;
+
+	public BaseProcessingEnvImpl() {
+		_addedUnits = new ArrayList<>();
+		_addedClassFiles = new ArrayList<>();
+		_deletedUnits = new ArrayList<>();
+		_elementUtils = ElementsImpl.create(this);
+		_typeUtils = new TypesImpl(this);
+		_factory = new Factory(this);
+		_errorRaised = false;
+	}
+
+	public void addNewUnit(ICompilationUnit unit) {
+		_addedUnits.add(unit);
+	}
+
+	public void addNewClassFile(ReferenceBinding binding) {
+		_addedClassFiles.add(binding);
+	}
+
+	public Compiler getCompiler() {
+		return _compiler;
+	}
+
+	public ICompilationUnit[] getDeletedUnits() {
+		ICompilationUnit[] result = new ICompilationUnit[_deletedUnits.size()];
+		_deletedUnits.toArray(result);
+		return result;
+	}
+
+	public ICompilationUnit[] getNewUnits() {
+		ICompilationUnit[] result = new ICompilationUnit[_addedUnits.size()];
+		_addedUnits.toArray(result);
+		return result;
+	}
+
+	@Override
+	public Elements getElementUtils() {
+		return _elementUtils;
+	}
+
+	@Override
+	public Filer getFiler() {
+		return _filer;
+	}
+
+	@Override
+	public Messager getMessager() {
+		return _messager;
+	}
+
+	@Override
+	public Map<String, String> getOptions() {
+		return _processorOptions;
+	}
+
+	@Override
+	public Types getTypeUtils() {
+		return _typeUtils;
+	}
+
+	public LookupEnvironment getLookupEnvironment() {
+		return _compiler.lookupEnvironment;
+	}
+
+	@Override
+	public SourceVersion getSourceVersion() {
+		if (this._compiler.options.sourceLevel <= ClassFileConstants.JDK1_5) {
+			return SourceVersion.RELEASE_5;
+		}
+		if (this._compiler.options.sourceLevel == ClassFileConstants.JDK1_6) {
+			return SourceVersion.RELEASE_6;
+		}
+		try {
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK1_7) {
+				return SourceVersion.valueOf("RELEASE_7"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK1_8) {
+				return SourceVersion.valueOf("RELEASE_8"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK9) {
+				return SourceVersion.valueOf("RELEASE_9"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK10) {
+				return SourceVersion.valueOf("RELEASE_10"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK11) {
+				return SourceVersion.valueOf("RELEASE_11"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK12) {
+				return SourceVersion.valueOf("RELEASE_12"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK13) {
+				return SourceVersion.valueOf("RELEASE_13"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK14) {
+				return SourceVersion.valueOf("RELEASE_14"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK15) {
+				return SourceVersion.valueOf("RELEASE_15"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK16) {
+				return SourceVersion.valueOf("RELEASE_16"); //$NON-NLS-1$
+			}
+			if (this._compiler.options.sourceLevel == ClassFileConstants.JDK17) {
+				return SourceVersion.valueOf("RELEASE_17"); //$NON-NLS-1$
+			}
+		} catch(IllegalArgumentException e) {
+			// handle call on a JDK 6
+			return SourceVersion.RELEASE_6;
+		}
+		// handle call on a JDK 6 by default
+		return SourceVersion.RELEASE_6;
+	}
+
+	/**
+	 * Called when AnnotationProcessorManager has retrieved the list of
+	 * newly generated compilation units (ie, once per round)
+	 */
+	public void reset() {
+		_addedUnits.clear();
+		_addedClassFiles.clear();
+		_deletedUnits.clear();
+	}
+
+	/**
+	 * Has an error been raised in any of the rounds of processing in this build?
+	 * @return
+	 */
+	public boolean errorRaised()
+	{
+		return _errorRaised;
+	}
+
+	/**
+	 * Set or clear the errorRaised flag.  Typically this will be set by the Messager
+	 * when an error has been raised, and it will never be cleared.
+	 */
+	public void setErrorRaised(boolean b)
+	{
+		_errorRaised = true;
+	}
+
+	public Factory getFactory()
+	{
+		return _factory;
+	}
+
+	public ReferenceBinding[] getNewClassFiles() {
+		ReferenceBinding[] result = new ReferenceBinding[_addedClassFiles.size()];
+		_addedClassFiles.toArray(result);
+		return result;
+	}
+	/*
+	 * This overrides ProcessingEnvironment, but can't declare so since
+	 * we are still compiling against JDK 8.
+	 */
+    public boolean isPreviewEnabled() {
+        return this._compiler.options.enablePreviewFeatures;
+    }
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchAnnotationProcessorManager.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchAnnotationProcessorManager.java
new file mode 100644
index 0000000..ebc85e1
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchAnnotationProcessorManager.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+
+import javax.annotation.processing.Processor;
+import javax.lang.model.SourceVersion;
+import javax.tools.JavaFileManager;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+
+import org.eclipse.jdt.internal.compiler.batch.Main;
+import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
+
+/**
+ * Java 6 annotation processor manager used when compiling from the command line
+ * or via the javax.tools.JavaCompiler interface.
+ * @see org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeAnnotationProcessorManager
+ */
+public class BatchAnnotationProcessorManager extends BaseAnnotationProcessorManager
+{
+
+	/**
+	 * Processors that have been set by calling CompilationTask.setProcessors().
+	 */
+	private List<Processor> _setProcessors = null;
+	private Iterator<Processor> _setProcessorIter = null;
+
+	/**
+	 * Processors named with the -processor option on the command line.
+	 */
+	private List<String> _commandLineProcessors;
+	private Iterator<String> _commandLineProcessorIter = null;
+
+	private ServiceLoader<Processor> _serviceLoader = null;
+	private Iterator<Processor> _serviceLoaderIter;
+
+	private ClassLoader _procLoader;
+
+	// Set this to true in order to trace processor discovery when -XprintProcessorInfo is specified
+	private final static boolean VERBOSE_PROCESSOR_DISCOVERY = true;
+	private boolean _printProcessorDiscovery = false;
+
+	/**
+	 * Zero-arg constructor so this object can be easily created via reflection.
+	 * A BatchAnnotationProcessorManager cannot be used until its
+	 * {@link #configure(Object, String[])} method has been called.
+	 */
+	public BatchAnnotationProcessorManager()
+	{
+	}
+
+	@Override
+	public void configure(Object batchCompiler, String[] commandLineArguments) {
+		if (null != _processingEnv) {
+			throw new IllegalStateException(
+					"Calling configure() more than once on an AnnotationProcessorManager is not supported"); //$NON-NLS-1$
+		}
+		BatchProcessingEnvImpl processingEnv = new BatchProcessingEnvImpl(this, (Main) batchCompiler, commandLineArguments);
+		_processingEnv = processingEnv;
+		@SuppressWarnings("resource") // fileManager is not opened here
+		JavaFileManager fileManager = processingEnv.getFileManager();
+		if (fileManager instanceof StandardJavaFileManager) {
+			Iterable<? extends File> location = null;
+			if (SourceVersion.latest().compareTo(SourceVersion.RELEASE_8) > 0) {
+				location = ((StandardJavaFileManager) fileManager).getLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH);
+			}
+			if (location != null) {
+				_procLoader = fileManager.getClassLoader(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH);
+			} else {
+				_procLoader = fileManager.getClassLoader(StandardLocation.ANNOTATION_PROCESSOR_PATH);
+			}
+		} else {
+			// Fall back to old code
+			_procLoader = fileManager.getClassLoader(StandardLocation.ANNOTATION_PROCESSOR_PATH);
+		}
+		parseCommandLine(commandLineArguments);
+		_round = 0;
+	}
+
+	/**
+	 * If a -processor option was specified in command line arguments,
+	 * parse it into a list of qualified classnames.
+	 * @param commandLineArguments contains one string for every space-delimited token on the command line
+	 */
+	private void parseCommandLine(String[] commandLineArguments) {
+		List<String> commandLineProcessors = null;
+		for (int i = 0; i < commandLineArguments.length; ++i) {
+			String option = commandLineArguments[i];
+			if ("-XprintProcessorInfo".equals(option)) { //$NON-NLS-1$
+				_printProcessorInfo = true;
+				_printProcessorDiscovery = VERBOSE_PROCESSOR_DISCOVERY;
+			}
+			else if ("-XprintRounds".equals(option)) { //$NON-NLS-1$
+				_printRounds = true;
+			}
+			else if ("-processor".equals(option)) { //$NON-NLS-1$
+				commandLineProcessors = new ArrayList<>();
+				String procs = commandLineArguments[++i];
+				commandLineProcessors.addAll(Arrays.asList(procs.split(","))); //$NON-NLS-1$
+				break;
+			}
+		}
+		_commandLineProcessors =  commandLineProcessors;
+		if (null != _commandLineProcessors) {
+			_commandLineProcessorIter = _commandLineProcessors.iterator();
+		}
+	}
+
+	@Override
+	public ProcessorInfo discoverNextProcessor() {
+		if (null != _setProcessors) {
+			// If setProcessors() was called, use that list until it's empty and then stop.
+			if (_setProcessorIter.hasNext()) {
+				Processor p = _setProcessorIter.next();
+				p.init(_processingEnv);
+				ProcessorInfo pi = new ProcessorInfo(p);
+				_processors.add(pi);
+				if (_printProcessorDiscovery && null != _out) {
+					_out.println("API specified processor: " + pi); //$NON-NLS-1$
+				}
+				return pi;
+			}
+			return null;
+		}
+
+		if (null != _commandLineProcessors) {
+			// If there was a -processor option, iterate over processor names,
+			// creating and initializing processors, until no more names are found, then stop.
+			if (_commandLineProcessorIter.hasNext()) {
+				String proc = _commandLineProcessorIter.next();
+				try {
+					Class<?> clazz = _procLoader.loadClass(proc);
+					Object o = clazz.getDeclaredConstructor().newInstance();
+					Processor p = (Processor) o;
+					p.init(_processingEnv);
+					ProcessorInfo pi = new ProcessorInfo(p);
+					_processors.add(pi);
+					if (_printProcessorDiscovery && null != _out) {
+						_out.println("Command line specified processor: " + pi); //$NON-NLS-1$
+					}
+					return pi;
+				} catch (Exception e) {
+					// TODO: better error handling
+					throw new AbortCompilation(null, e);
+				}
+			}
+			return null;
+		}
+
+		// if no processors were explicitly specified with setProcessors()
+		// or the command line, search the processor path with ServiceLoader.
+		if (null == _serviceLoader ) {
+			_serviceLoader = ServiceLoader.load(Processor.class, _procLoader);
+			_serviceLoaderIter = _serviceLoader.iterator();
+		}
+		try {
+			if (_serviceLoaderIter.hasNext()) {
+				Processor p = _serviceLoaderIter.next();
+				p.init(_processingEnv);
+				ProcessorInfo pi = new ProcessorInfo(p);
+				_processors.add(pi);
+				if (_printProcessorDiscovery && null != _out) {
+					StringBuilder sb = new StringBuilder();
+					sb.append("Discovered processor service "); //$NON-NLS-1$
+					sb.append(pi);
+					sb.append("\n  supporting "); //$NON-NLS-1$
+					sb.append(pi.getSupportedAnnotationTypesAsString());
+					sb.append("\n  in "); //$NON-NLS-1$
+					sb.append(getProcessorLocation(p));
+					_out.println(sb.toString());
+				}
+				return pi;
+			}
+		} catch (ServiceConfigurationError e) {
+			// TODO: better error handling
+			throw new AbortCompilation(null, e);
+		}
+		return null;
+	}
+
+	/**
+	 * Used only for debugging purposes.  Generates output like "file:jar:D:/temp/jarfiles/myJar.jar!/".
+	 * Surely this code already exists in several hundred other places?
+	 * @return the location whence a processor class was loaded.
+	 */
+	private String getProcessorLocation(Processor p) {
+		// Get the classname in a form that can be passed to ClassLoader.getResource(),
+		// e.g., "pa/pb/pc/Outer$Inner.class"
+		boolean isMember = false;
+		Class<?> outerClass = p.getClass();
+		StringBuilder innerName = new StringBuilder();
+		while (outerClass.isMemberClass()) {
+			innerName.insert(0, outerClass.getSimpleName());
+			innerName.insert(0, '$');
+			isMember = true;
+			outerClass = outerClass.getEnclosingClass();
+		}
+		String path = outerClass.getName();
+		path = path.replace('.', '/');
+		if (isMember) {
+			path = path + innerName;
+		}
+		path = path + ".class"; //$NON-NLS-1$
+
+		// Find the URL for the class resource and strip off the resource name itself
+		String location = _procLoader.getResource(path).toString();
+		if (location.endsWith(path)) {
+			location = location.substring(0, location.length() - path.length());
+		}
+		return location;
+	}
+
+	@Override
+	public void reportProcessorException(Processor p, Exception e) {
+		// TODO: if (verbose) report the processor
+		throw new AbortCompilation(null, e);
+	}
+
+	@Override
+	public void setProcessors(Object[] processors) {
+		if (!_isFirstRound) {
+			throw new IllegalStateException("setProcessors() cannot be called after processing has begun"); //$NON-NLS-1$
+		}
+		// Cast all the processors here, rather than failing later.
+		// But don't call init() until the processor is actually needed.
+		_setProcessors = new ArrayList<>(processors.length);
+		for (Object o : processors) {
+			Processor p = (Processor)o;
+			_setProcessors.add(p);
+		}
+		_setProcessorIter = _setProcessors.iterator();
+
+		// processors set this way take precedence over anything on the command line
+		_commandLineProcessors = null;
+		_commandLineProcessorIter = null;
+	}
+
+	@Override
+	protected void cleanUp() {
+		// the classloader needs to be kept open between rounds, close it at the end:
+		if (this._procLoader instanceof URLClassLoader) {
+			try {
+				((URLClassLoader) this._procLoader).close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java
new file mode 100644
index 0000000..b731cc0
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2018 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    philippe.marschall@netcetera.ch - Fix for 338370
+ *    IBM Corporation - Fix for validating relative name
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashSet;
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.FilerException;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.JavaFileManager.Location;
+
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+
+/**
+ * Implementation of Filer used when compilation is driven by command line
+ * or by Tool interface.  This version does not need to keep track of
+ * dependencies.
+ */
+public class BatchFilerImpl implements Filer {
+
+	protected final BaseAnnotationProcessorManager _dispatchManager;
+	protected final BatchProcessingEnvImpl _env;
+	protected final JavaFileManager _fileManager;
+	protected final HashSet<URI> _createdFiles;
+
+	public BatchFilerImpl(BaseAnnotationProcessorManager dispatchManager, BatchProcessingEnvImpl env)
+	{
+		_dispatchManager = dispatchManager;
+		_fileManager = env._fileManager;
+		_env = env;
+		_createdFiles = new HashSet<>();
+	}
+
+	public void addNewUnit(ICompilationUnit unit) {
+		_env.addNewUnit(unit);
+	}
+
+	public void addNewClassFile(ReferenceBinding binding) {
+		_env.addNewClassFile(binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Filer#createClassFile(java.lang.CharSequence, javax.lang.model.element.Element[])
+	 */
+	@Override
+	public JavaFileObject createClassFile(CharSequence name,
+			Element... originatingElements) throws IOException {
+		JavaFileObject jfo = _fileManager.getJavaFileForOutput(
+				StandardLocation.CLASS_OUTPUT, name.toString(), JavaFileObject.Kind.CLASS, null);
+		URI uri = jfo.toUri();
+		if (_createdFiles.contains(uri)) {
+			throw new FilerException("Class file already created : " + name); //$NON-NLS-1$
+		}
+
+		_createdFiles.add(uri);
+		return new HookedJavaFileObject(jfo, jfo.getName(), name.toString(), this);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Filer#createResource(javax.tools.JavaFileManager.Location, java.lang.CharSequence, java.lang.CharSequence, javax.lang.model.element.Element[])
+	 */
+	@Override
+	public FileObject createResource(Location location, CharSequence pkg,
+			CharSequence relativeName, Element... originatingElements)
+			throws IOException {
+		validateName(relativeName);
+		FileObject fo = _fileManager.getFileForOutput(
+				location, pkg.toString(), relativeName.toString(), null);
+		URI uri = fo.toUri();
+		if (_createdFiles.contains(uri)) {
+			throw new FilerException("Resource already created : " + location + '/' + pkg + '/' + relativeName); //$NON-NLS-1$
+		}
+
+		_createdFiles.add(uri);
+		return fo;
+	}
+
+	private static void validateName(CharSequence relativeName) {
+		int length = relativeName.length();
+		if (length == 0) {
+			throw new IllegalArgumentException("relative path cannot be empty"); //$NON-NLS-1$
+		}
+		String path = relativeName.toString();
+		if (path.indexOf('\\') != -1) {
+			// normalize the path with '/'
+			path = path.replace('\\', '/');
+		}
+		if (path.charAt(0) == '/') {
+			throw new IllegalArgumentException("relative path is absolute"); //$NON-NLS-1$
+		}
+		boolean hasDot = false;
+		for (int i = 0; i < length; i++) {
+			switch(path.charAt(i)) {
+				case '/' :
+					if (hasDot) {
+						throw new IllegalArgumentException("relative name " + relativeName + " is not relative"); //$NON-NLS-1$ //$NON-NLS-2$
+					}
+					break;
+				case '.' :
+					hasDot = true;
+					break;
+				default:
+					hasDot = false;
+			}
+		}
+		if (hasDot) {
+			throw new IllegalArgumentException("relative name " + relativeName + " is not relative"); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Filer#createSourceFile(java.lang.CharSequence, javax.lang.model.element.Element[])
+	 */
+	@Override
+	public JavaFileObject createSourceFile(CharSequence name,
+			Element... originatingElements) throws IOException {
+		String moduleAndPkgString = name.toString();
+		int slash = moduleAndPkgString.indexOf('/');
+		String mod = null;
+		if (slash != -1) {
+			name = moduleAndPkgString.substring(slash + 1, name.length());
+			mod = moduleAndPkgString.substring(0, slash);
+		}
+		TypeElement typeElement = _env._elementUtils.getTypeElement(name);
+		if (typeElement != null) {
+			throw new FilerException("Source file already exists : " + moduleAndPkgString); //$NON-NLS-1$
+		}
+		Location location = mod == null ? StandardLocation.SOURCE_OUTPUT : _fileManager.getLocationForModule(StandardLocation.SOURCE_OUTPUT, mod);
+		JavaFileObject jfo = _fileManager.getJavaFileForOutput(location, name.toString(), JavaFileObject.Kind.SOURCE, null);
+		URI uri = jfo.toUri();
+		if (_createdFiles.contains(uri)) {
+			throw new FilerException("Source file already created : " + name); //$NON-NLS-1$
+		}
+
+		_createdFiles.add(uri);
+		// hook the file object's writers to create compilation unit and add to addedUnits()
+		return new HookedJavaFileObject(jfo, jfo.getName(), name.toString(), this);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Filer#getResource(javax.tools.JavaFileManager.Location, java.lang.CharSequence, java.lang.CharSequence)
+	 */
+	@Override
+	public FileObject getResource(Location location, CharSequence pkg,
+			CharSequence relativeName) throws IOException {
+		validateName(relativeName);
+		FileObject fo = _fileManager.getFileForInput(
+				location, pkg.toString(), relativeName.toString());
+		if (fo == null) {
+			throw new FileNotFoundException("Resource does not exist : " + location + '/' + pkg + '/' + relativeName); //$NON-NLS-1$
+		}
+		URI uri = fo.toUri();
+		if (_createdFiles.contains(uri)) {
+			throw new FilerException("Resource already created : " + location + '/' + pkg + '/' + relativeName); //$NON-NLS-1$
+		}
+		return fo;
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchMessagerImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchMessagerImpl.java
new file mode 100644
index 0000000..7a713fe
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchMessagerImpl.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2006-2009 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic.Kind;
+
+import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import org.eclipse.jdt.internal.compiler.batch.Main;
+
+/**
+ * An implementation of Messager that reports messages via the Compiler
+ */
+public class BatchMessagerImpl extends BaseMessagerImpl implements Messager {
+
+	private final Main _compiler;
+	private final BaseProcessingEnvImpl _processingEnv;
+
+	public BatchMessagerImpl(BaseProcessingEnvImpl processingEnv, Main compiler) {
+		_compiler = compiler;
+		_processingEnv = processingEnv;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Messager#printMessage(javax.tools.Diagnostic.Kind, java.lang.CharSequence)
+	 */
+	@Override
+	public void printMessage(Kind kind, CharSequence msg) {
+		printMessage(kind, msg, null, null, null);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Messager#printMessage(javax.tools.Diagnostic.Kind, java.lang.CharSequence, javax.lang.model.element.Element)
+	 */
+	@Override
+	public void printMessage(Kind kind, CharSequence msg, Element e) {
+		printMessage(kind, msg, e, null, null);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Messager#printMessage(javax.tools.Diagnostic.Kind, java.lang.CharSequence, javax.lang.model.element.Element, javax.lang.model.element.AnnotationMirror)
+	 */
+	@Override
+	public void printMessage(Kind kind, CharSequence msg, Element e,
+			AnnotationMirror a) {
+		printMessage(kind, msg, e, a, null);
+
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.annotation.processing.Messager#printMessage(javax.tools.Diagnostic.Kind, java.lang.CharSequence, javax.lang.model.element.Element, javax.lang.model.element.AnnotationMirror, javax.lang.model.element.AnnotationValue)
+	 */
+	@Override
+	public void printMessage(Kind kind, CharSequence msg, Element e,
+			AnnotationMirror a, AnnotationValue v) {
+		if (kind == Kind.ERROR) {
+			_processingEnv.setErrorRaised(true);
+		}
+		CategorizedProblem problem = createProblem(kind, msg, e, a, v);
+		if (problem != null) {
+			this._compiler.addExtraProblems(problem);
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchProcessingEnvImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchProcessingEnvImpl.java
new file mode 100644
index 0000000..cbf8abe
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchProcessingEnvImpl.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2015 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.lang.reflect.Field;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.tools.JavaFileManager;
+
+import org.eclipse.jdt.internal.compiler.apt.util.EclipseFileManager;
+import org.eclipse.jdt.internal.compiler.batch.Main;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
+
+/**
+ * The implementation of ProcessingEnvironment that is used when compilation is
+ * driven by the command line or by the Tool interface.  This environment uses
+ * the JavaFileManager provided by the compiler.
+ * @see org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeProcessingEnvImpl
+ */
+public class BatchProcessingEnvImpl extends BaseProcessingEnvImpl {
+
+	protected final BaseAnnotationProcessorManager _dispatchManager;
+	protected final JavaFileManager _fileManager;
+	protected final Main _compilerOwner;
+
+	public BatchProcessingEnvImpl(BaseAnnotationProcessorManager dispatchManager, Main batchCompiler,
+			String[] commandLineArguments)
+	{
+		super();
+		_compilerOwner = batchCompiler;
+		_compiler = batchCompiler.batchCompiler;
+		_dispatchManager = dispatchManager;
+		Class<?> c = null;
+		try {
+			c = Class.forName("org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl"); //$NON-NLS-1$
+		} catch (ClassNotFoundException e) {
+			// ignore
+		}
+		Field field = null;
+		JavaFileManager javaFileManager = null;
+		if (c != null) {
+			try {
+				field = c.getField("fileManager"); //$NON-NLS-1$
+			} catch (SecurityException e) {
+				// ignore
+			} catch (IllegalArgumentException e) {
+				// ignore
+			} catch (NoSuchFieldException e) {
+				// ignore
+			}
+		}
+		if (field != null) {
+			try {
+				javaFileManager = (JavaFileManager) field.get(batchCompiler);
+			} catch (IllegalArgumentException e) {
+				// ignore
+			} catch (IllegalAccessException e) {
+				// ignore
+			}
+		}
+		if (javaFileManager != null) {
+			_fileManager = javaFileManager;
+		} else {
+			String encoding = (String) batchCompiler.options.get(CompilerOptions.OPTION_Encoding);
+			Charset charset = encoding != null ? Charset.forName(encoding) : null;
+			JavaFileManager manager = new EclipseFileManager(batchCompiler.compilerLocale, charset);
+			ArrayList<String> options = new ArrayList<>();
+			options.addAll(Arrays.asList(commandLineArguments));
+			for (Iterator<String> iterator = options.iterator(); iterator.hasNext(); ) {
+				manager.handleOption(iterator.next(), iterator);
+			}
+			_fileManager = manager;
+		}
+		_processorOptions = Collections.unmodifiableMap(parseProcessorOptions(commandLineArguments));
+		_filer = new BatchFilerImpl(_dispatchManager, this);
+		_messager = new BatchMessagerImpl(this, _compilerOwner);
+	}
+
+	/**
+	 * Parse the -A command line arguments so that they can be delivered to
+	 * processors with {@link javax.annotation.processing.ProcessingEnvironment#getOptions()}.  In Sun's Java 6
+	 * version of javac, unlike in the Java 5 apt tool, only the -A options are
+	 * passed to processors, not the other command line options; that behavior
+	 * is repeated here.
+	 * @param args the equivalent of the args array from the main() method.
+	 * @return a map of key to value, or key to null if there is no value for
+	 * a particular key.  The "-A" is stripped from the key, so a command-line
+	 * argument like "-Afoo=bar" will result in an entry with key "foo" and
+	 * value "bar".
+	 */
+	private Map<String, String> parseProcessorOptions(String[] args) {
+		Map<String, String> options = new LinkedHashMap<>();
+		for (String arg : args) {
+			if (!arg.startsWith("-A")) { //$NON-NLS-1$
+				continue;
+			}
+			int equals = arg.indexOf('=');
+			if (equals == 2) {
+				// option begins "-A=" - not valid
+				Exception e = new IllegalArgumentException("-A option must have a key before the equals sign"); //$NON-NLS-1$
+				throw new AbortCompilation(null, e);
+			}
+			if (equals == arg.length() - 1) {
+				// option ends with "=" - not valid
+				options.put(arg.substring(2, equals), null);
+			} else if (equals == -1) {
+				// no value
+				options.put(arg.substring(2), null);
+			} else {
+				// value and key
+				options.put(arg.substring(2, equals), arg.substring(equals + 1));
+			}
+		}
+		return options;
+	}
+
+	public JavaFileManager getFileManager() {
+		return _fileManager;
+	}
+
+	@Override
+	public Locale getLocale() {
+		return _compilerOwner.compilerLocale;
+	}
+
+	public boolean shouldIgnoreOptionalProblems(char[] fileName) {
+		return Main.shouldIgnoreOptionalProblems(this._compilerOwner.ignoreOptionalProblemsFromFolders, fileName);
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/HookedJavaFileObject.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/HookedJavaFileObject.java
new file mode 100644
index 0000000..b06e9f9
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/HookedJavaFileObject.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2014 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+import javax.tools.ForwardingJavaFileObject;
+import javax.tools.JavaFileObject;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.env.IBinaryType;
+import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+
+/**
+ * A delegating JavaFileObject that hooks the close() methods of the Writer
+ * or OutputStream objects that it produces, and notifies the annotation
+ * dispatch manager when a new compilation unit is produced.
+ */
+public class HookedJavaFileObject extends
+		ForwardingJavaFileObject<JavaFileObject>
+{
+	// A delegating Writer that passes all commands to its contained Writer,
+	// but hooks close() to notify the annotation dispatch manager of the new unit.
+	private class ForwardingWriter extends Writer {
+		private final Writer _w;
+		ForwardingWriter(Writer w) {
+			_w = w;
+		}
+		@Override
+		public Writer append(char c) throws IOException {
+			return _w.append(c);
+		}
+		@Override
+		public Writer append(CharSequence csq, int start, int end)
+				throws IOException {
+			return _w.append(csq, start, end);
+		}
+		@Override
+		public Writer append(CharSequence csq) throws IOException {
+			return _w.append(csq);
+		}
+		// This is the only interesting method - it has to notify the
+		// dispatch manager of the new file.
+		@Override
+		public void close() throws IOException {
+			_w.close();
+			closed();
+		}
+		@Override
+		public void flush() throws IOException {
+			_w.flush();
+		}
+		@Override
+		public void write(char[] cbuf) throws IOException {
+			_w.write(cbuf);
+		}
+		@Override
+		public void write(int c) throws IOException {
+			_w.write(c);
+		}
+		@Override
+		public void write(String str, int off, int len)
+				throws IOException {
+			_w.write(str, off, len);
+		}
+		@Override
+		public void write(String str) throws IOException {
+			_w.write(str);
+		}
+		@Override
+		public void write(char[] cbuf, int off, int len)
+		throws IOException {
+			_w.write(cbuf, off, len);
+		}
+		@Override
+		protected Object clone() throws CloneNotSupportedException {
+			return new ForwardingWriter(this._w);
+		}
+		@Override
+		public int hashCode() {
+			return _w.hashCode();
+		}
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			final ForwardingWriter other = (ForwardingWriter) obj;
+			if (_w == null) {
+				if (other._w != null)
+					return false;
+			} else if (!_w.equals(other._w))
+				return false;
+			return true;
+		}
+		@Override
+		public String toString() {
+			return "ForwardingWriter wrapping " + _w.toString(); //$NON-NLS-1$
+		}
+	}
+
+	// A delegating Writer that passes all commands to its contained Writer,
+	// but hooks close() to notify the annotation dispatch manager of the new unit.
+	private class ForwardingOutputStream extends OutputStream {
+		private final OutputStream _os;
+
+		ForwardingOutputStream(OutputStream os) {
+			_os = os;
+		}
+
+		@Override
+		public void close() throws IOException {
+			_os.close();
+			closed();
+		}
+		@Override
+		public void flush() throws IOException {
+			_os.flush();
+		}
+		@Override
+		public void write(byte[] b, int off, int len) throws IOException {
+			_os.write(b, off, len);
+		}
+		@Override
+		public void write(byte[] b) throws IOException {
+			_os.write(b);
+		}
+		@Override
+		public void write(int b) throws IOException {
+			_os.write(b);
+		}
+		@Override
+		protected Object clone() throws CloneNotSupportedException {
+			return new ForwardingOutputStream(this._os);
+		}
+		@Override
+		public int hashCode() {
+			return _os.hashCode();
+		}
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			final ForwardingOutputStream other = (ForwardingOutputStream) obj;
+			if (_os == null) {
+				if (other._os != null)
+					return false;
+			} else if (!_os.equals(other._os))
+				return false;
+			return true;
+		}
+		@Override
+		public String toString() {
+			return "ForwardingOutputStream wrapping " + _os.toString(); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * The Filer implementation that we need to notify when a new file is created.
+	 */
+	protected final BatchFilerImpl _filer;
+
+	/**
+	 * The name of the file that is created; this is passed to the CompilationUnit constructor,
+	 * and ultimately to the java.io.File constructor, so it is a normal pathname, just like
+	 * what would be on the compiler command line.
+	 */
+	protected final String _fileName;
+
+
+
+	/**
+	 * A compilation unit is created when the writer or stream is closed.  Only do this once.
+	 */
+	private boolean _closed = false;
+
+	private String _typeName;
+
+	public HookedJavaFileObject(JavaFileObject fileObject, String fileName, String typeName, BatchFilerImpl filer) {
+		super(fileObject);
+		_filer = filer;
+		_fileName = fileName;
+		_typeName = typeName;
+	}
+
+	@SuppressWarnings("resource") // ForwardingOutputStream forwards close() too
+	@Override
+	public OutputStream openOutputStream() throws IOException {
+		return new ForwardingOutputStream(super.openOutputStream());
+	}
+
+	@SuppressWarnings("resource") // ForwardingWriter forwards close() too
+	@Override
+	public Writer openWriter() throws IOException {
+		return new ForwardingWriter(super.openWriter());
+	}
+
+	protected void closed() {
+		if (!_closed) {
+			_closed = true;
+			//TODO: support encoding
+			switch(this.getKind()) {
+				case SOURCE :
+					CompilationUnit unit = new CompilationUnit(null, _fileName, null /* encoding */, null, this._filer._env.shouldIgnoreOptionalProblems(_fileName.toCharArray()), null);
+					_filer.addNewUnit(unit);
+					break;
+				case CLASS :
+					IBinaryType binaryType = null;
+					try {
+						binaryType = ClassFileReader.read(_fileName);
+					} catch (ClassFormatException e) {
+						/* When the annotation processor produces garbage, javac seems to show some resilience, by hooking the source type,
+						   which since is resolved can answer annotations during discovery - Not sure if this sanctioned by the spec, to be taken
+						   up with Oracle. Here we mimic the bug, see that addNewClassFile is simply collecting ReferenceBinding's, so adding
+						   a SourceTypeBinding works just fine.
+						*/
+						ReferenceBinding type = this._filer._env._compiler.lookupEnvironment.getType(CharOperation.splitOn('.', _typeName.toCharArray()));
+						if (type != null)
+							_filer.addNewClassFile(type);
+					} catch (IOException e) {
+						// ignore
+					}
+					if (binaryType != null) {
+						char[] name = binaryType.getName();
+						ReferenceBinding type = this._filer._env._compiler.lookupEnvironment.getType(CharOperation.splitOn('/', name));
+						if (type != null && type.isValidBinding()) {
+							if (type.isBinaryBinding()) {
+								_filer.addNewClassFile(type);
+							} else {
+								BinaryTypeBinding binaryBinding = new BinaryTypeBinding(type.getPackage(), binaryType, this._filer._env._compiler.lookupEnvironment, true);
+								if (binaryBinding != null)
+									_filer.addNewClassFile(binaryBinding);
+							}
+						}
+					}
+					break;
+				case HTML:
+				case OTHER:
+					break;
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/IProcessorProvider.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/IProcessorProvider.java
new file mode 100644
index 0000000..5e88da3
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/IProcessorProvider.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.util.List;
+
+import javax.annotation.processing.Processor;
+
+/**
+ * Implementors know how to discover annotation processors, and maintain a list of processors that
+ * have been discovered and initialized so far.
+ */
+public interface IProcessorProvider {
+
+	/**
+	 * Return the next processor that can be discovered, according to the order and discovery rules
+	 *         of the provider (see, for instance, {@link Processor}.
+	 * @return a ProcessorInfo wrapping an initialized Processor, or <code>null</code> if there are
+	 * no more processors to be discovered.
+	 */
+	ProcessorInfo discoverNextProcessor();
+
+	/**
+	 * @return the list of all processors that have been discovered so far. This list will grow when
+	 *         {@link #discoverNextProcessor()} is called.
+	 */
+	List<ProcessorInfo> getDiscoveredProcessors();
+
+	/**
+	 * Called when a processor throws an exception.  This may abort compilation, throw an
+	 * unchecked exception, etc; the caller should not assume that this method will return.
+	 *
+	 * @param p the processor, if known, or null if not.
+	 * @param e
+	 */
+	void reportProcessorException(Processor p, Exception e);
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/ProcessorInfo.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/ProcessorInfo.java
new file mode 100644
index 0000000..c33a945
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/ProcessorInfo.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.processing.Processor;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Cached information associated with a {@link Processor} in the context
+ * of annotation processor dispatch.
+ * <p>
+ * This class supports inclusion in a collection by implementing
+ * equals() and hashCode().  Its concept of identity is based on
+ * the class object of the Processor that it wraps; so, for instance,
+ * it is not possible to have a Set that contains more than one
+ * instance of a particular Processor class.  In fact, it is possible
+ * to have more than one instance of a Processor if there are multiple
+ * build threads, but within the context of a particular dispatch
+ * manager, there will only be one of any given Processor class.
+ */
+public class ProcessorInfo {
+	final Processor _processor;
+	final Set<String> _supportedOptions;
+	final SourceVersion _supportedSourceVersion;
+
+	private final Pattern _supportedAnnotationTypesPattern;
+	private final boolean _supportsStar;
+	private boolean _hasBeenCalled;
+
+	/**
+	 * Create a ProcessorInfo wrapping a particular Processor. The Processor must already have been
+	 * initialized (that is,
+	 * {@link Processor#init(javax.annotation.processing.ProcessingEnvironment)} must already have
+	 * been called). Its getSupportedXXX() methods will be called and the results will be cached.
+	 */
+	public ProcessorInfo(Processor p)
+	{
+		_processor = p;
+		_hasBeenCalled = false;
+		_supportedSourceVersion = p.getSupportedSourceVersion();
+		_supportedOptions = p.getSupportedOptions();
+		Set<String> supportedAnnotationTypes = p.getSupportedAnnotationTypes();
+
+		boolean supportsStar = false;
+		if (null != supportedAnnotationTypes && !supportedAnnotationTypes.isEmpty()) {
+			StringBuilder regex = new StringBuilder();
+			Iterator<String> iName = supportedAnnotationTypes.iterator();
+			while (true) {
+				String name = iName.next();
+				supportsStar |= "*".equals(name);  //$NON-NLS-1$
+				String escapedName1 = name.replace(".", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
+				String escapedName2 = escapedName1.replace("*", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
+				regex.append(escapedName2);
+				if (!iName.hasNext()) {
+					break;
+				}
+				regex.append('|');
+			}
+			_supportedAnnotationTypesPattern = Pattern.compile(regex.toString());
+		}
+		else {
+			_supportedAnnotationTypesPattern = null;
+		}
+		_supportsStar = supportsStar;
+	}
+
+	/**
+	 * Compute the subset of <code>annotations</code> that are described by <code>annotationTypes</code>,
+	 * and determine whether the processor should be called.  A processor will be called if it has
+	 * any annotations to process, or if it supports "*", or if it was called in a previous round.
+	 * If the return value of this method is true once for a given processor, then it will always be true on
+	 * subsequent calls.
+	 *
+	 * @param annotations a set of annotation types
+	 * @param result an empty modifiable set, which upon return will contain a subset of <code>annotations</code>, which may be empty but will not be null.
+	 * @return true if the processor should be called on this round.
+	 */
+	public boolean computeSupportedAnnotations(Set<TypeElement> annotations, Set<TypeElement> result)
+	{
+		if (null != annotations && !annotations.isEmpty() && null != _supportedAnnotationTypesPattern) {
+			for (TypeElement annotation : annotations) {
+				Matcher matcher = _supportedAnnotationTypesPattern.matcher(annotation.getQualifiedName().toString());
+				if (matcher.matches()) {
+					result.add(annotation);
+				}
+			}
+		}
+		boolean call = _hasBeenCalled || _supportsStar || !result.isEmpty();
+		_hasBeenCalled |= call;
+		return call;
+	}
+
+	/**
+	 * @return true if the processor included "*" among its list of supported annotations.
+	 */
+	public boolean supportsStar()
+	{
+		return _supportsStar;
+	}
+
+	/**
+	 * Must be called at the beginning of a build to ensure that no information is
+	 * carried over from the previous build.  In particular, processors are
+	 * required to be called on every round after the round in which they are
+	 * first called; this method resets the "has been called" flag.
+	 */
+	public void reset()
+	{
+		_hasBeenCalled = false;
+	}
+
+	@Override
+	public int hashCode() {
+		return _processor.getClass().hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final ProcessorInfo other = (ProcessorInfo) obj;
+		if (!_processor.getClass().equals(other._processor.getClass()))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString()
+	{
+		return _processor.getClass().getName();
+	}
+
+	/**
+	 * @return a string representing the set of supported annotation types, in a format
+	 * suitable for debugging.  The format is unspecified and subject to change.
+	 */
+	public String getSupportedAnnotationTypesAsString()
+	{
+		StringBuilder sb = new StringBuilder();
+		sb.append('[');
+		Iterator<String> iAnnots = _processor.getSupportedAnnotationTypes().iterator();
+		boolean hasNext = iAnnots.hasNext();
+		while (hasNext) {
+			sb.append(iAnnots.next());
+			hasNext = iAnnots.hasNext();
+			if (hasNext) {
+				sb.append(',');
+			}
+		}
+		sb.append(']');
+		return sb.toString();
+	}
+}
+
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/RoundDispatcher.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/RoundDispatcher.java
new file mode 100644
index 0000000..84d4d26
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/RoundDispatcher.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2015 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Manages context during a single round of annotation processing.
+ */
+public class RoundDispatcher {
+
+	private final Set<TypeElement> _unclaimedAnnotations;
+	private final RoundEnvironment _roundEnv;
+	private final IProcessorProvider _provider;
+	private boolean _searchForStar = false;
+	private final PrintWriter _traceProcessorInfo;
+	private final PrintWriter _traceRounds;
+
+	/**
+	 * Processors discovered so far.  This list may grow during the
+	 * course of a round, as additional processors are discovered.
+	 */
+	private final List<ProcessorInfo> _processors;
+
+	/**
+	 * @param rootAnnotations a possibly empty but non-null set of annotations on the
+	 * root compilation units of this round.  A local copy of the set will be made, to
+	 * avoid modifying the set passed in.
+	 * @param traceProcessorInfo a PrintWriter that processor trace output will be sent
+	 * to, or null if tracing is not desired.
+	 * @param traceRounds
+	 */
+	public RoundDispatcher(
+			IProcessorProvider provider,
+			RoundEnvironment env,
+			Set<TypeElement> rootAnnotations,
+			PrintWriter traceProcessorInfo,
+			PrintWriter traceRounds)
+	{
+		_provider = provider;
+		_processors = provider.getDiscoveredProcessors();
+		_roundEnv = env;
+		_unclaimedAnnotations = new HashSet<>(rootAnnotations);
+		_traceProcessorInfo = traceProcessorInfo;
+		_traceRounds = traceRounds;
+	}
+
+	/**
+	 * Handle a complete round, dispatching to all appropriate processors.
+	 */
+	public void round()
+	{
+		if (null != _traceRounds) {
+			StringBuilder sbElements = new StringBuilder();
+			sbElements.append("\tinput files: {"); //$NON-NLS-1$
+			Iterator<? extends Element> iElements = _roundEnv.getRootElements().iterator();
+			boolean hasNext = iElements.hasNext();
+			while (hasNext) {
+				sbElements.append(iElements.next());
+				hasNext = iElements.hasNext();
+				if (hasNext) {
+					sbElements.append(',');
+				}
+			}
+			sbElements.append('}');
+			_traceRounds.println(sbElements.toString());
+
+			StringBuilder sbAnnots = new StringBuilder();
+			sbAnnots.append("\tannotations: ["); //$NON-NLS-1$
+			Iterator<TypeElement> iAnnots = _unclaimedAnnotations.iterator();
+			hasNext = iAnnots.hasNext();
+			while (hasNext) {
+				sbAnnots.append(iAnnots.next());
+				hasNext = iAnnots.hasNext();
+				if (hasNext) {
+					sbAnnots.append(',');
+				}
+			}
+			sbAnnots.append(']');
+			_traceRounds.println(sbAnnots.toString());
+
+			_traceRounds.println("\tlast round: " + _roundEnv.processingOver()); //$NON-NLS-1$
+		}
+
+		// If there are no root annotations, try to find a processor that claims "*"
+		_searchForStar = _unclaimedAnnotations.isEmpty();
+
+		// Iterate over all the already-found processors, giving each one a chance at the unclaimed
+		// annotations. If a processor is called at all, it is called on every subsequent round
+		// including the final round, but it may be called with an empty set of annotations.
+		for (ProcessorInfo pi : _processors) {
+			handleProcessor(pi);
+		}
+
+		// If there are any unclaimed annotations, or if there were no root annotations and
+		// we have not yet run into a processor that claimed "*", continue discovery.
+		while (_searchForStar || !_unclaimedAnnotations.isEmpty()) {
+			ProcessorInfo pi = _provider.discoverNextProcessor();
+			if (null == pi) {
+				// There are no more processors to be discovered.
+				break;
+			}
+			handleProcessor(pi);
+		}
+
+		// TODO: If !unclaimedAnnos.isEmpty(), issue a warning.
+	}
+
+	/**
+	 * Evaluate a single processor.  Depending on the unclaimed annotations,
+	 * the annotations this processor supports, and whether it has already been
+	 * called in a previous round, possibly call its process() method.
+	 */
+	private void handleProcessor(ProcessorInfo pi)
+	{
+		try {
+			Set<TypeElement> annotationsToProcess = new HashSet<>();
+			boolean shouldCall = pi.computeSupportedAnnotations(
+					_unclaimedAnnotations, annotationsToProcess);
+			if (shouldCall) {
+				boolean claimed = pi._processor.process(annotationsToProcess, _roundEnv);
+				if (null != _traceProcessorInfo && !_roundEnv.processingOver()) {
+					StringBuilder sb = new StringBuilder();
+					sb.append("Processor "); //$NON-NLS-1$
+					sb.append(pi._processor.getClass().getName());
+					sb.append(" matches ["); //$NON-NLS-1$
+					Iterator<TypeElement> i = annotationsToProcess.iterator();
+					boolean hasNext = i.hasNext();
+					while (hasNext) {
+						sb.append(i.next());
+						hasNext = i.hasNext();
+						if (hasNext) {
+							sb.append(' ');
+						}
+					}
+					sb.append("] and returns "); //$NON-NLS-1$
+					sb.append(claimed);
+					_traceProcessorInfo.println(sb.toString());
+				}
+				if (claimed) {
+					// The processor claimed its annotations.
+					_unclaimedAnnotations.removeAll(annotationsToProcess);
+					if (pi.supportsStar()) {
+						_searchForStar = false;
+					}
+				}
+			}
+		} catch (Throwable e) {
+			// If a processor throws an exception (as opposed to reporting an error),
+			// report it and abort compilation by throwing AbortCompilation.
+			_provider.reportProcessorException(pi._processor, new Exception(e));
+		}
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/RoundEnvImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/RoundEnvImpl.java
new file mode 100644
index 0000000..3f8515c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/RoundEnvImpl.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    IBM Corporation - initial API and implementation
+ *    IBM Corporation - Fix for bug 328575
+ *    het@google.com - Bug 415274 - Annotation processing throws a NPE in getElementsAnnotatedWith()
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.dispatch;
+
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+
+import org.eclipse.jdt.internal.compiler.apt.model.Factory;
+import org.eclipse.jdt.internal.compiler.apt.model.TypeElementImpl;
+import org.eclipse.jdt.internal.compiler.apt.util.ManyToMany;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TagBits;
+
+public class RoundEnvImpl implements RoundEnvironment
+{
+	private final BaseProcessingEnvImpl _processingEnv;
+	private final boolean _isLastRound;
+	private final CompilationUnitDeclaration[] _units;
+	private final ManyToMany<TypeElement, Element> _annoToUnit;
+	private final ReferenceBinding[] _binaryTypes;
+	private final Factory _factory;
+	private Set<Element> _rootElements = null;
+
+	public RoundEnvImpl(CompilationUnitDeclaration[] units, ReferenceBinding[] binaryTypeBindings, boolean isLastRound, BaseProcessingEnvImpl env) {
+		_processingEnv = env;
+		_isLastRound = isLastRound;
+		_units = units;
+		_factory = _processingEnv.getFactory();
+
+		// Discover the annotations that will be passed to Processor.process()
+		AnnotationDiscoveryVisitor visitor = new AnnotationDiscoveryVisitor(_processingEnv);
+		if (_units != null) {
+			for (CompilationUnitDeclaration unit : _units) {
+				unit.scope.environment.suppressImportErrors = true;
+				unit.traverse(visitor, unit.scope);
+				unit.scope.environment.suppressImportErrors = false;
+			}
+		}
+		_annoToUnit = visitor._annoToElement;
+		if (binaryTypeBindings != null) collectAnnotations(binaryTypeBindings);
+		_binaryTypes = binaryTypeBindings;
+	}
+
+	private void collectAnnotations(ReferenceBinding[] referenceBindings) {
+		for (ReferenceBinding referenceBinding : referenceBindings) {
+			// collect all annotations from the binary types
+			if (referenceBinding instanceof ParameterizedTypeBinding) {
+				referenceBinding = ((ParameterizedTypeBinding) referenceBinding).genericType();
+			}
+			AnnotationBinding[] annotationBindings = Factory.getPackedAnnotationBindings(referenceBinding.getAnnotations());
+			for (AnnotationBinding annotationBinding : annotationBindings) {
+				TypeElement anno = (TypeElement)_factory.newElement(annotationBinding.getAnnotationType());
+				Element element = _factory.newElement(referenceBinding);
+				_annoToUnit.put(anno, element);
+			}
+			FieldBinding[] fieldBindings = referenceBinding.fields();
+			for (FieldBinding fieldBinding : fieldBindings) {
+				annotationBindings = Factory.getPackedAnnotationBindings(fieldBinding.getAnnotations());
+				for (AnnotationBinding annotationBinding : annotationBindings) {
+					TypeElement anno = (TypeElement)_factory.newElement(annotationBinding.getAnnotationType());
+					Element element = _factory.newElement(fieldBinding);
+					_annoToUnit.put(anno, element);
+				}
+			}
+			MethodBinding[] methodBindings = referenceBinding.methods();
+			for (MethodBinding methodBinding : methodBindings) {
+				annotationBindings = Factory.getPackedAnnotationBindings(methodBinding.getAnnotations());
+				for (AnnotationBinding annotationBinding : annotationBindings) {
+					TypeElement anno = (TypeElement)_factory.newElement(annotationBinding.getAnnotationType());
+					Element element = _factory.newElement(methodBinding);
+					_annoToUnit.put(anno, element);
+				}
+			}
+			ReferenceBinding[] memberTypes = referenceBinding.memberTypes();
+			collectAnnotations(memberTypes);
+		}
+	}
+
+	/**
+	 * Return the set of annotation types that were discovered on the root elements.
+	 * This does not include inherited annotations, only those directly on the root
+	 * elements.
+	 * @return a set of annotation types, possibly empty.
+	 */
+	public Set<TypeElement> getRootAnnotations()
+	{
+		return Collections.unmodifiableSet(_annoToUnit.getKeySet());
+	}
+
+	@Override
+	public boolean errorRaised()
+	{
+		return _processingEnv.errorRaised();
+	}
+
+	/**
+	 * From the set of root elements and their enclosed elements, return the subset that are annotated
+	 * with {@code a}.  If {@code a} is annotated with the {@link java.lang.annotation.Inherited}
+	 * annotation, include those elements that inherit the annotation from their superclasses.
+	 * Note that {@link java.lang.annotation.Inherited} only applies to classes (i.e. TypeElements).
+	 */
+	@Override
+	public Set<? extends Element> getElementsAnnotatedWith(TypeElement a)
+	{
+		if (a.getKind() != ElementKind.ANNOTATION_TYPE) {
+			throw new IllegalArgumentException("Argument must represent an annotation type"); //$NON-NLS-1$
+		}
+		Binding annoBinding = ((TypeElementImpl)a)._binding;
+		if (0 != (annoBinding.getAnnotationTagBits() & TagBits.AnnotationInherited)) {
+			Set<Element> annotatedElements = new HashSet<>(_annoToUnit.getValues(a));
+			// For all other root elements that are TypeElements, and for their recursively enclosed
+			// types, add each element if it has a superclass are annotated with 'a'
+			ReferenceBinding annoTypeBinding = (ReferenceBinding) annoBinding;
+			for (TypeElement element : ElementFilter.typesIn(getRootElements())) {
+				ReferenceBinding typeBinding = (ReferenceBinding)((TypeElementImpl)element)._binding;
+				addAnnotatedElements(annoTypeBinding, typeBinding, annotatedElements);
+			}
+			return Collections.unmodifiableSet(annotatedElements);
+		}
+		return Collections.unmodifiableSet(_annoToUnit.getValues(a));
+	}
+
+	/**
+	 * For every type in types that is a class and that is annotated with anno, either directly or by inheritance,
+	 * add that type to result.  Recursively descend on each types's child classes as well.
+	 * @param anno the compiler binding for an annotation type
+	 * @param type a type, not necessarily a class
+	 * @param result must be a modifiable Set; will accumulate annotated classes
+	 */
+	private void addAnnotatedElements(ReferenceBinding anno, ReferenceBinding type, Set<Element> result) {
+		if (type.isClass()) {
+			if (inheritsAnno(type, anno)) {
+				result.add(_factory.newElement(type));
+			}
+		}
+		for (ReferenceBinding element : type.memberTypes()) {
+			addAnnotatedElements(anno, element, result);
+		}
+	}
+
+	/**
+	 * Check whether an element has a superclass that is annotated with an @Inherited annotation.
+	 * @param element must be a class (not an interface, enum, etc.).
+	 * @param anno must be an annotation type, and must be @Inherited
+	 * @return true if element has a superclass that is annotated with anno
+	 */
+	private boolean inheritsAnno(ReferenceBinding element, ReferenceBinding anno) {
+		ReferenceBinding searchedElement = element;
+		do {
+			if (searchedElement instanceof ParameterizedTypeBinding) {
+				searchedElement = ((ParameterizedTypeBinding) searchedElement).genericType();
+			}
+			AnnotationBinding[] annos = Factory.getPackedAnnotationBindings(searchedElement.getAnnotations());
+			for (AnnotationBinding annoBinding : annos) {
+				if (annoBinding.getAnnotationType() == anno) { //$IDENTITY-COMPARISON$
+					// element is annotated with anno
+					return true;
+				}
+			}
+		} while (null != (searchedElement = searchedElement.superclass()));
+		return false;
+	}
+
+	@Override
+	public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a)
+	{
+		String canonicalName = a.getCanonicalName();
+		if (canonicalName == null) {
+			// null for anonymous and local classes or an array of those
+			throw new IllegalArgumentException("Argument must represent an annotation type"); //$NON-NLS-1$
+		}
+		TypeElement annoType = _processingEnv.getElementUtils().getTypeElement(canonicalName);
+		if (annoType == null) {
+			return Collections.emptySet();
+		}
+		return getElementsAnnotatedWith(annoType);
+	}
+
+	@Override
+	public Set<? extends Element> getRootElements()
+	{
+		if (_units == null) {
+			return Collections.emptySet();
+		}
+		if (_rootElements == null) {
+			Set<Element> elements = new HashSet<>(_units.length);
+			for (CompilationUnitDeclaration unit : _units) {
+				if (unit.moduleDeclaration != null && unit.moduleDeclaration.binding != null) {
+					Element m = _factory.newElement(unit.moduleDeclaration.binding);
+					elements.add(m);
+					continue;
+				}
+				if (null == unit.scope || null == unit.scope.topLevelTypes)
+					continue;
+				for (SourceTypeBinding binding : unit.scope.topLevelTypes) {
+					Element element = _factory.newElement(binding);
+					if (null == element) {
+						throw new IllegalArgumentException("Top-level type binding could not be converted to element: " + binding); //$NON-NLS-1$
+					}
+					elements.add(element);
+				}
+			}
+			if (this._binaryTypes != null) {
+				for (ReferenceBinding typeBinding : _binaryTypes) {
+					Element element = _factory.newElement(typeBinding);
+					if (null == element) {
+						throw new IllegalArgumentException("Top-level type binding could not be converted to element: " + typeBinding); //$NON-NLS-1$
+					}
+					elements.add(element);
+					ModuleBinding binding = typeBinding.module();
+					if (binding != null) {
+						Element m = _factory.newElement(binding);
+						elements.add(m);
+					}
+				}
+			}
+			_rootElements = elements;
+		}
+		return _rootElements;
+	}
+
+	@Override
+	public boolean processingOver()
+	{
+		return _isLastRound;
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationMemberValue.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationMemberValue.java
new file mode 100644
index 0000000..6687e7b
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationMemberValue.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Vladimir Piskarev and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Vladimir Piskarev - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+
+public class AnnotationMemberValue extends AnnotationValueImpl {
+
+	private final MethodBinding _methodBinding;
+
+	/**
+	 * @param value
+	 *            The JDT representation of a compile-time constant. See
+	 *            {@link org.eclipse.jdt.internal.compiler.lookup.ElementValuePair#getValue()} for possible object types:
+	 *            <ul>
+	 *            <li>{@link org.eclipse.jdt.internal.compiler.impl.Constant} for member
+	 *            of primitive type or String</li>
+	 *            <li>{@link org.eclipse.jdt.internal.compiler.lookup.TypeBinding} for a member value of type
+	 *            {@link java.lang.Class}</li>
+	 *            <li>{@link org.eclipse.jdt.internal.compiler.lookup.FieldBinding} for an enum constant</li>
+	 *            <li>{@link org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding} for an annotation instance</li>
+	 *            <li><code>Object[]</code> for a member value of array type, where the
+	 *            array entries are one of the above</li>
+	 *            </ul>
+	 * @param methodBinding the method binding that defined this member value pair
+	 */
+	public AnnotationMemberValue(BaseProcessingEnvImpl env, Object value, MethodBinding methodBinding) {
+		super(env, value, methodBinding.returnType);
+		_methodBinding = methodBinding;
+	}
+
+	/**
+	 * @return the method binding that defined this member value pair.
+	 */
+	public MethodBinding getMethodBinding() {
+		return _methodBinding;
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationMirrorImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationMirrorImpl.java
new file mode 100644
index 0000000..5b9fd1c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationMirrorImpl.java
@@ -0,0 +1,561 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2016 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     het@google.com - Bug 441790
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.MirroredTypeException;
+import javax.lang.model.type.MirroredTypesException;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+public class AnnotationMirrorImpl implements AnnotationMirror, InvocationHandler {
+
+	public final BaseProcessingEnvImpl _env;
+	public final AnnotationBinding _binding;
+
+	/* package */ AnnotationMirrorImpl(BaseProcessingEnvImpl env, AnnotationBinding binding) {
+		_env = env;
+		_binding = binding;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof AnnotationMirrorImpl) {
+			if (this._binding == null) {
+				return ((AnnotationMirrorImpl) obj)._binding == null;
+			}
+			return equals(this._binding, ((AnnotationMirrorImpl) obj)._binding);
+		}
+		return obj == null ? false : obj.equals(this); // obj could be wrapped by a proxy.
+	}
+
+	private static boolean equals(AnnotationBinding annotationBinding, AnnotationBinding annotationBinding2) {
+		if (annotationBinding.getAnnotationType() != annotationBinding2.getAnnotationType()) return false; //$IDENTITY-COMPARISON$
+		final ElementValuePair[] elementValuePairs = annotationBinding.getElementValuePairs();
+		final ElementValuePair[] elementValuePairs2 = annotationBinding2.getElementValuePairs();
+		final int length = elementValuePairs.length;
+		if (length != elementValuePairs2.length) return false;
+		loop: for (int i = 0; i < length; i++) {
+			ElementValuePair pair = elementValuePairs[i];
+			// loop on the given pair to make sure one will match
+			for (int j = 0; j < length; j++) {
+				ElementValuePair pair2 = elementValuePairs2[j];
+				if (pair.binding == pair2.binding) {
+					if (pair.value == null) {
+						if (pair2.value == null) {
+							continue loop;
+						}
+						return false;
+					} else {
+						if (pair2.value == null) return false;
+						if (pair2.value instanceof Object[] && pair.value instanceof Object[]) {
+							if (!Arrays.equals((Object[]) pair.value, (Object[]) pair2.value)) {
+								return false;
+							}
+						} else if (!pair2.value.equals(pair.value)){
+							return false;
+						}
+					}
+					continue loop;
+				}
+			}
+			return false;
+		}
+		return true;
+	}
+
+	@Override
+	public DeclaredType getAnnotationType() {
+		return (DeclaredType) _env.getFactory().newTypeMirror(_binding.getAnnotationType());
+	}
+
+	/**
+	 * @return all the members of this annotation mirror that have explicit values.
+	 * Default values are not included.
+	 */
+	@Override
+	public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues() {
+		if (this._binding == null) {
+			return Collections.emptyMap();
+		}
+		ElementValuePair[] pairs = _binding.getElementValuePairs();
+		Map<ExecutableElement, AnnotationValue> valueMap =
+			new LinkedHashMap<>(pairs.length);
+		for (ElementValuePair pair : pairs) {
+			MethodBinding method = pair.getMethodBinding();
+			if (method == null) {
+				// ideally we should be able to create a fake ExecutableElementImpl
+				continue;
+			}
+			ExecutableElement e = new ExecutableElementImpl(_env, method);
+			AnnotationValue v = new AnnotationMemberValue(_env, pair.getValue(), method);
+			valueMap.put(e, v);
+		}
+		return Collections.unmodifiableMap(valueMap);
+	}
+
+	/**
+	 * @see javax.lang.model.util.Elements#getElementValuesWithDefaults(AnnotationMirror)
+	 * @return all the members of this annotation mirror that have explicit or default
+	 * values.
+	 */
+	public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults() {
+		if (this._binding == null) {
+			return Collections.emptyMap();
+		}
+		ElementValuePair[] pairs = _binding.getElementValuePairs();
+		ReferenceBinding annoType = _binding.getAnnotationType();
+		Map<ExecutableElement, AnnotationValue> valueMap =
+			new LinkedHashMap<>();
+		for (MethodBinding method : annoType.methods()) {
+			// if binding is in ElementValuePair list, then get value from there
+			boolean foundExplicitValue = false;
+			for (int i = 0; i < pairs.length; ++i) {
+				MethodBinding explicitBinding = pairs[i].getMethodBinding();
+				if (method == explicitBinding) {
+					ExecutableElement e = new ExecutableElementImpl(_env, explicitBinding);
+					AnnotationValue v = new AnnotationMemberValue(_env, pairs[i].getValue(), explicitBinding);
+					valueMap.put(e, v);
+					foundExplicitValue = true;
+					break;
+				}
+			}
+			// else get default value if one exists
+			if (!foundExplicitValue) {
+				Object defaultVal = method.getDefaultValue();
+				if (null != defaultVal) {
+					ExecutableElement e = new ExecutableElementImpl(_env, method);
+					AnnotationValue v = new AnnotationMemberValue(_env, defaultVal, method);
+					valueMap.put(e, v);
+				}
+			}
+		}
+		return Collections.unmodifiableMap(valueMap);
+	}
+
+	@Override
+	public int hashCode() {
+		if (this._binding == null) return this._env.hashCode();
+		return this._binding.hashCode();
+	}
+
+	/*
+	 * Used by getAnnotation(), which returns a reflective proxy of the annotation class.  When processors then
+	 * invoke methods such as value() on the annotation proxy, this method is called.
+	 * <p>
+	 * A challenge here is that the processor was not necessarily compiled against the same annotation
+	 * definition that the compiler is looking at right now, not to mention that the annotation itself
+	 * may be defective in source.  So the actual type of the value may be quite different than the
+	 * type expected by the caller, which will result in a ClassCastException, which is ugly for the
+	 * processor to try to catch.  So we try to catch and correct this type mismatch where possible.
+	 * <p>
+	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+	 */
+	@Override
+	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+	{
+		if (this._binding == null) return null;
+        final String methodName = method.getName();
+        if ( args == null || args.length == 0 ) {
+            if( methodName.equals("hashCode") ) { //$NON-NLS-1$
+                return Integer.valueOf(hashCode());
+            }
+            else if( methodName.equals("toString") ) { //$NON-NLS-1$
+                return toString();
+            }
+            else if( methodName.equals("annotationType")) { //$NON-NLS-1$
+            	return proxy.getClass().getInterfaces()[0];
+            }
+        }
+        else if ( args.length == 1 && methodName.equals("equals") ) { //$NON-NLS-1$
+            return Boolean.valueOf(equals(args[0]));
+        }
+
+        // If it's not one of the above methods, it must be an annotation member, so it cannot take any arguments
+        if ( args != null && args.length != 0 ) {
+            throw new NoSuchMethodException("method " + method.getName() + formatArgs(args) + " does not exist on annotation " + toString()); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        final MethodBinding methodBinding = getMethodBinding(methodName);
+        if ( methodBinding == null ) {
+            throw new NoSuchMethodException("method " + method.getName() + "() does not exist on annotation" + toString()); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        Object actualValue = null;
+        boolean foundMethod = false;
+        ElementValuePair[] pairs = _binding.getElementValuePairs();
+		for (ElementValuePair pair : pairs) {
+			if (methodName.equals(new String(pair.getName()))) {
+				actualValue = pair.getValue();
+				foundMethod = true;
+				break;
+			}
+		}
+		if (!foundMethod) {
+			// couldn't find explicit value; see if there's a default
+			actualValue = methodBinding.getDefaultValue();
+		}
+		Class<?> expectedType = method.getReturnType();
+		TypeBinding actualType = methodBinding.returnType;
+        return getReflectionValue(actualValue, actualType, expectedType);
+	}
+
+	@Override
+    public String toString() {
+    	TypeMirror decl = getAnnotationType();
+    	StringBuilder sb = new StringBuilder();
+    	sb.append('@');
+    	sb.append(decl.toString());
+    	Map<? extends ExecutableElement, ? extends AnnotationValue> values = getElementValues();
+		if (!values.isEmpty()) {
+			sb.append('(');
+			boolean first = true;
+			for (Entry<? extends ExecutableElement, ? extends AnnotationValue> e : values.entrySet()) {
+				if (!first) {
+					sb.append(", "); //$NON-NLS-1$
+				}
+				first = false;
+				sb.append(e.getKey().getSimpleName());
+				sb.append(" = "); //$NON-NLS-1$
+				sb.append(e.getValue().toString());
+			}
+			sb.append(')');
+		}
+		return sb.toString();
+    }
+
+	/**
+	 * Used for constructing exception message text.
+	 * @return a string like "(a, b, c)".
+	 */
+    private String formatArgs(final Object[] args)
+    {
+        // estimate that each class name (plus the separators) is 10 characters long plus 2 for "()".
+        final StringBuilder builder = new StringBuilder(args.length * 8 + 2 );
+        builder.append('(');
+        for( int i=0; i<args.length; i++ )
+        {
+            if( i > 0 )
+            	builder.append(", "); //$NON-NLS-1$
+            builder.append(args[i].getClass().getName());
+        }
+        builder.append(')');
+        return builder.toString();
+    }
+
+	/**
+	 * Find a particular annotation member by name.
+	 * @return a compiler method binding, or null if no member was found.
+	 */
+	private MethodBinding getMethodBinding(String name) {
+		ReferenceBinding annoType = _binding.getAnnotationType();
+		MethodBinding[] methods = annoType.getMethods(name.toCharArray());
+		for (MethodBinding method : methods) {
+			// annotation members have no parameters
+			if (method.parameters.length == 0) {
+				return method;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Convert an annotation member value from JDT into Reflection, and from whatever its actual type
+	 * is into whatever type the reflective invoker of a method is expecting.
+	 * <p>
+	 * Only certain types are permitted as member values.  Specifically, a member must be a constant,
+	 * and must be either a primitive type, String, Class, an enum constant, an annotation, or an
+	 * array of any of those.  Multidimensional arrays are not permitted.
+	 *
+	 * @param actualValue the value as represented by {@link ElementValuePair#getValue()}
+	 * @param actualType the return type of the corresponding {@link MethodBinding}
+	 * @param expectedType the type that the reflective method invoker is expecting
+	 * @return an object of the expected type representing the annotation member value,
+	 * or an appropriate dummy value (such as null) if no value is available
+	 */
+	private Object getReflectionValue(Object actualValue, TypeBinding actualType, Class<?> expectedType)
+	{
+		if (null == expectedType) {
+			// With no expected type, we can't even guess at a conversion
+			return null;
+		}
+		if (null == actualValue) {
+			// Return a type-appropriate equivalent of null
+			return Factory.getMatchingDummyValue(expectedType);
+		}
+		if (expectedType.isArray()) {
+			if (Class.class.equals(expectedType.getComponentType())) {
+				// package Class[]-valued return as a MirroredTypesException
+				if (actualType.isArrayType() && ((ArrayBinding)actualType).leafComponentType.erasure().id == TypeIds.T_JavaLangClass) {
+
+					Object[] bindings;
+					if(actualValue instanceof Object[]) {
+						bindings = (Object[]) actualValue;
+					} else if(actualValue instanceof TypeBinding) {
+						// when a single class element is passed to array: @AnnotationType(Something.class)
+						bindings = new Object[] {actualValue};
+					} else {
+						bindings = null;
+					}
+
+					if(bindings != null) {
+						List<TypeMirror> mirrors = new ArrayList<>(bindings.length);
+						for (int i = 0; i < bindings.length; ++i) {
+							if (bindings[i] instanceof TypeBinding) {
+								mirrors.add(_env.getFactory().newTypeMirror((TypeBinding)bindings[i]));
+							}
+						}
+						throw new MirroredTypesException(mirrors);
+					}
+				}
+				// TODO: actual value is not a TypeBinding[].  Should we return a TypeMirror[] around an ErrorType?
+				return null;
+			}
+			// Handle arrays of types other than Class, e.g., int[], MyEnum[], ...
+			return convertJDTArrayToReflectionArray(actualValue, actualType, expectedType);
+		}
+		else if (Class.class.equals(expectedType)) {
+			// package the Class-valued return as a MirroredTypeException
+			if (actualValue instanceof TypeBinding) {
+				TypeMirror mirror = _env.getFactory().newTypeMirror((TypeBinding)actualValue);
+				throw new MirroredTypeException(mirror);
+			}
+			else {
+				// TODO: actual value is not a TypeBinding.  Should we return a TypeMirror around an ErrorType?
+				return null;
+			}
+		}
+		else {
+			// Handle unitary values of type other than Class, e.g., int, MyEnum, ...
+			return convertJDTValueToReflectionType(actualValue, actualType, expectedType);
+		}
+	}
+
+	/**
+	 * Convert an array of JDT types as obtained from ElementValuePair.getValue()
+	 * (e.g., an Object[] containing IntConstant elements) to the type expected by
+	 * a reflective method invocation (e.g., int[]).
+	 * <p>
+	 * This does not handle arrays of Class, but it does handle primitives, enum constants,
+	 * and types such as String.
+	 * @param jdtValue the actual value returned by ElementValuePair.getValue() or MethodBinding.getDefault()
+	 * @param jdtType the return type of the annotation method binding
+	 * @param expectedType the type that the invoker of the method is expecting; must be an array type
+	 * @return an Object which is, e.g., an int[]; or null, if an array cannot be created.
+	 */
+	private Object convertJDTArrayToReflectionArray(Object jdtValue, TypeBinding jdtType, Class<?> expectedType)
+	{
+		assert null != expectedType && expectedType.isArray();
+		if (!jdtType.isArrayType()) {
+			// the compiler says that the type binding isn't an array type; this probably means
+			// that there's some sort of syntax error.
+			return null;
+		}
+		Object[] jdtArray;
+		// See bug 261969: it's legal to pass a solo element for an array-typed value
+		if (jdtValue != null && !(jdtValue instanceof Object[])) {
+			// Create an array of the expected type
+			jdtArray = (Object[]) Array.newInstance(jdtValue.getClass(), 1);
+			jdtArray[0] = jdtValue;
+		} else {
+			jdtArray = (Object[])jdtValue;
+		}
+		TypeBinding jdtLeafType = jdtType.leafComponentType();
+		Class<?> expectedLeafType = expectedType.getComponentType();
+        final int length = jdtArray.length;
+        final Object returnArray = Array.newInstance(expectedLeafType, length);
+        for (int i = 0; i < length; ++i) {
+        	Object jdtElementValue = jdtArray[i];
+    		if (expectedLeafType.isPrimitive() || String.class.equals(expectedLeafType)) {
+    			if (jdtElementValue instanceof Constant) {
+    				if (boolean.class.equals(expectedLeafType)) {
+    					Array.setBoolean(returnArray, i, ((Constant)jdtElementValue).booleanValue());
+    				}
+    				else if (byte.class.equals(expectedLeafType)) {
+    					Array.setByte(returnArray, i, ((Constant)jdtElementValue).byteValue());
+    				}
+    				else if (char.class.equals(expectedLeafType)) {
+    					Array.setChar(returnArray, i, ((Constant)jdtElementValue).charValue());
+    				}
+    				else if (double.class.equals(expectedLeafType)) {
+    					Array.setDouble(returnArray, i, ((Constant)jdtElementValue).doubleValue());
+    				}
+    				else if (float.class.equals(expectedLeafType)) {
+    					Array.setFloat(returnArray, i, ((Constant)jdtElementValue).floatValue());
+    				}
+    				else if (int.class.equals(expectedLeafType)) {
+    					Array.setInt(returnArray, i, ((Constant)jdtElementValue).intValue());
+    				}
+    				else if (long.class.equals(expectedLeafType)) {
+    					Array.setLong(returnArray, i, ((Constant)jdtElementValue).longValue());
+    				}
+    				else if (short.class.equals(expectedLeafType)) {
+    					Array.setShort(returnArray, i, ((Constant)jdtElementValue).shortValue());
+    				}
+    				else if (String.class.equals(expectedLeafType)) {
+    					Array.set(returnArray, i, ((Constant)jdtElementValue).stringValue());
+    				}
+    			}
+    			else {
+	    			// Primitive or string is expected, but our actual value cannot be coerced into one.
+	    			// TODO: if the actual value is an array of primitives, should we unpack the first one?
+	    			Factory.setArrayMatchingDummyValue(returnArray, i, expectedLeafType);
+    			}
+    		}
+    		else if (expectedLeafType.isEnum()) {
+    			Object returnVal = null;
+    	        if (jdtLeafType != null && jdtLeafType.isEnum() && jdtElementValue instanceof FieldBinding) {
+    	        	FieldBinding binding = (FieldBinding)jdtElementValue;
+    	        	try {
+    	        		Field returnedField = null;
+    	        		returnedField = expectedLeafType.getField( new String(binding.name) );
+    	        		if (null != returnedField) {
+    	        			returnVal = returnedField.get(null);
+    	        		}
+    	        	}
+    	        	catch (NoSuchFieldException nsfe) {
+    	        		// return null
+    	        	}
+    	        	catch (IllegalAccessException iae) {
+    	        		// return null
+    	        	}
+    	        }
+    	        Array.set(returnArray, i, returnVal);
+    		}
+    		else if (expectedLeafType.isAnnotation()) {
+    			// member value is expected to be an annotation type.  Wrap it in an Annotation proxy.
+    			Object returnVal = null;
+    			if (jdtLeafType.isAnnotationType() && jdtElementValue instanceof AnnotationBinding) {
+    				AnnotationMirrorImpl annoMirror =
+    					(AnnotationMirrorImpl)_env.getFactory().newAnnotationMirror((AnnotationBinding)jdtElementValue);
+    				returnVal = Proxy.newProxyInstance(expectedLeafType.getClassLoader(),
+    						new Class[]{ expectedLeafType }, annoMirror );
+    			}
+    	        Array.set(returnArray, i, returnVal);
+    		}
+    		else {
+    			Array.set(returnArray, i, null);
+    		}
+        }
+		return returnArray;
+	}
+
+	/**
+	 * Convert a JDT annotation value as obtained from ElementValuePair.getValue()
+	 * (e.g., IntConstant, FieldBinding, etc.) to the type expected by a reflective
+	 * method invocation (e.g., int, an enum constant, etc.).
+	 * @return a value of type {@code expectedType}, or a dummy value of that type if
+	 * the actual value cannot be converted.
+	 */
+	private Object convertJDTValueToReflectionType(Object jdtValue, TypeBinding actualType, Class<?> expectedType) {
+		if (expectedType.isPrimitive() || String.class.equals(expectedType)) {
+			if (jdtValue instanceof Constant) {
+				if (boolean.class.equals(expectedType)) {
+					return ((Constant)jdtValue).booleanValue();
+				}
+				else if (byte.class.equals(expectedType)) {
+					return ((Constant)jdtValue).byteValue();
+				}
+				else if (char.class.equals(expectedType)) {
+					return ((Constant)jdtValue).charValue();
+				}
+				else if (double.class.equals(expectedType)) {
+					return ((Constant)jdtValue).doubleValue();
+				}
+				else if (float.class.equals(expectedType)) {
+					return ((Constant)jdtValue).floatValue();
+				}
+				else if (int.class.equals(expectedType)) {
+					return ((Constant)jdtValue).intValue();
+				}
+				else if (long.class.equals(expectedType)) {
+					return ((Constant)jdtValue).longValue();
+				}
+				else if (short.class.equals(expectedType)) {
+					return ((Constant)jdtValue).shortValue();
+				}
+				else if (String.class.equals(expectedType)) {
+					return ((Constant)jdtValue).stringValue();
+				}
+			}
+			// Primitive or string is expected, but our actual value cannot be coerced into one.
+			// TODO: if the actual value is an array of primitives, should we unpack the first one?
+			return Factory.getMatchingDummyValue(expectedType);
+		}
+		else if (expectedType.isEnum()) {
+			Object returnVal = null;
+	        if (actualType != null && actualType.isEnum() && jdtValue instanceof FieldBinding) {
+
+	        	FieldBinding binding = (FieldBinding)jdtValue;
+	        	try {
+	        		Field returnedField = null;
+	        		returnedField = expectedType.getField( new String(binding.name) );
+	        		if (null != returnedField) {
+	        			returnVal = returnedField.get(null);
+	        		}
+	        	}
+	        	catch (NoSuchFieldException nsfe) {
+	        		// return null
+	        	}
+	        	catch (IllegalAccessException iae) {
+	        		// return null
+	        	}
+	        }
+	        return null == returnVal ? Factory.getMatchingDummyValue(expectedType) : returnVal;
+		}
+		else if (expectedType.isAnnotation()) {
+			// member value is expected to be an annotation type.  Wrap it in an Annotation proxy.
+			if (actualType.isAnnotationType() && jdtValue instanceof AnnotationBinding) {
+				AnnotationMirrorImpl annoMirror =
+					(AnnotationMirrorImpl)_env.getFactory().newAnnotationMirror((AnnotationBinding)jdtValue);
+				return Proxy.newProxyInstance(expectedType.getClassLoader(),
+						new Class[]{ expectedType }, annoMirror );
+			}
+			else {
+				// No way to cast a non-annotation value to an annotation type; return null to caller
+				return null;
+			}
+		}
+		else {
+			return Factory.getMatchingDummyValue(expectedType);
+		}
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationValueImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationValueImpl.java
new file mode 100644
index 0000000..5ed405d
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/AnnotationValueImpl.java
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     het@google.com - Bug 441790
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
+import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
+import org.eclipse.jdt.internal.compiler.impl.LongConstant;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+public class AnnotationValueImpl implements AnnotationValue, TypeIds {
+
+	/*
+	 * Additions to T_* constants in TypeIds.
+	 */
+	private static final int T_AnnotationMirror = -1;
+	private static final int T_EnumConstant = -2;
+	private static final int T_ClassObject = -3;
+	private static final int T_ArrayType = -4;
+
+	private final BaseProcessingEnvImpl _env;
+
+	/**
+	 * The annotation value, as it would be returned by
+	 * {@link #getValue()}.  For instance, an Integer (for an int
+	 * constant), a VariableElement (for an enum constant), or
+	 * a List<AnnotationValueImpl> containing multiple such (for an array type).
+	 */
+	private final Object _value;
+
+	/**
+	 * The type stored in _value, represented as a T_* value from {@link TypeIds}
+	 * or one of the additional T_* values defined in this class.
+	 */
+	private final int _kind;
+
+	/**
+	 * @param value
+	 *            The JDT representation of a compile-time constant. See
+	 *            {@link org.eclipse.jdt.internal.compiler.lookup.ElementValuePair#getValue()} for possible object types:
+	 *            <ul>
+	 *            <li>{@link org.eclipse.jdt.internal.compiler.impl.Constant} for member
+	 *            of primitive type or String</li>
+	 *            <li>{@link TypeBinding} for a member value of type
+	 *            {@link java.lang.Class}</li>
+	 *            <li>{@link FieldBinding} for an enum constant</li>
+	 *            <li>{@link AnnotationBinding} for an annotation instance</li>
+	 *            <li><code>Object[]</code> for a member value of array type, where the
+	 *            array entries are one of the above</li>
+	 *            </ul>
+	 * @param type
+	 *            The JDT representation of the type of the constant, as determined
+	 *            by the return type of the element.  This is needed because the type
+	 *            of the value may have been widened (e.g., byte to int) by the compiler
+	 *            and we need to call the proper visitor.  This is used only for base types.
+	 *            If it is null or not a BaseTypeBinding, it is ignored and the type is
+	 *            determined from the type of the value.
+	 */
+	public AnnotationValueImpl(BaseProcessingEnvImpl env, Object value, TypeBinding type) {
+		_env = env;
+		int kind[] = new int[1];
+		if (type == null) {
+			_value = convertToMirrorType(value, type, kind);
+			_kind = kind[0];
+		} else if (type.isArrayType()) {
+			List<AnnotationValue> convertedValues = null;
+			TypeBinding valueType = ((ArrayBinding)type).elementsType();
+			if (value instanceof Object[]) {
+				Object[] values = (Object[])value;
+				convertedValues = new ArrayList<>(values.length);
+				for (Object oneValue : values) {
+					convertedValues.add(new AnnotationValueImpl(_env, oneValue, valueType));
+				}
+			} else {
+				convertedValues = new ArrayList<>(1);
+				convertedValues.add(new AnnotationValueImpl(_env, value, valueType));
+			}
+			_value = Collections.unmodifiableList(convertedValues);
+			_kind = T_ArrayType;
+		} else {
+			_value = convertToMirrorType(value, type, kind);
+			_kind = kind[0];
+		}
+	}
+
+	/**
+	 * Convert the JDT representation of a single constant into its javax.lang.model
+	 * representation.  For instance, convert a StringConstant into a String, or
+	 * a FieldBinding into a VariableElement.  This does not handle the case where
+	 * value is an Object[].
+	 * @param value the JDT object
+	 * @param type the return type of the annotation member.  If null or not a
+	 * BaseTypeBinding, this is ignored and the value is inspected to determine type.
+	 * @param kind an int array whose first element will be set to the type of the
+	 * converted object, represented with T_* values from TypeIds or from this class.
+	 * @return
+	 */
+	private Object convertToMirrorType(Object value, TypeBinding type, int kind[]) {
+		if (type == null) {
+			kind[0] = TypeIds.T_JavaLangString;
+			return "<error>"; //$NON-NLS-1$
+		} else if (type instanceof BaseTypeBinding || type.id == TypeIds.T_JavaLangString) {
+			if (value == null) {
+				if (type instanceof BaseTypeBinding
+						|| type.id == TypeIds.T_JavaLangString) {
+					// return a string with error in it to reflect a value that could not be resolved
+					kind[0] = TypeIds.T_JavaLangString;
+					return "<error>"; //$NON-NLS-1$
+				} else if (type.isAnnotationType()) {
+					kind[0] = T_AnnotationMirror;
+					return _env.getFactory().newAnnotationMirror(null);
+				}
+			} else if (value instanceof Constant) {
+				if (type instanceof BaseTypeBinding) {
+					kind[0] = ((BaseTypeBinding)type).id;
+				}
+				else if (type.id == TypeIds.T_JavaLangString) {
+					kind[0] = ((Constant)value).typeID();
+				} else {
+					// error case
+					kind[0] = TypeIds.T_JavaLangString;
+					return "<error>"; //$NON-NLS-1$
+				}
+				switch (kind[0]) {
+				case T_boolean:
+					return ((Constant)value).booleanValue();
+				case T_byte:
+					return ((Constant)value).byteValue();
+				case T_char:
+					return ((Constant)value).charValue();
+				case T_double:
+					return ((Constant)value).doubleValue();
+				case T_float:
+					return ((Constant)value).floatValue();
+				case T_int:
+					try {
+						if (value instanceof LongConstant
+								|| value instanceof DoubleConstant
+								|| value instanceof FloatConstant) {
+							// error case
+							kind[0] = TypeIds.T_JavaLangString;
+							return "<error>"; //$NON-NLS-1$
+						}
+						return ((Constant)value).intValue();
+					} catch (ShouldNotImplement e) {
+						kind[0] = TypeIds.T_JavaLangString;
+						return "<error>"; //$NON-NLS-1$
+					}
+				case T_JavaLangString:
+					return ((Constant)value).stringValue();
+				case T_long:
+					return ((Constant)value).longValue();
+				case T_short:
+					return ((Constant)value).shortValue();
+				}
+			}
+		} else if (type.isEnum()) {
+			if (value instanceof FieldBinding) {
+				kind[0] = T_EnumConstant;
+				return _env.getFactory().newElement((FieldBinding) value);
+			} else {
+				kind[0] = TypeIds.T_JavaLangString;
+				return "<error>"; //$NON-NLS-1$
+			}
+		} else if (type.isAnnotationType()) {
+			if (value instanceof AnnotationBinding) {
+				kind[0] = T_AnnotationMirror;
+				return _env.getFactory().newAnnotationMirror((AnnotationBinding) value);
+			}
+		} else if (value instanceof TypeBinding) {
+			kind[0] = T_ClassObject;
+			return _env.getFactory().newTypeMirror((TypeBinding) value);
+		}
+		// error case
+		kind[0] = TypeIds.T_JavaLangString;
+		return "<error>"; //$NON-NLS-1$
+	}
+
+	@SuppressWarnings("unchecked") // Need to cast Object _value to a List<AnnotationValue>
+	@Override
+	public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+		switch (_kind) {
+		case TypeIds.T_boolean:
+			return v.visitBoolean((Boolean)_value, p);
+		case TypeIds.T_byte:
+			return v.visitByte((Byte)_value, p);
+		case TypeIds.T_char:
+			return v.visitChar((Character)_value, p);
+		case TypeIds.T_double:
+			return v.visitDouble((Double)_value, p);
+		case TypeIds.T_float:
+			return v.visitFloat((Float)_value, p);
+		case TypeIds.T_int:
+			return v.visitInt((Integer)_value, p);
+		case TypeIds.T_JavaLangString:
+			return v.visitString((String)_value, p);
+		case TypeIds.T_long:
+			return v.visitLong((Long)_value, p);
+		case TypeIds.T_short:
+			return v.visitShort((Short)_value, p);
+		case T_EnumConstant:
+			return v.visitEnumConstant((VariableElement)_value, p);
+		case T_ClassObject:
+			return v.visitType((TypeMirror)_value, p);
+		case T_AnnotationMirror:
+			return v.visitAnnotation((AnnotationMirror)_value, p);
+		case T_ArrayType:
+			return v.visitArray((List<AnnotationValue>)_value, p);
+		default:
+			return null;
+		}
+	}
+
+	@Override
+	public Object getValue() {
+		return _value;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof AnnotationValueImpl) {
+			return this._value.equals(((AnnotationValueImpl) obj)._value);
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		return this._value.hashCode() + this._kind;
+	}
+
+	@Override
+	public String toString() {
+		if (_value == null) {
+			return "null"; //$NON-NLS-1$
+		} else if (_value instanceof String) {
+			String value = (String) _value;
+			StringBuffer sb = new StringBuffer();
+			sb.append('"');
+			for (int i = 0; i < value.length(); i++) {
+				Util.appendEscapedChar(sb, value.charAt(i), true);
+			}
+			sb.append('"');
+			return sb.toString();
+		} else if (_value instanceof Character) {
+			StringBuffer sb = new StringBuffer();
+			sb.append('\'');
+			Util.appendEscapedChar(sb, ((Character) _value).charValue(), false);
+			sb.append('\'');
+			return sb.toString();
+		} else if (_value instanceof VariableElement) {
+			VariableElement enumDecl = (VariableElement) _value;
+			return enumDecl.asType().toString() + "." + enumDecl.getSimpleName(); //$NON-NLS-1$
+		} else if (_value instanceof Collection) {
+			// It must be Collection<AnnotationValue>
+			@SuppressWarnings("unchecked")
+			Collection<AnnotationValue> values = (Collection<AnnotationValue>) _value;
+			StringBuilder sb = new StringBuilder();
+			sb.append('{');
+			boolean first = true;
+			for (AnnotationValue annoValue : values) {
+				if (!first) {
+					sb.append(", "); //$NON-NLS-1$
+				}
+				first = false;
+				sb.append(annoValue.toString());
+			}
+			sb.append('}');
+			return sb.toString();
+		} else if (_value instanceof TypeMirror) {
+			return _value.toString() + ".class"; //$NON-NLS-1$
+		} else {
+			return _value.toString();
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ArrayTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ArrayTypeImpl.java
new file mode 100644
index 0000000..d0ec21c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ArrayTypeImpl.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2014 BEA Systems, Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - fix for 342598
+ *    IBM Corporation - Java 8 support
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+
+/**
+ * Implementation of ArrayType, which represents an array of some type.
+ */
+public class ArrayTypeImpl extends TypeMirrorImpl implements ArrayType {
+
+	ArrayTypeImpl(BaseProcessingEnvImpl env, ArrayBinding binding) {
+		super(env, binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.ArrayType#getComponentType()
+	 */
+	@Override
+	public TypeMirror getComponentType() {
+		return _env.getFactory().newTypeMirror(((ArrayBinding)_binding).elementsType());
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#accept(javax.lang.model.type.TypeVisitor, java.lang.Object)
+	 */
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+		return v.visitArray(this, p);
+	}
+
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings() {
+		AnnotationBinding[] oldies = ((ArrayBinding)_binding).getTypeAnnotations();
+		AnnotationBinding[] newbies = Binding.NO_ANNOTATIONS;
+		// Strip out the annotations on sub arrays
+		for (int i = 0, length = oldies == null ? 0 : oldies.length; i < length; i++) {
+			if (oldies[i] == null) {
+				System.arraycopy(oldies, 0, newbies = new AnnotationBinding[i], 0, i);
+				return newbies;
+			}
+		}
+		return newbies;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#getKind()
+	 */
+	@Override
+	public TypeKind getKind() {
+		return TypeKind.ARRAY;
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/DeclaredTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/DeclaredTypeImpl.java
new file mode 100644
index 0000000..e59d198
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/DeclaredTypeImpl.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2018 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - fix for 342598
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+/**
+ * Implementation of DeclaredType, which refers to a particular usage or instance of a type.
+ * Contrast with {@link javax.lang.model.element.TypeElement}, which is an element that potentially defines a family
+ * of DeclaredTypes.
+ */
+public class DeclaredTypeImpl extends TypeMirrorImpl implements DeclaredType {
+
+	private final ElementKind _elementKindHint;
+
+	/* package */ DeclaredTypeImpl(BaseProcessingEnvImpl env, ReferenceBinding binding) {
+		super(env, binding);
+		_elementKindHint = null;
+	}
+
+	/**
+	 * Create a DeclaredType that knows in advance what kind of element to produce from asElement().
+	 * This is useful in the case where the type binding is to an unresolved type, but we know
+	 * from context what type it is - e.g., an annotation type.
+	 */
+	/* package */ DeclaredTypeImpl(BaseProcessingEnvImpl env, ReferenceBinding binding, ElementKind elementKindHint) {
+		super(env, binding);
+		_elementKindHint = elementKindHint;
+	}
+
+	@Override
+	public Element asElement() {
+		TypeBinding prototype = null;
+		if (_binding instanceof TypeBinding) {
+			prototype = ((TypeBinding) _binding).prototype();
+		}
+		if (prototype != null) {
+			return _env.getFactory().newElement(prototype, _elementKindHint);
+		}
+		// The JDT compiler does not distinguish between type elements and declared types
+		return _env.getFactory().newElement((ReferenceBinding)_binding, _elementKindHint);
+	}
+
+	@Override
+	public TypeMirror getEnclosingType() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		ReferenceBinding enclosingType = binding.enclosingType();
+		if (enclosingType != null) {
+			return _env.getFactory().newTypeMirror(enclosingType);
+		}
+		return _env.getFactory().getNoType(TypeKind.NONE);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see javax.lang.model.type.DeclaredType#getTypeArguments()
+	 * @see javax.lang.model.element.TypeElement#getTypeParameters().
+	 */
+	@Override
+	public List<? extends TypeMirror> getTypeArguments() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		if (binding.isParameterizedType()) {
+			ParameterizedTypeBinding ptb = (ParameterizedTypeBinding)_binding;
+			TypeBinding[] arguments = ptb.arguments;
+			int length = arguments == null ? 0 : arguments.length;
+			if (length == 0) return Collections.emptyList();
+			List<TypeMirror> args = new ArrayList<>(length);
+			for (TypeBinding arg : arguments) {
+				args.add(_env.getFactory().newTypeMirror(arg));
+			}
+			return Collections.unmodifiableList(args);
+		}
+		if (binding.isGenericType()) {
+			TypeVariableBinding[] typeVariables = binding.typeVariables();
+			List<TypeMirror> args = new ArrayList<>(typeVariables.length);
+			for (TypeBinding arg : typeVariables) {
+				args.add(_env.getFactory().newTypeMirror(arg));
+			}
+			return Collections.unmodifiableList(args);
+		}
+		return Collections.emptyList();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#accept(javax.lang.model.type.TypeVisitor, java.lang.Object)
+	 */
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+		return v.visitDeclared(this, p);
+	}
+
+	@Override
+	public TypeKind getKind() {
+		return TypeKind.DECLARED;
+	}
+
+	@Override
+	public String toString() {
+		return new String(_binding.readableName());
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementImpl.java
new file mode 100644
index 0000000..a9669d9
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementImpl.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+
+/**
+ * Element represents any defined Java language element - a package,
+ * a method, a class or interface.  Contrast with DeclaredType.
+ */
+public abstract class ElementImpl
+	implements javax.lang.model.element.Element, IElementInfo
+{
+	public final BaseProcessingEnvImpl _env;
+	public final Binding _binding;
+
+	protected ElementImpl(BaseProcessingEnvImpl env, Binding binding) {
+		_env = env;
+		_binding = binding;
+	}
+
+	@Override
+	public TypeMirror asType() {
+		return _env.getFactory().newTypeMirror(_binding);
+	}
+
+	/**
+	 * @return the set of compiler annotation bindings on this element
+	 */
+	protected abstract AnnotationBinding[] getAnnotationBindings();
+
+	/* Package any repeating annotations into containers, return others as is.
+	   In the compiler bindings repeating annotations are left in as is, hence
+	   this step. The return value would match what one would expect to see in
+	   a class file.
+	*/
+	public final AnnotationBinding [] getPackedAnnotationBindings() {
+		return Factory.getPackedAnnotationBindings(getAnnotationBindings());
+	}
+
+	@Override
+	public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
+		A annotation = _env.getFactory().getAnnotation(getPackedAnnotationBindings(), annotationClass);
+		if (annotation != null || this.getKind() != ElementKind.CLASS || annotationClass.getAnnotation(Inherited.class) == null)
+			return annotation;
+
+		ElementImpl superClass = (ElementImpl) _env.getFactory().newElement(((ReferenceBinding) this._binding).superclass());
+		return superClass == null ? null : superClass.getAnnotation(annotationClass);
+	}
+
+	@Override
+	public List<? extends AnnotationMirror> getAnnotationMirrors() {
+		return _env.getFactory().getAnnotationMirrors(getPackedAnnotationBindings());
+	}
+
+	@Override
+	public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+		A [] annotations = _env.getFactory().getAnnotationsByType(Factory.getUnpackedAnnotationBindings(getPackedAnnotationBindings()), annotationType);
+		if (annotations.length != 0 || this.getKind() != ElementKind.CLASS || annotationType.getAnnotation(Inherited.class) == null)
+			return annotations;
+
+		ElementImpl superClass =  (ElementImpl) _env.getFactory().newElement(((ReferenceBinding) this._binding).superclass());
+		return superClass == null ? annotations : superClass.getAnnotationsByType(annotationType);
+	}
+
+	@Override
+	public Set<Modifier> getModifiers() {
+		// Most subclasses implement this; this default is appropriate for
+		// PackageElement and TypeParameterElement.
+		return Collections.emptySet();
+	}
+
+	@Override
+	public Name getSimpleName() {
+		return new NameImpl(_binding.shortReadableName());
+	}
+
+	@Override
+	public int hashCode() {
+		return _binding.hashCode();
+	}
+
+	// TODO: equals() implemented as == of JDT bindings.  Valid within
+	// a single Compiler instance; breaks in IDE if processors cache values.
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final ElementImpl other = (ElementImpl) obj;
+		if (_binding == null) {
+			if (other._binding != null)
+				return false;
+		} else if (_binding != other._binding)
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return _binding.toString();
+	}
+
+	@Override
+	public String getFileName() {
+		// Subclasses should override and return something of value
+		return null;
+	}
+
+	/**
+	 * @return the package containing this element.  The package of a PackageElement is itself.
+	 */
+	PackageElement getPackage() {
+		return null;
+	}
+
+	/**
+	 * Subclassed by VariableElementImpl, TypeElementImpl, and ExecutableElementImpl.
+	 * This base implementation suffices for other types.
+	 * @see javax.lang.model.util.Elements#hides
+	 * @return true if this element hides {@code hidden}
+	 */
+	public boolean hides(Element hidden)
+	{
+		return false;
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementsImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementsImpl.java
new file mode 100644
index 0000000..8d0a0d1
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementsImpl.java
@@ -0,0 +1,739 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2022 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - Fix for bug 341494
+ *    IBM Corporation - Fix for bug 328575
+ *    IBM Corporation - Java 8 support
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Javadoc;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
+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.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TagBits;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+
+/**
+ * Utilities for working with java8 and earlier language elements.
+ * There is one of these for every ProcessingEnvironment.
+ *
+ * @see ElementsImpl9
+ */
+public class ElementsImpl implements Elements {
+
+	// Used for parsing Javadoc comments: matches initial delimiter, followed by whitespace
+	private static final Pattern INITIAL_DELIMITER = Pattern.compile("^\\s*/\\*+"); //$NON-NLS-1$
+
+	protected final BaseProcessingEnvImpl _env;
+
+	/*
+	 * The processing env creates and caches an ElementsImpl.  Other clients should
+	 * not create their own; they should ask the env for it.
+	 */
+	protected ElementsImpl(BaseProcessingEnvImpl env) {
+		_env = env;
+	}
+
+	public static ElementsImpl create(BaseProcessingEnvImpl env) {
+		return (SourceVersion.latest().compareTo(SourceVersion.RELEASE_8) <= 0)? new ElementsImpl(env): new ElementsImpl9(env);
+	}
+
+	/**
+	 * Return all the annotation mirrors on this element, including inherited annotations.
+	 * Annotations are inherited only if the annotation type is meta-annotated with @Inherited,
+	 * and the annotation is on a class: e.g., annotations are not inherited for interfaces, methods,
+	 * or fields.
+	 */
+	@Override
+	public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {
+		// if e is a class, walk up its superclass hierarchy looking for @Inherited annotations not already in the list
+		if (e.getKind() == ElementKind.CLASS && e instanceof TypeElementImpl) {
+			List<AnnotationBinding> annotations = new ArrayList<>();
+			// A class can only have one annotation of a particular annotation type.
+			Set<ReferenceBinding> annotationTypes = new HashSet<>();
+			ReferenceBinding binding = (ReferenceBinding)((TypeElementImpl)e)._binding;
+			boolean checkIfInherited = false;
+			while (null != binding) {
+				if (binding instanceof ParameterizedTypeBinding) {
+					binding = ((ParameterizedTypeBinding) binding).genericType();
+				}
+				for (AnnotationBinding annotation : Factory.getPackedAnnotationBindings(binding.getAnnotations())) {
+					if (annotation == null) continue;
+					ReferenceBinding annotationType = annotation.getAnnotationType();
+					if (checkIfInherited && (annotationType.getAnnotationTagBits() & TagBits.AnnotationInherited) == 0)
+						continue;
+					if (!annotationTypes.contains(annotationType)) {
+						annotationTypes.add(annotationType);
+						annotations.add(annotation);
+					}
+				}
+				binding = binding.superclass();
+				checkIfInherited = true;
+			}
+			List<AnnotationMirror> list = new ArrayList<>(annotations.size());
+			for (AnnotationBinding annotation : annotations) {
+				list.add(_env.getFactory().newAnnotationMirror(annotation));
+			}
+			return Collections.unmodifiableList(list);
+		}
+		else {
+			return e.getAnnotationMirrors();
+		}
+	}
+
+	/**
+	 * Compute a list of all the visible entities in this type.  Specifically:
+	 * <ul>
+	 * <li>All nested types declared in this type, including interfaces and enums</li>
+	 * <li>All protected or public nested types declared in this type's superclasses
+	 * and superinterfaces, that are not hidden by a name collision</li>
+	 * <li>All methods declared in this type, including constructors but not
+	 * including static or instance initializers, and including abstract
+	 * methods and unimplemented methods declared in interfaces</li>
+	 * <li>All protected or public methods declared in this type's superclasses,
+	 * that are not overridden by another method, but not including constructors
+	 * or initializers. Includes abstract methods and methods declared in
+	 * superinterfaces but not implemented</li>
+	 * <li>All fields declared in this type, including constants</li>
+	 * <li>All non-private fields declared in this type's superclasses and
+	 * superinterfaces, that are not hidden by a name collision.</li>
+	 * </ul>
+	 */
+	@Override
+	public List<? extends Element> getAllMembers(TypeElement type) {
+		if (null == type || !(type instanceof TypeElementImpl)) {
+			return Collections.emptyList();
+		}
+		ReferenceBinding binding = (ReferenceBinding)((TypeElementImpl)type)._binding;
+		// Map of element simple name to binding
+		Map<String, ReferenceBinding> types = new HashMap<>();
+		// Javac implementation does not take field name collisions into account
+		List<FieldBinding> fields = new ArrayList<>();
+		// For methods, need to compare parameters, not just names
+		Map<String, Set<MethodBinding>> methods = new HashMap<>();
+		Set<ReferenceBinding> superinterfaces = new LinkedHashSet<>();
+		boolean ignoreVisibility = true;
+		while (null != binding) {
+			addMembers(binding, ignoreVisibility, types, fields, methods);
+			Set<ReferenceBinding> newfound = new LinkedHashSet<>();
+			collectSuperInterfaces(binding, superinterfaces, newfound);
+			for (ReferenceBinding superinterface : newfound) {
+				addMembers(superinterface, false, types, fields, methods);
+			}
+			superinterfaces.addAll(newfound);
+			binding = binding.superclass();
+			ignoreVisibility = false;
+		}
+		List<Element> allMembers = new ArrayList<>();
+		for (ReferenceBinding nestedType : types.values()) {
+			allMembers.add(_env.getFactory().newElement(nestedType));
+		}
+		for (FieldBinding field : fields) {
+			allMembers.add(_env.getFactory().newElement(field));
+		}
+		for (Set<MethodBinding> sameNamedMethods : methods.values()) {
+			for (MethodBinding method : sameNamedMethods) {
+				allMembers.add(_env.getFactory().newElement(method));
+			}
+		}
+		return allMembers;
+	}
+
+	/**
+	 * Recursively depth-first walk the tree of superinterfaces of a type, collecting
+	 * all the unique superinterface bindings.  (Note that because of generics, a type may
+	 * have multiple unique superinterface bindings corresponding to the same interface
+	 * declaration.)
+	 * @param existing bindings already in this set will not be re-added or recursed into
+	 * @param newfound newly found bindings will be added to this set
+	 */
+	private void collectSuperInterfaces(ReferenceBinding type,
+			Set<ReferenceBinding> existing, Set<ReferenceBinding> newfound) {
+		for (ReferenceBinding superinterface : type.superInterfaces()) {
+			if (!existing.contains(superinterface) && !newfound.contains(superinterface)) {
+				newfound.add(superinterface);
+				collectSuperInterfaces(superinterface, existing, newfound);
+			}
+		}
+	}
+
+	/**
+	 * Add the members of a type to the maps of subtypes, fields, and methods.  Add only those
+	 * which are non-private and which are not overridden by an already-discovered member.
+	 * For fields, add them all; javac implementation does not take field hiding into account.
+	 * @param binding the type whose members will be added to the lists
+	 * @param ignoreVisibility if true, all members will be added regardless of whether they
+	 * are private, overridden, etc.
+	 * @param types a map of type simple name to type binding
+	 * @param fields a list of field bindings
+	 * @param methods a map of method simple name to set of method bindings with that name
+	 */
+	private void addMembers(ReferenceBinding binding, boolean ignoreVisibility, Map<String, ReferenceBinding> types,
+			List<FieldBinding> fields, Map<String, Set<MethodBinding>> methods)
+	{
+		for (ReferenceBinding subtype : binding.memberTypes()) {
+			if (ignoreVisibility || !subtype.isPrivate()) {
+				String name = new String(subtype.sourceName());
+				if (null == types.get(name)) {
+					types.put(name, subtype);
+				}
+			}
+		}
+		for (FieldBinding field : binding.fields()) {
+			if (ignoreVisibility || !field.isPrivate()) {
+				fields.add(field);
+			}
+		}
+		for (MethodBinding method : binding.methods()) {
+			if (!method.isSynthetic() && (ignoreVisibility || (!method.isPrivate() && !method.isConstructor()))) {
+				String methodName = new String(method.selector);
+				Set<MethodBinding> sameNamedMethods = methods.get(methodName);
+				if (null == sameNamedMethods) {
+					// New method name.  Create a set for it and add it to the list.
+					// We don't expect many methods with same name, so only 4 slots:
+					sameNamedMethods = new HashSet<>(4);
+					methods.put(methodName, sameNamedMethods);
+					sameNamedMethods.add(method);
+				}
+				else {
+					// We already have a method with this name.  Is this method overridden?
+					boolean unique = true;
+					if (!ignoreVisibility) {
+						for (MethodBinding existing : sameNamedMethods) {
+							MethodVerifier verifier = _env.getLookupEnvironment().methodVerifier();
+							if (verifier.doesMethodOverride(existing, method)) {
+								unique = false;
+								break;
+							}
+						}
+					}
+					if (unique) {
+						sameNamedMethods.add(method);
+					}
+				}
+			}
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.util.Elements#getBinaryName(javax.lang.model.element.TypeElement)
+	 */
+	@Override
+	public Name getBinaryName(TypeElement type) {
+		TypeElementImpl typeElementImpl = (TypeElementImpl) type;
+		ReferenceBinding referenceBinding = (ReferenceBinding) typeElementImpl._binding;
+		return new NameImpl(
+			CharOperation.replaceOnCopy(referenceBinding.constantPoolName(), '/', '.'));
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.util.Elements#getConstantExpression(java.lang.Object)
+	 */
+	@Override
+	public String getConstantExpression(Object value) {
+		if (!(value instanceof Integer)
+				&& !(value instanceof Byte)
+				&& !(value instanceof Float)
+				&& !(value instanceof Double)
+				&& !(value instanceof Long)
+				&& !(value instanceof Short)
+				&& !(value instanceof Character)
+				&& !(value instanceof String)
+				&& !(value instanceof Boolean)) {
+			throw new IllegalArgumentException("Not a valid wrapper type : " + value.getClass()); //$NON-NLS-1$
+		}
+		if (value instanceof Character) {
+			StringBuilder builder = new StringBuilder();
+			builder.append('\'').append(value).append('\'');
+			return String.valueOf(builder);
+		} else if (value instanceof String) {
+			StringBuilder builder = new StringBuilder();
+			builder.append('\"').append(value).append('\"');
+			return String.valueOf(builder);
+		} else if (value instanceof Float) {
+			StringBuilder builder = new StringBuilder();
+			builder.append(value).append('f');
+			return String.valueOf(builder);
+		} else if (value instanceof Long) {
+			StringBuilder builder = new StringBuilder();
+			builder.append(value).append('L');
+			return String.valueOf(builder);
+		} else if (value instanceof Short) {
+			StringBuilder builder = new StringBuilder();
+			builder.append("(short)").append(value); //$NON-NLS-1$
+			return String.valueOf(builder);
+		} else if (value instanceof Byte) {
+			StringBuilder builder = new StringBuilder();
+			builder.append("(byte)0x"); //$NON-NLS-1$
+			int intValue = ((Byte) value).byteValue();
+			String hexString = Integer.toHexString(intValue & 0xFF);
+			if (hexString.length() < 2) {
+				builder.append('0');
+			}
+			builder.append(hexString);
+			return String.valueOf(builder);
+		}
+		return String.valueOf(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.util.Elements#getDocComment(javax.lang.model.element.Element)
+	 */
+	@Override
+	public String getDocComment(Element e) {
+		char[] unparsed = getUnparsedDocComment(e);
+		return formatJavadoc(unparsed);
+	}
+
+	/**
+	 * Return the entire javadoc comment on e, including the comment characters and whitespace
+	 * @param e an Element of any sort, possibly with a javadoc comment.
+	 * @return a String, or null if the comment is not available
+	 */
+	private char[] getUnparsedDocComment(Element e)
+	{
+		Javadoc javadoc = null;
+		ReferenceContext referenceContext = null;
+		switch(e.getKind()) {
+			case ANNOTATION_TYPE :
+			case CLASS :
+			case ENUM :
+			case INTERFACE :
+			case RECORD :
+				TypeElementImpl typeElementImpl = (TypeElementImpl) e;
+				ReferenceBinding referenceBinding = (ReferenceBinding)typeElementImpl._binding;
+				if (referenceBinding instanceof SourceTypeBinding) {
+					SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) referenceBinding;
+					referenceContext = sourceTypeBinding.scope.referenceContext;
+					javadoc = ((TypeDeclaration) referenceContext).javadoc;
+				}
+				break;
+			case PACKAGE :
+				// might need to handle javadoc of package-info.java file
+				PackageElementImpl packageElementImpl = (PackageElementImpl) e;
+				PackageBinding packageBinding = (PackageBinding) packageElementImpl._binding;
+				char[][] compoundName = CharOperation.arrayConcat(packageBinding.compoundName, TypeConstants.PACKAGE_INFO_NAME);
+				ReferenceBinding type = this._env.getLookupEnvironment().getType(compoundName);
+				if (type != null && type.isValidBinding() && (type instanceof SourceTypeBinding)) {
+					SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) type;
+					referenceContext = sourceTypeBinding.scope.referenceContext;
+					javadoc = ((TypeDeclaration) referenceContext).javadoc;
+				}
+				break;
+			case CONSTRUCTOR :
+			case METHOD :
+				ExecutableElementImpl executableElementImpl = (ExecutableElementImpl) e;
+				MethodBinding methodBinding = (MethodBinding) executableElementImpl._binding;
+				AbstractMethodDeclaration sourceMethod = methodBinding.sourceMethod();
+				if (sourceMethod != null) {
+					javadoc = sourceMethod.javadoc;
+					referenceContext = sourceMethod;
+				}
+				break;
+			case RECORD_COMPONENT :
+			case ENUM_CONSTANT :
+			case FIELD :
+				VariableElementImpl variableElementImpl = (VariableElementImpl) e;
+				FieldBinding fieldBinding = (FieldBinding) variableElementImpl._binding;
+				FieldDeclaration sourceField = fieldBinding.sourceField();
+				if (sourceField != null) {
+					javadoc = sourceField.javadoc;
+					if (fieldBinding.declaringClass instanceof SourceTypeBinding) {
+						SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) fieldBinding.declaringClass;
+						referenceContext = sourceTypeBinding.scope.referenceContext;
+					}
+				}
+				break;
+			default:
+				return null;
+		}
+		if (javadoc != null && referenceContext != null) {
+			char[] contents = referenceContext.compilationResult().getCompilationUnit().getContents();
+			if (contents != null) {
+				return CharOperation.subarray(contents, javadoc.sourceStart, javadoc.sourceEnd - 1);
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Strip the comment characters from a javadoc comment. Assume the comment is already
+	 * missing its closing delimiter.
+	 *
+	 * Javac's behavior with regard to tab expansion and trimming of whitespace and
+	 * asterisks is bizarre and undocumented.  We do our best here to emulate it.
+	 */
+	private static String formatJavadoc(char[] unparsed)
+	{
+		if (unparsed == null || unparsed.length < 5) { // delimiters take 5 chars
+			return null;
+		}
+
+		String[] lines = new String(unparsed).split("\n"); //$NON-NLS-1$
+		Matcher delimiterMatcher = INITIAL_DELIMITER.matcher(lines[0]);
+		if (!delimiterMatcher.find()) {
+			return null;
+		}
+		int iOpener = delimiterMatcher.end();
+		lines[0] = lines[0].substring(iOpener);
+		if (lines.length == 1) {
+			// single-line comment.  Should trim(), but javac doesn't.
+			// we should however remove the starting whitespaces
+			StringBuilder sb = new StringBuilder();
+			char[] chars = lines[0].toCharArray();
+			boolean startingWhitespaces = true;
+			for (char c : chars) {
+				if (Character.isWhitespace(c))
+					if (startingWhitespaces) {
+						continue;
+					} else {
+						sb.append(c);
+				} else {
+					startingWhitespaces = false;
+					sb.append(c);
+				}
+			}
+			return sb.toString();
+		}
+
+		// if the first line ends with spaces after the /** then we want to insert a line separator
+		int firstLine = lines[0].trim().length() > 0 ? 0 : 1;
+
+		// If the last line is now empty, skip it
+		int lastLine = lines[lines.length - 1].trim().length() > 0 ? lines.length - 1 : lines.length - 2;
+
+		StringBuilder sb = new StringBuilder();
+		if (lines[0].length() != 0 && firstLine == 1) {
+			// insert a line separator only if the remaining chars on the line are whitespaces
+			sb.append('\n');
+		}
+		boolean preserveLineSeparator = lines[0].length() == 0;
+		for (int line = firstLine; line <= lastLine; ++line) {
+			char[] chars = lines[line].toCharArray();
+			int starsIndex = getStars(chars);
+			int leadingWhitespaces = 0;
+			boolean recordLeadingWhitespaces = true;
+			for (int i = 0, max = chars.length; i < max; i++) {
+				char c = chars[i];
+				switch(c) {
+					case ' ' :
+						if (starsIndex == -1) {
+							if (recordLeadingWhitespaces) {
+								leadingWhitespaces++;
+							} else {
+								sb.append(c);
+							}
+						} else if (i >= starsIndex) {
+							sb.append(c);
+						}
+						break;
+					default :
+						// convert leadingwhitespaces to spaces
+						recordLeadingWhitespaces = false;
+						if (leadingWhitespaces != 0) {
+							int numberOfTabs = leadingWhitespaces / 8;
+							if (numberOfTabs != 0) {
+								for (int j = 0, max2 = numberOfTabs; j < max2; j++) {
+									sb.append("        "); //$NON-NLS-1$
+								}
+								if ((leadingWhitespaces % 8) >= 1) {
+									sb.append(' ');
+								}
+							} else if (line != 0) {
+								// we don't want to preserve the leading spaces for the first line
+								for (int j = 0, max2 = leadingWhitespaces; j < max2; j++) {
+									sb.append(' ');
+								}
+							}
+							leadingWhitespaces = 0;
+							sb.append(c);
+						} else if (c == '\t') {
+							if (i >= starsIndex) {
+								sb.append(c);
+							}
+						} else if (c != '*' || i > starsIndex) {
+							sb.append(c);
+						}
+				}
+			}
+
+			// append a newline at the end of each line except the last, even if we skipped the last entirely
+			int end = lines.length - 1;
+			if (line < end) {
+				sb.append('\n');
+			} else if (preserveLineSeparator && line == end) {
+				sb.append('\n');
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Returns the index of the last leading stars on this line, -1 if none.
+	 *
+	 * @param line the given line
+	 * @return the computed index
+	 */
+	private static int getStars(char[] line) {
+		loop: for (int i = 0, max = line.length; i < max; i++) {
+			char c = line[i];
+			if (!Character.isWhitespace(c)) {
+				if (c == '*') {
+					// only whitespaces before the first star
+					// consume all stars and return the last index
+					for (int j = i + 1; j < max; j++) {
+						if (line[j] != '*') {
+							return j;
+						}
+					}
+					return max - 1;
+				}
+				// no need to continue
+				break loop;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * @return all the annotation instance's explicitly set values, plus default values
+	 *         for all the annotation members that are not explicitly set but that have
+	 *         defaults. By comparison, {@link AnnotationMirror#getElementValues()} only
+	 *         returns the explicitly set values.
+	 * @see javax.lang.model.util.Elements#getElementValuesWithDefaults(javax.lang.model.element.AnnotationMirror)
+	 */
+	@Override
+	public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults(
+			AnnotationMirror a) {
+		return ((AnnotationMirrorImpl)a).getElementValuesWithDefaults();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.util.Elements#getName(java.lang.CharSequence)
+	 */
+	@Override
+	public Name getName(CharSequence cs) {
+		return new NameImpl(cs);
+	}
+
+	@Override
+	public PackageElement getPackageElement(CharSequence name) {
+		LookupEnvironment le = _env.getLookupEnvironment(); // FIXME(SHMOD): does this lookup need to be module-aware?
+		if (name.length() == 0) {
+			return (PackageElement) _env.getFactory().newElement(le.defaultPackage);
+		}
+		char[] packageName = name.toString().toCharArray();
+		PackageBinding packageBinding = le.createPackage(CharOperation.splitOn('.', packageName));
+		if (packageBinding == null) {
+			return null;
+		}
+		return (PackageElement) _env.getFactory().newElement(packageBinding);
+	}
+
+	@Override
+	public PackageElement getPackageOf(Element type) {
+		switch(type.getKind()) {
+			case ANNOTATION_TYPE :
+			case CLASS :
+			case ENUM :
+			case INTERFACE :
+			case RECORD :
+				TypeElementImpl typeElementImpl = (TypeElementImpl) type;
+				ReferenceBinding referenceBinding = (ReferenceBinding)typeElementImpl._binding;
+				return (PackageElement) _env.getFactory().newElement(referenceBinding.fPackage);
+			case PACKAGE :
+				return (PackageElement) type;
+			case CONSTRUCTOR :
+			case METHOD :
+				ExecutableElementImpl executableElementImpl = (ExecutableElementImpl) type;
+				MethodBinding methodBinding = (MethodBinding) executableElementImpl._binding;
+				return (PackageElement) _env.getFactory().newElement(methodBinding.declaringClass.fPackage);
+			case ENUM_CONSTANT :
+			case FIELD :
+			case RECORD_COMPONENT :
+				VariableElementImpl variableElementImpl = (VariableElementImpl) type;
+				FieldBinding fieldBinding = (FieldBinding) variableElementImpl._binding;
+				return (PackageElement) _env.getFactory().newElement(fieldBinding.declaringClass.fPackage);
+			case PARAMETER :
+				variableElementImpl = (VariableElementImpl) type;
+				LocalVariableBinding localVariableBinding = (LocalVariableBinding) variableElementImpl._binding;
+				return (PackageElement) _env.getFactory().newElement(localVariableBinding.declaringScope.classScope().referenceContext.binding.fPackage);
+			case EXCEPTION_PARAMETER :
+			case INSTANCE_INIT :
+			case OTHER :
+			case STATIC_INIT :
+			case TYPE_PARAMETER :
+			case LOCAL_VARIABLE :
+				return null;
+		default:
+			break;
+		}
+		// unreachable
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.util.Elements#getTypeElement(java.lang.CharSequence)
+	 */
+	@Override
+	public TypeElement getTypeElement(CharSequence name) {
+		LookupEnvironment le = _env.getLookupEnvironment();
+		final char[][] compoundName = CharOperation.splitOn('.', name.toString().toCharArray());
+		ReferenceBinding binding = le.getType(compoundName);
+		// If we didn't find the binding, maybe it's a nested type;
+		// try finding the top-level type and then working downwards.
+		if (null == binding) {
+			ReferenceBinding topLevelBinding = null;
+			int topLevelSegments = compoundName.length;
+			while (--topLevelSegments > 0) {
+				char[][] topLevelName = new char[topLevelSegments][];
+				for (int i = 0; i < topLevelSegments; ++i) {
+					topLevelName[i] = compoundName[i];
+				}
+				topLevelBinding = le.getType(topLevelName);
+				if (null != topLevelBinding) {
+					break;
+				}
+			}
+			if (null == topLevelBinding) {
+				return null;
+			}
+			binding = topLevelBinding;
+			for (int i = topLevelSegments; null != binding && i < compoundName.length; ++i) {
+				binding = binding.getMemberType(compoundName[i]);
+			}
+		}
+		if (null == binding) {
+			return null;
+		}
+		if((binding.tagBits & TagBits.HasMissingType) != 0) {
+			return null;
+		}
+		return new TypeElementImpl(_env, binding, null);
+	}
+
+	/* (non-Javadoc)
+	 * Element A hides element B if: A and B are both fields, both nested types, or both methods; and
+	 * the enclosing element of B is a superclass or superinterface of the enclosing element of A.
+	 * See JLS 8.3 (for hiding of fields), 8.4.8.2 (hiding of class methods), and 8.5 (for hiding of member types).
+	 * @see javax.lang.model.util.Elements#hides(javax.lang.model.element.Element, javax.lang.model.element.Element)
+	 */
+	@Override
+	public boolean hides(Element hider, Element hidden) {
+		if (hidden == null) {
+			// required by API spec
+			throw new NullPointerException();
+		}
+		return ((ElementImpl)hider).hides(hidden);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.util.Elements#isDeprecated(javax.lang.model.element.Element)
+	 */
+	@Override
+	public boolean isDeprecated(Element e) {
+		if (!(e instanceof ElementImpl)) {
+			return false;
+		}
+		return (((ElementImpl)e)._binding.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0;
+	}
+
+	/* (non-Javadoc)
+	 * See JLS 8.4.8.1 for discussion of hiding of methods
+	 * @see javax.lang.model.util.Elements#overrides(javax.lang.model.element.ExecutableElement, javax.lang.model.element.ExecutableElement, javax.lang.model.element.TypeElement)
+	 */
+	@Override
+	public boolean overrides(ExecutableElement overrider, ExecutableElement overridden,
+			TypeElement type) {
+		if (overridden == null || type == null) {
+			throw new NullPointerException();
+		}
+		return ((ExecutableElementImpl)overrider).overrides(overridden, type);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.util.Elements#printElements(java.io.Writer, javax.lang.model.element.Element[])
+	 */
+	@Override
+	public void printElements(Writer w, Element... elements) {
+		String lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$
+		for (Element element : elements) {
+			try {
+				w.write(element.toString());
+				w.write(lineSeparator);
+			} catch (IOException e) {
+				// ignore
+			}
+		}
+		try {
+			w.flush();
+		} catch (IOException e) {
+			// ignore
+		}
+	}
+
+	@Override
+	public boolean isFunctionalInterface(TypeElement type) {
+		if (type != null && type.getKind() == ElementKind.INTERFACE) {
+			ReferenceBinding binding = (ReferenceBinding)((TypeElementImpl) type)._binding;
+			if (binding instanceof SourceTypeBinding) {
+				return binding.isFunctionalInterface(((SourceTypeBinding) binding).scope);
+			}
+		}
+		return false;
+	}
+	@Override
+    public boolean isAutomaticModule(ModuleElement module) {
+		ModuleBinding binding = ((ModuleElementImpl) module).binding;
+		return binding != null ? binding.isAutomatic() : false;
+    }
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementsImpl9.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementsImpl9.java
new file mode 100644
index 0000000..b47dc55
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ElementsImpl9.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2021 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Igor Fedorenko - extracted from ElementsImpl
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.lang.model.AnnotatedConstruct;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TagBits;
+import org.eclipse.jdt.internal.compiler.util.HashtableOfModule;
+
+/**
+ * Utilities for working with java9 language elements.
+ * There is one of these for every ProcessingEnvironment.
+ */
+public class ElementsImpl9 extends ElementsImpl {
+
+	public ElementsImpl9(BaseProcessingEnvImpl env) {
+		super(env);
+	}
+
+	@Override
+	public TypeElement getTypeElement(CharSequence name) {
+		final char[][] compoundName = CharOperation.splitOn('.', name.toString().toCharArray());
+		Set<? extends ModuleElement> allModuleElements = getAllModuleElements();
+		for (ModuleElement moduleElement : allModuleElements) {
+			TypeElement t = getTypeElement(compoundName, ((ModuleElementImpl) moduleElement).binding);
+			if (t != null) {
+				return t;
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public TypeElement getTypeElement(ModuleElement module, CharSequence name) {
+		ModuleBinding mBinding = ((ModuleElementImpl) module).binding;
+		final char[][] compoundName = CharOperation.splitOn('.', name.toString().toCharArray());
+		return getTypeElement(compoundName, mBinding);
+	}
+
+	private TypeElement getTypeElement(final char[][] compoundName, ModuleBinding mBinding) {
+		LookupEnvironment le = mBinding == null ? _env.getLookupEnvironment() : mBinding.environment;
+		ReferenceBinding binding = mBinding == null ? le.getType(compoundName) : le.getType(compoundName, mBinding);
+		// If we didn't find the binding, maybe it's a nested type;
+		// try finding the top-level type and then working downwards.
+		if (null == binding) {
+			ReferenceBinding topLevelBinding = null;
+			int topLevelSegments = compoundName.length;
+			while (--topLevelSegments > 0) {
+				char[][] topLevelName = new char[topLevelSegments][];
+				for (int i = 0; i < topLevelSegments; ++i) {
+					topLevelName[i] = compoundName[i];
+				}
+				topLevelBinding = le.getType(topLevelName);
+				if (null != topLevelBinding) {
+					break;
+				}
+			}
+			if (null == topLevelBinding) {
+				return null;
+			}
+			binding = topLevelBinding;
+			for (int i = topLevelSegments; null != binding && i < compoundName.length; ++i) {
+				binding = binding.getMemberType(compoundName[i]);
+			}
+		}
+		if (null == binding) {
+			return null;
+		}
+		if ((binding.tagBits & TagBits.HasMissingType) != 0) {
+			return null;
+		}
+		return new TypeElementImpl(_env, binding, null);
+	}
+
+
+	@Override
+	public Origin getOrigin(Element e) {
+		return Origin.EXPLICIT;
+	}
+
+	@Override
+	public Origin getOrigin(AnnotatedConstruct c, AnnotationMirror a) {
+		return Origin.EXPLICIT;
+	}
+
+	@Override
+	public Origin getOrigin(ModuleElement m, ModuleElement.Directive directive) {
+		return Origin.EXPLICIT;
+	}
+
+	@Override
+	public boolean isBridge(ExecutableElement e) {
+		MethodBinding methodBinding = (MethodBinding) ((ExecutableElementImpl) e)._binding;
+		return methodBinding.isBridge();
+	}
+
+	@Override
+	public ModuleElement getModuleOf(Element elem) {
+		if (elem instanceof ModuleElement) {
+			return (ModuleElement) elem;
+		}
+		Element parent = elem.getEnclosingElement();
+		while (parent != null) {
+			if (parent instanceof ModuleElement) {
+				return (ModuleElement) parent;
+			}
+			parent = parent.getEnclosingElement();
+		}
+		return null;
+	}
+
+	@Override
+	public ModuleElement getModuleElement(CharSequence name) {
+		LookupEnvironment lookup = _env.getLookupEnvironment();
+		ModuleBinding binding = lookup.getModule(name.length() == 0 ? ModuleBinding.UNNAMED : name.toString().toCharArray());
+		//TODO: Surely there has to be a better way than calling toString().toCharArray()?
+		if (binding == null) {
+			return null;
+		}
+		return new ModuleElementImpl(_env, binding);
+	}
+
+	@Override
+	public Set<? extends ModuleElement> getAllModuleElements() {
+		LookupEnvironment lookup = _env.getLookupEnvironment();
+		HashtableOfModule knownModules = lookup.knownModules;
+		ModuleBinding[] modules = knownModules.valueTable;
+		if (modules == null || modules.length == 0) {
+			return Collections.emptySet();
+		}
+		Set<ModuleElement> mods = new HashSet<>(modules.length);
+		for (ModuleBinding moduleBinding : modules) {
+			if (moduleBinding == null)
+				continue;
+			ModuleElement element = (ModuleElement) _env.getFactory().newElement(moduleBinding);
+			mods.add(element);
+		}
+		mods.add((ModuleElement) _env.getFactory().newElement(lookup.UnNamedModule));
+		return mods;
+	}
+
+	@Override
+	public
+	PackageElement getPackageElement(ModuleElement module, CharSequence name) {
+		ModuleBinding mBinding = ((ModuleElementImpl) module).binding;
+		final char[][] compoundName = CharOperation.splitOn('.', name.toString().toCharArray());
+		PackageBinding p = null;
+		if (mBinding != null) {
+			p = mBinding.getVisiblePackage(compoundName);
+		} else {
+			p = _env.getLookupEnvironment().createPackage(compoundName);
+		}
+		if (p == null || !p.isValidBinding())
+			return null;
+		return (PackageElement) _env.getFactory().newElement(p);
+	}
+	@Override
+	public boolean isAutomaticModule(ModuleElement module) {
+		ModuleBinding mBinding = ((ModuleElementImpl) module).binding;
+        return mBinding.isAutomatic();
+    }
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ErrorTypeElement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ErrorTypeElement.java
new file mode 100644
index 0000000..1031488
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ErrorTypeElement.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+
+/**
+ * Element corresponding to the Error type mirror
+ */
+public class ErrorTypeElement extends TypeElementImpl {
+
+	ErrorTypeElement(BaseProcessingEnvImpl env, ReferenceBinding binding) {
+		super(env, binding, null);
+	}
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.TypeElement#getInterfaces()
+	 */
+	@Override
+	public List<? extends TypeMirror> getInterfaces() {
+		return Collections.emptyList();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.TypeElement#getNestingKind()
+	 */
+	@Override
+	public NestingKind getNestingKind() {
+		return NestingKind.TOP_LEVEL;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.TypeElement#getQualifiedName()
+	 */
+	@Override
+	public Name getQualifiedName() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		char[] qName;
+		if (binding.isMemberType()) {
+			qName = CharOperation.concatWith(binding.enclosingType().compoundName, binding.sourceName, '.');
+			CharOperation.replace(qName, '$', '.');
+		} else {
+			qName = CharOperation.concatWith(binding.compoundName, '.');
+		}
+		return new NameImpl(qName);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.TypeElement#getSuperclass()
+	 */
+	@Override
+	public TypeMirror getSuperclass() {
+		return this._env.getFactory().getNoType(TypeKind.NONE);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.TypeElement#getTypeParameters()
+	 */
+	@Override
+	public List<? extends TypeParameterElement> getTypeParameters() {
+		return Collections.emptyList();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#asType()
+	 */
+	@Override
+	public TypeMirror asType() {
+		return this._env.getFactory().getErrorType((ReferenceBinding) this._binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#getAnnotation(java.lang.Class)
+	 */
+	@Override
+	public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#getAnnotationMirrors()
+	 */
+	@Override
+	public List<? extends AnnotationMirror> getAnnotationMirrors() {
+		return Collections.emptyList();
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+		return (A[]) Array.newInstance(annotationType, 0);
+	}
+
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#getEnclosedElements()
+	 */
+	@Override
+	public List<? extends Element> getEnclosedElements() {
+		return Collections.emptyList();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#getEnclosingElement()
+	 */
+	@Override
+	public Element getEnclosingElement() {
+		return this._env.getFactory().newPackageElement(this._env.getLookupEnvironment().defaultPackage);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#getKind()
+	 */
+	@Override
+	public ElementKind getKind() {
+		return ElementKind.CLASS;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#getModifiers()
+	 */
+	@Override
+	public Set<Modifier> getModifiers() {
+		return Collections.emptySet();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Element#getSimpleName()
+	 */
+	@Override
+	public Name getSimpleName() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		return new NameImpl(binding.sourceName());
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ErrorTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ErrorTypeImpl.java
new file mode 100644
index 0000000..9ada225
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ErrorTypeImpl.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2015 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+/**
+ * Implementation of the {@link ErrorType} interface.
+ */
+public class ErrorTypeImpl extends DeclaredTypeImpl implements ErrorType {
+
+	/* package */ ErrorTypeImpl(BaseProcessingEnvImpl env, ReferenceBinding binding) {
+		super(env, binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.DeclaredType#asElement()
+	 */
+	@Override
+	public Element asElement() {
+		return this._env.getFactory().newElement((ReferenceBinding) this._binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.DeclaredType#getEnclosingType()
+	 */
+	@Override
+	public TypeMirror getEnclosingType() {
+		return NoTypeImpl.NO_TYPE_NONE;
+	}
+
+	@Override
+	public List<? extends TypeMirror> getTypeArguments() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		if (binding.isParameterizedType()) {
+			ParameterizedTypeBinding ptb = (ParameterizedTypeBinding)_binding;
+			TypeBinding[] arguments = ptb.arguments;
+			int length = arguments == null ? 0 : arguments.length;
+			if (length == 0) return Collections.emptyList();
+			List<TypeMirror> args = new ArrayList<>(length);
+			for (TypeBinding arg : arguments) {
+				args.add(_env.getFactory().newTypeMirror(arg));
+			}
+			return Collections.unmodifiableList(args);
+		}
+		if (binding.isGenericType()) {
+			TypeVariableBinding[] typeVariables = binding.typeVariables();
+			List<TypeMirror> args = new ArrayList<>(typeVariables.length);
+			for (TypeBinding arg : typeVariables) {
+				args.add(_env.getFactory().newTypeMirror(arg));
+			}
+			return Collections.unmodifiableList(args);
+		}
+		return Collections.emptyList();
+	}
+
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#accept(javax.lang.model.type.TypeVisitor, java.lang.Object)
+	 */
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+		return v.visitError(this, p);
+	}
+
+	@Override
+	public List<? extends AnnotationMirror> getAnnotationMirrors() {
+		return Factory.EMPTY_ANNOTATION_MIRRORS;
+	}
+
+	@Override
+	public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+		return null;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+		return (A[]) Array.newInstance(annotationType, 0);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#getKind()
+	 */
+	@Override
+	public TypeKind getKind() {
+		return TypeKind.ERROR;
+	}
+
+	@Override
+	public String toString() {
+		return new String(_binding.readableName());
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ExecutableElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ExecutableElementImpl.java
new file mode 100644
index 0000000..fd32623
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ExecutableElementImpl.java
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2015 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *    Jesper Steen Moller - Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationHolder;
+import org.eclipse.jdt.internal.compiler.lookup.AptBinaryLocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+public class ExecutableElementImpl extends ElementImpl implements
+		ExecutableElement {
+
+	private Name _name = null;
+
+	/* package */ ExecutableElementImpl(BaseProcessingEnvImpl env, MethodBinding binding) {
+		super(env, binding);
+	}
+
+	@Override
+	public <R, P> R accept(ElementVisitor<R, P> v, P p)
+	{
+		return v.visitExecutable(this, p);
+	}
+
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings()
+	{
+		return ((MethodBinding)_binding).getAnnotations();
+	}
+
+	@Override
+	public AnnotationValue getDefaultValue() {
+		MethodBinding binding = (MethodBinding)_binding;
+		Object defaultValue = binding.getDefaultValue();
+		if (defaultValue != null) return new AnnotationMemberValue(_env, defaultValue, binding);
+		return null;
+	}
+
+	@Override
+	public List<? extends Element> getEnclosedElements() {
+		return Collections.emptyList();
+	}
+
+	@Override
+	public Element getEnclosingElement() {
+		MethodBinding binding = (MethodBinding)_binding;
+		if (null == binding.declaringClass) {
+			return null;
+		}
+		return _env.getFactory().newElement(binding.declaringClass);
+	}
+
+	@Override
+	public String getFileName() {
+		ReferenceBinding dc = ((MethodBinding)_binding).declaringClass;
+		char[] name = dc.getFileName();
+		if (name == null)
+			return null;
+		return new String(name);
+	}
+
+	@Override
+	public ElementKind getKind() {
+		MethodBinding binding = (MethodBinding)_binding;
+		if (binding.isConstructor()) {
+			return ElementKind.CONSTRUCTOR;
+		}
+		else if (CharOperation.equals(binding.selector, TypeConstants.CLINIT)) {
+			return ElementKind.STATIC_INIT;
+		}
+		else if (CharOperation.equals(binding.selector, TypeConstants.INIT)) {
+			return ElementKind.INSTANCE_INIT;
+		}
+		else {
+			return ElementKind.METHOD;
+		}
+	}
+
+	@Override
+	public Set<Modifier> getModifiers() {
+		MethodBinding binding = (MethodBinding)_binding;
+		return Factory.getModifiers(binding.modifiers, getKind());
+	}
+
+	@Override
+	PackageElement getPackage()
+	{
+		MethodBinding binding = (MethodBinding)_binding;
+		if (null == binding.declaringClass) {
+			return null;
+		}
+		return _env.getFactory().newPackageElement(binding.declaringClass.fPackage);
+	}
+
+	@Override
+	public List<? extends VariableElement> getParameters() {
+		MethodBinding binding = (MethodBinding)_binding;
+		int length = binding.parameters == null ? 0 : binding.parameters.length;
+		if (0 != length) {
+			AbstractMethodDeclaration methodDeclaration = binding.sourceMethod();
+			List<VariableElement> params = new ArrayList<>(length);
+			if (methodDeclaration != null) {
+				for (Argument argument : methodDeclaration.arguments) {
+					VariableElement param = new VariableElementImpl(_env, argument.binding);
+					params.add(param);
+				}
+			} else {
+				// binary method
+				AnnotationBinding[][] parameterAnnotationBindings = null;
+				AnnotationHolder annotationHolder = binding.declaringClass.retrieveAnnotationHolder(binding, false);
+				if (annotationHolder != null) {
+					parameterAnnotationBindings = annotationHolder.getParameterAnnotations();
+				}
+				// we need to filter the synthetic arguments
+				int i = 0;
+				for (TypeBinding typeBinding : binding.parameters) {
+					char name[] = binding.parameterNames.length > i ? binding.parameterNames[i] : null;
+					if (name == null) {
+ 						StringBuilder builder = new StringBuilder("arg");//$NON-NLS-1$
+						builder.append(i);
+						name = String.valueOf(builder).toCharArray();
+					}
+					VariableElement param = new VariableElementImpl(_env,
+							new AptBinaryLocalVariableBinding(
+									name,
+									typeBinding,
+									0,
+									parameterAnnotationBindings != null ? parameterAnnotationBindings[i] : null,
+									binding));
+					params.add(param);
+					i++;
+				}
+			}
+			return Collections.unmodifiableList(params);
+		}
+		return Collections.emptyList();
+	}
+
+	@Override
+	public TypeMirror getReturnType() {
+		MethodBinding binding = (MethodBinding)_binding;
+		if (binding.returnType == null) {
+			return null;
+		}
+		else return _env.getFactory().newTypeMirror(binding.returnType);
+	}
+
+	@Override
+	public Name getSimpleName() {
+		MethodBinding binding = (MethodBinding)_binding;
+		if (_name == null) {
+			_name = new NameImpl(binding.selector);
+		}
+		return _name;
+	}
+
+	@Override
+	public List<? extends TypeMirror> getThrownTypes() {
+		MethodBinding binding = (MethodBinding)_binding;
+		if (binding.thrownExceptions.length == 0) {
+			return Collections.emptyList();
+		}
+		List<TypeMirror> list = new ArrayList<>(binding.thrownExceptions.length);
+		for (ReferenceBinding exception : binding.thrownExceptions) {
+			list.add(_env.getFactory().newTypeMirror(exception));
+		}
+		return list;
+	}
+
+	@Override
+	public List<? extends TypeParameterElement> getTypeParameters() {
+		MethodBinding binding = (MethodBinding)_binding;
+		TypeVariableBinding[] variables = binding.typeVariables();
+		if (variables.length == 0) {
+			return Collections.emptyList();
+		}
+		List<TypeParameterElement> params = new ArrayList<>(variables.length);
+		for (TypeVariableBinding variable : variables) {
+			params.add(_env.getFactory().newTypeParameterElement(variable, this));
+		}
+		return Collections.unmodifiableList(params);
+	}
+
+	@Override
+	public boolean hides(Element hidden)
+	{
+		if (!(hidden instanceof ExecutableElementImpl)) {
+			return false;
+		}
+		MethodBinding hiderBinding = (MethodBinding)_binding;
+		MethodBinding hiddenBinding = (MethodBinding)((ExecutableElementImpl)hidden)._binding;
+		if (hiderBinding == hiddenBinding) {
+			return false;
+		}
+		if (hiddenBinding.isPrivate()) {
+			return false;
+		}
+		// See JLS 8.4.8: hiding only applies to static methods
+		if (!hiderBinding.isStatic() || !hiddenBinding.isStatic()) {
+			return false;
+		}
+		// check names
+		if (!CharOperation.equals(hiddenBinding.selector, hiderBinding.selector)) {
+			return false;
+		}
+		// check parameters
+		if (!_env.getLookupEnvironment().methodVerifier().isMethodSubsignature(hiderBinding, hiddenBinding)) {
+			return false;
+		}
+		return null != hiderBinding.declaringClass.findSuperTypeOriginatingFrom(hiddenBinding.declaringClass);
+	}
+
+	@Override
+	public boolean isVarArgs() {
+		return ((MethodBinding) _binding).isVarargs();
+	}
+
+	/**
+	 * Return true if this method overrides {@code overridden} in the context of {@code type}.  For
+	 * instance, consider
+	 * <pre>
+	 *   interface A { void f(); }
+	 *   class B { void f() {} }
+	 *   class C extends B implements I { }
+	 * </pre>
+	 * In the context of B, B.f() does not override A.f(); they are unrelated.  But in the context of
+	 * C, B.f() does override A.f().  That is, the copy of B.f() that C inherits overrides A.f().
+	 * This is equivalent to considering two questions: first, does C inherit B.f(); if so, does
+	 * the inherited C.f() override A.f().  If B.f() were private, for instance, then in the context
+	 * of C it would still not override A.f().
+	 *
+	 * @see javax.lang.model.util.Elements#overrides(ExecutableElement, ExecutableElement, TypeElement)
+     * @jls3 8.4.8 Inheritance, Overriding, and Hiding
+     * @jls3 9.4.1 Inheritance and Overriding
+	 */
+	public boolean overrides(ExecutableElement overridden, TypeElement type)
+	{
+		MethodBinding overriddenBinding = (MethodBinding)((ExecutableElementImpl) overridden)._binding;
+		ReferenceBinding overriderContext = (ReferenceBinding)((TypeElementImpl)type)._binding;
+		if ((MethodBinding)_binding == overriddenBinding
+				|| overriddenBinding.isStatic()
+				|| overriddenBinding.isPrivate()
+				|| ((MethodBinding)_binding).isStatic()) {
+			return false;
+		}
+		char[] selector = ((MethodBinding)_binding).selector;
+		if (!CharOperation.equals(selector, overriddenBinding.selector))
+			return false;
+
+		// Construct a binding to the equivalent of this (the overrider) as it would be inherited by 'type'.
+		// Can only do this if 'type' is descended from the overrider.
+		// Second clause of the AND is required to match a peculiar javac behavior.
+		if (null == overriderContext.findSuperTypeOriginatingFrom(((MethodBinding)_binding).declaringClass) &&
+				null == ((MethodBinding)_binding).declaringClass.findSuperTypeOriginatingFrom(overriderContext)) {
+			return false;
+		}
+		MethodBinding overriderBinding = new MethodBinding((MethodBinding)_binding, overriderContext);
+		if (overriderBinding.isPrivate()) {
+			// a private method can never override another method.  The other method would either be
+			// private itself, in which case it would not be visible; or this would be a restriction
+			// of access, which is a compile-time error.
+			return false;
+		}
+
+		TypeBinding match = overriderBinding.declaringClass.findSuperTypeOriginatingFrom(overriddenBinding.declaringClass);
+		if (!(match instanceof ReferenceBinding)) return false;
+
+		org.eclipse.jdt.internal.compiler.lookup.MethodBinding[] superMethods = ((ReferenceBinding)match).getMethods(selector);
+		LookupEnvironment lookupEnvironment = _env.getLookupEnvironment();
+		if (lookupEnvironment == null) return false;
+		MethodVerifier methodVerifier = lookupEnvironment.methodVerifier();
+		for (int i = 0, length = superMethods.length; i < length; i++) {
+			if (superMethods[i].original() == overriddenBinding) {
+				return methodVerifier.doesMethodOverride(overriderBinding, superMethods[i]);
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public TypeMirror getReceiverType() {
+		return _env.getFactory().getReceiverType((MethodBinding) _binding);
+	}
+
+	@Override
+	public boolean isDefault() {
+		if (_binding != null) {
+			return ((MethodBinding)_binding).isDefaultMethod();
+		}
+		return false;
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ExecutableTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ExecutableTypeImpl.java
new file mode 100644
index 0000000..841c425
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ExecutableTypeImpl.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2016 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+/**
+ * Implementation of the ExecutableType
+ *
+ */
+public class ExecutableTypeImpl extends TypeMirrorImpl implements ExecutableType {
+
+	ExecutableTypeImpl(BaseProcessingEnvImpl env, MethodBinding binding) {
+		super(env, binding);
+	}
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.ExecutableType#getParameterTypes()
+	 */
+	@Override
+	public List<? extends TypeMirror> getParameterTypes() {
+		MethodBinding binding = (MethodBinding) this._binding;
+		TypeBinding[] parameters = binding.parameters;
+		int length = parameters.length;
+		boolean isEnumConstructor = binding.isConstructor()
+				&& binding.declaringClass.isEnum()
+				&& binding.declaringClass.isBinaryBinding()
+				&& ((binding.modifiers & ExtraCompilerModifiers.AccGenericSignature) == 0);
+		if (isEnumConstructor) {
+			ArrayList<TypeMirror> list = new ArrayList<>();
+			for (int i = 0; i < length; i++) {
+				list.add(_env.getFactory().newTypeMirror(parameters[i]));
+			}
+			return Collections.unmodifiableList(list);
+		}
+		if (length != 0) {
+			ArrayList<TypeMirror> list = new ArrayList<>();
+			for (TypeBinding typeBinding : parameters) {
+				list.add(_env.getFactory().newTypeMirror(typeBinding));
+			}
+			return Collections.unmodifiableList(list);
+		}
+		return Collections.emptyList();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.ExecutableType#getReturnType()
+	 */
+	@Override
+	public TypeMirror getReturnType() {
+		return _env.getFactory().newTypeMirror(((MethodBinding) this._binding).returnType);
+	}
+
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings() {
+		return ((MethodBinding) this._binding).returnType.getTypeAnnotations();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.ExecutableType#getThrownTypes()
+	 */
+	@Override
+	public List<? extends TypeMirror> getThrownTypes() {
+		ArrayList<TypeMirror> list = new ArrayList<>();
+		ReferenceBinding[] thrownExceptions = ((MethodBinding) this._binding).thrownExceptions;
+		if (thrownExceptions.length != 0) {
+			for (ReferenceBinding referenceBinding : thrownExceptions) {
+				list.add(_env.getFactory().newTypeMirror(referenceBinding));
+			}
+		}
+		return Collections.unmodifiableList(list);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.ExecutableType#getTypeVariables()
+	 */
+	@Override
+	public List<? extends TypeVariable> getTypeVariables() {
+		ArrayList<TypeVariable> list = new ArrayList<>();
+		TypeVariableBinding[] typeVariables = ((MethodBinding) this._binding).typeVariables();
+		if (typeVariables.length != 0) {
+			for (TypeVariableBinding typeVariableBinding : typeVariables) {
+				list.add((TypeVariable) _env.getFactory().newTypeMirror(typeVariableBinding));
+			}
+		}
+		return Collections.unmodifiableList(list);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#accept(javax.lang.model.type.TypeVisitor, java.lang.Object)
+	 */
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+		return v.visitExecutable(this, p);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#getKind()
+	 */
+	@Override
+	public TypeKind getKind() {
+		return TypeKind.EXECUTABLE;
+	}
+
+	@Override
+	public TypeMirror getReceiverType() {
+		return _env.getFactory().getReceiverType((MethodBinding) _binding);
+	}
+	@Override
+	public String toString() {
+		return new String(((MethodBinding) this._binding).returnType.readableName());
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/Factory.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/Factory.java
new file mode 100644
index 0000000..b3a40e4
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/Factory.java
@@ -0,0 +1,924 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2020 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - fix for 342598
+ *    IBM Corporation - Java 8 support
+ *    het@google.com - Bug 427943 - The method org.eclipse.jdt.internal.compiler.apt.model.Factory.getPrimitiveType does not throw IllegalArgumentException
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
+import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+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.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TagBits;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
+
+/**
+ * Creates javax.lang.model wrappers around JDT internal compiler bindings.
+ */
+public class Factory {
+
+	// using auto-boxing to take advantage of caching, if any.
+	// the dummy value picked here falls within the caching range.
+	public static final Byte DUMMY_BYTE = 0;
+	public static final Character DUMMY_CHAR = '0';
+	public static final Double DUMMY_DOUBLE = 0d;
+	public static final Float DUMMY_FLOAT = 0f;
+	public static final Integer DUMMY_INTEGER = 0;
+	public static final Long DUMMY_LONG = 0l;
+	public static final Short DUMMY_SHORT = 0;
+
+	private final BaseProcessingEnvImpl _env;
+	public static List<? extends AnnotationMirror> EMPTY_ANNOTATION_MIRRORS = Collections.emptyList();
+
+	/**
+	 * This object should only be constructed by the BaseProcessingEnvImpl.
+	 */
+	public Factory(BaseProcessingEnvImpl env) {
+		_env = env;
+	}
+
+	/**
+	 * Convert an array of compiler annotation bindings into a list of AnnotationMirror
+	 * @return a non-null, possibly empty, unmodifiable list.
+	 */
+	public List<? extends AnnotationMirror> getAnnotationMirrors(AnnotationBinding[] annotations) {
+		if (null == annotations || 0 == annotations.length) {
+			return Collections.emptyList();
+		}
+		List<AnnotationMirror> list = new ArrayList<>(annotations.length);
+		for (AnnotationBinding annotation : annotations) {
+			if (annotation == null) continue;
+			list.add(newAnnotationMirror(annotation));
+		}
+		return Collections.unmodifiableList(list);
+	}
+
+	@SuppressWarnings("unchecked") // for the cast to A
+	public <A extends Annotation> A[] getAnnotationsByType(AnnotationBinding[] annoInstances, Class<A> annotationClass) {
+		A[] result = getAnnotations(annoInstances, annotationClass, false);
+		return result == null ? (A[]) Array.newInstance(annotationClass, 0) : result;
+	}
+
+
+	public <A extends Annotation> A getAnnotation(AnnotationBinding[] annoInstances, Class<A> annotationClass) {
+		A[] result = getAnnotations(annoInstances, annotationClass, true);
+		return result == null ? null : result[0];
+	}
+
+	@SuppressWarnings("unchecked") // for cast of newProxyInstance() to A
+	private <A extends Annotation> A[] getAnnotations(AnnotationBinding[] annoInstances, Class<A> annotationClass, boolean justTheFirst) {
+		if(annoInstances == null || annoInstances.length == 0 || annotationClass == null )
+			return null;
+
+		String annoTypeName = annotationClass.getName();
+		if(annoTypeName == null ) return null;
+
+		List<A> list = new ArrayList<>(annoInstances.length);
+		for(AnnotationBinding annoInstance : annoInstances) {
+			if (annoInstance == null)
+				continue;
+
+			AnnotationMirrorImpl annoMirror = createAnnotationMirror(annoTypeName, annoInstance);
+			if (annoMirror != null) {
+				list.add((A)Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class[]{ annotationClass }, annoMirror));
+				if (justTheFirst) break;
+			}
+		}
+		A [] result = (A[]) Array.newInstance(annotationClass, list.size());
+		return list.size() > 0 ? (A[]) list.toArray(result) :  null;
+	}
+
+	private AnnotationMirrorImpl createAnnotationMirror(String annoTypeName, AnnotationBinding annoInstance) {
+		ReferenceBinding binding = annoInstance.getAnnotationType();
+		if (binding != null && binding.isAnnotationType() ) {
+			char[] qName;
+			if (binding.isMemberType()) {
+				annoTypeName = annoTypeName.replace('$', '.');
+				qName = CharOperation.concatWith(binding.enclosingType().compoundName, binding.sourceName, '.');
+				CharOperation.replace(qName, '$', '.');
+			} else {
+				qName = CharOperation.concatWith(binding.compoundName, '.');
+			}
+			if(annoTypeName.equals(new String(qName)) ){
+				return (AnnotationMirrorImpl)_env.getFactory().newAnnotationMirror(annoInstance);
+			}
+		}
+		return null;
+	}
+
+	private static void appendModifier(Set<Modifier> result, int modifiers, int modifierConstant, Modifier modifier) {
+		if ((modifiers & modifierConstant) != 0) {
+			result.add(modifier);
+		}
+	}
+
+	private static void decodeModifiers(Set<Modifier> result, int modifiers, int[] checkBits) {
+		if (checkBits == null) return;
+		for (int i = 0, max = checkBits.length; i < max; i++) {
+			switch(checkBits[i]) {
+				case ClassFileConstants.AccPublic :
+					appendModifier(result, modifiers, checkBits[i], Modifier.PUBLIC);
+					break;
+				case ClassFileConstants.AccProtected:
+					appendModifier(result, modifiers, checkBits[i], Modifier.PROTECTED);
+					break;
+				case ClassFileConstants.AccPrivate :
+					appendModifier(result, modifiers, checkBits[i], Modifier.PRIVATE);
+					break;
+				case ClassFileConstants.AccAbstract :
+					appendModifier(result, modifiers, checkBits[i], Modifier.ABSTRACT);
+					break;
+				case ExtraCompilerModifiers.AccDefaultMethod :
+					try {
+						appendModifier(result, modifiers, checkBits[i], Modifier.valueOf("DEFAULT")); //$NON-NLS-1$
+					} catch(IllegalArgumentException iae) {
+						// Don't have JDK 1.8, just ignore and proceed.
+					}
+					break;
+				case ClassFileConstants.AccStatic :
+					appendModifier(result, modifiers, checkBits[i], Modifier.STATIC);
+					break;
+				case ClassFileConstants.AccFinal :
+					appendModifier(result, modifiers, checkBits[i], Modifier.FINAL);
+					break;
+				case ClassFileConstants.AccSynchronized :
+					appendModifier(result, modifiers, checkBits[i], Modifier.SYNCHRONIZED);
+					break;
+				case ClassFileConstants.AccNative :
+					appendModifier(result, modifiers, checkBits[i], Modifier.NATIVE);
+					break;
+				case ClassFileConstants.AccStrictfp :
+					appendModifier(result, modifiers, checkBits[i], Modifier.STRICTFP);
+					break;
+				case ClassFileConstants.AccTransient :
+					appendModifier(result, modifiers, checkBits[i], Modifier.TRANSIENT);
+					break;
+				case ClassFileConstants.AccVolatile :
+					appendModifier(result, modifiers, checkBits[i], Modifier.VOLATILE);
+					break;
+				case ExtraCompilerModifiers.AccNonSealed :
+					try {
+						appendModifier(result, modifiers, checkBits[i], Modifier.valueOf("NON_SEALED")); //$NON-NLS-1$
+					} catch(IllegalArgumentException iae) {
+						// Don't have JDK 15, just ignore and proceed.
+					}
+					break;
+				case ExtraCompilerModifiers.AccSealed :
+					try {
+						appendModifier(result, modifiers, checkBits[i], Modifier.valueOf("SEALED")); //$NON-NLS-1$
+					} catch(IllegalArgumentException iae) {
+						// Don't have JDK 15, just ignore and proceed.
+					}
+					break;
+			}
+		}
+	}
+
+    public static Object getMatchingDummyValue(final Class<?> expectedType){
+    	if( expectedType.isPrimitive() ){
+    		if(expectedType == boolean.class)
+    			return Boolean.FALSE;
+    		else if( expectedType == byte.class )
+    			return DUMMY_BYTE;
+    		else if( expectedType == char.class )
+    			return DUMMY_CHAR;
+    		else if( expectedType == double.class)
+    			return DUMMY_DOUBLE;
+    		else if( expectedType == float.class )
+    			return DUMMY_FLOAT;
+    		else if( expectedType == int.class )
+    			return DUMMY_INTEGER;
+    		else if( expectedType == long.class )
+    			return DUMMY_LONG;
+    		else if(expectedType == short.class)
+    			return DUMMY_SHORT;
+    		else // expectedType == void.class. can this happen?
+    			return DUMMY_INTEGER; // anything would work
+    	}
+    	else
+    		return null;
+    }
+
+	public TypeMirror getReceiverType(MethodBinding binding) {
+		if (binding != null) {
+			if (binding.receiver != null) {
+				return _env.getFactory().newTypeMirror(binding.receiver);
+			}
+			if (binding.declaringClass != null) {
+				if (!binding.isStatic() && (!binding.isConstructor() || binding.declaringClass.isMemberType())) {
+					return _env.getFactory().newTypeMirror(binding.declaringClass);
+				}
+			}
+		}
+		return NoTypeImpl.NO_TYPE_NONE;
+	}
+
+	public static Set<Modifier> getModifiers(int modifiers, ElementKind kind) {
+		return getModifiers(modifiers, kind, false);
+	}
+	/**
+	 * Convert from the JDT's ClassFileConstants flags to the Modifier enum.
+	 */
+	public static Set<Modifier> getModifiers(int modifiers, ElementKind kind, boolean isFromBinary)
+	{
+		EnumSet<Modifier> result = EnumSet.noneOf(Modifier.class);
+		switch(kind) {
+			case CONSTRUCTOR :
+			case METHOD :
+				// modifiers for methods
+				decodeModifiers(result, modifiers, new int[] {
+					ClassFileConstants.AccPublic,
+					ClassFileConstants.AccProtected,
+					ClassFileConstants.AccPrivate,
+					ClassFileConstants.AccAbstract,
+					ClassFileConstants.AccStatic,
+					ClassFileConstants.AccFinal,
+					ClassFileConstants.AccSynchronized,
+					ClassFileConstants.AccNative,
+					ClassFileConstants.AccStrictfp,
+					ExtraCompilerModifiers.AccDefaultMethod
+				});
+				break;
+			case FIELD :
+			case ENUM_CONSTANT :
+				// for fields
+				decodeModifiers(result, modifiers, new int[] {
+					ClassFileConstants.AccPublic,
+					ClassFileConstants.AccProtected,
+					ClassFileConstants.AccPrivate,
+					ClassFileConstants.AccStatic,
+					ClassFileConstants.AccFinal,
+					ClassFileConstants.AccTransient,
+					ClassFileConstants.AccVolatile
+				});
+				break;
+			case ENUM :
+				if (isFromBinary) {
+					decodeModifiers(result, modifiers, new int[] {
+						ClassFileConstants.AccPublic,
+						ClassFileConstants.AccProtected,
+						ClassFileConstants.AccFinal,
+						ClassFileConstants.AccPrivate,
+						ClassFileConstants.AccAbstract,
+						ClassFileConstants.AccStatic,
+						ClassFileConstants.AccStrictfp,
+						ExtraCompilerModifiers.AccSealed,
+					});
+				} else {
+					// enum from source cannot be explicitly abstract
+					decodeModifiers(result, modifiers, new int[] {
+						ClassFileConstants.AccPublic,
+						ClassFileConstants.AccProtected,
+						ClassFileConstants.AccFinal,
+						ClassFileConstants.AccPrivate,
+						ClassFileConstants.AccStatic,
+						ClassFileConstants.AccStrictfp,
+						ExtraCompilerModifiers.AccSealed,
+					});
+				}
+				break;
+			case ANNOTATION_TYPE :
+			case INTERFACE :
+			case CLASS :
+			case RECORD :
+				// for type
+				decodeModifiers(result, modifiers, new int[] {
+					ClassFileConstants.AccPublic,
+					ClassFileConstants.AccProtected,
+					ClassFileConstants.AccAbstract,
+					ClassFileConstants.AccFinal,
+					ClassFileConstants.AccPrivate,
+					ClassFileConstants.AccStatic,
+					ClassFileConstants.AccStrictfp,
+					ExtraCompilerModifiers.AccSealed,
+					ExtraCompilerModifiers.AccNonSealed
+				});
+				break;
+			case MODULE :
+				decodeModifiers(result, modifiers, new int[] {
+						ClassFileConstants.ACC_OPEN,
+						ClassFileConstants.ACC_TRANSITIVE
+				});
+			default:
+				break;
+		}
+		return Collections.unmodifiableSet(result);
+	}
+
+	public AnnotationMirror newAnnotationMirror(AnnotationBinding binding)
+	{
+		return new AnnotationMirrorImpl(_env, binding);
+	}
+
+	/**
+	 * Create a new element that knows what kind it is even if the binding is unresolved.
+	 */
+	public Element newElement(Binding binding, ElementKind kindHint) {
+		if (binding == null)
+			return null;
+		switch (binding.kind()) {
+		case Binding.FIELD:
+		case Binding.LOCAL:
+		case Binding.RECORD_COMPONENT:
+		case Binding.VARIABLE:
+			return new VariableElementImpl(_env, (VariableBinding) binding);
+		case Binding.TYPE:
+		case Binding.GENERIC_TYPE:
+			ReferenceBinding referenceBinding = (ReferenceBinding)binding;
+			if ((referenceBinding.tagBits & TagBits.HasMissingType) != 0) {
+				return new ErrorTypeElement(this._env, referenceBinding);
+			}
+			if (CharOperation.equals(referenceBinding.sourceName, TypeConstants.PACKAGE_INFO_NAME)) {
+				return newPackageElement(referenceBinding.fPackage);
+			}
+			return new TypeElementImpl(_env, referenceBinding, kindHint);
+		case Binding.METHOD:
+			return new ExecutableElementImpl(_env, (MethodBinding)binding);
+		case Binding.RAW_TYPE:
+		case Binding.PARAMETERIZED_TYPE:
+			return new TypeElementImpl(_env, ((ParameterizedTypeBinding)binding).genericType(), kindHint);
+		case Binding.PACKAGE:
+			return newPackageElement((PackageBinding)binding);
+		case Binding.TYPE_PARAMETER:
+			return new TypeParameterElementImpl(_env, (TypeVariableBinding)binding);
+			// TODO: fill in the rest of these
+		case Binding.MODULE:
+			return new ModuleElementImpl(_env, (ModuleBinding) binding);
+		case Binding.IMPORT:
+		case Binding.ARRAY_TYPE:
+		case Binding.BASE_TYPE:
+		case Binding.WILDCARD_TYPE:
+		case Binding.INTERSECTION_TYPE:
+			throw new UnsupportedOperationException("NYI: binding type " + binding.kind()); //$NON-NLS-1$
+		}
+		return null;
+	}
+
+	public Element newElement(Binding binding) {
+		return newElement(binding, null);
+	}
+
+	/**
+	 * Convenience method - equivalent to {@code (PackageElement)Factory.newElement(binding)}
+	 */
+	public PackageElement newPackageElement(PackageBinding binding)
+	{
+		if (binding != null && binding.enclosingModule != null) {
+			binding = binding.getIncarnation(binding.enclosingModule);
+		}
+		if (binding == null) {
+			return null;
+		}
+		return new PackageElementImpl(_env, binding);
+	}
+
+	public NullType getNullType() {
+		return NoTypeImpl.NULL_TYPE;
+	}
+
+	public NoType getNoType(TypeKind kind)
+	{
+		switch (kind) {
+		case NONE:
+			return NoTypeImpl.NO_TYPE_NONE;
+		case VOID:
+			return NoTypeImpl.NO_TYPE_VOID;
+		case PACKAGE:
+			return NoTypeImpl.NO_TYPE_PACKAGE;
+		case MODULE:
+			return new NoTypeImpl(kind);
+		default:
+			throw new IllegalArgumentException();
+		}
+	}
+
+	/**
+	 * Get a type mirror object representing the specified primitive type kind.
+	 * @throw IllegalArgumentException if a non-primitive TypeKind is requested
+	 */
+	public PrimitiveTypeImpl getPrimitiveType(TypeKind kind)
+	{
+		switch (kind) {
+		case BOOLEAN:
+			return PrimitiveTypeImpl.BOOLEAN;
+		case BYTE:
+			return PrimitiveTypeImpl.BYTE;
+		case CHAR:
+			return PrimitiveTypeImpl.CHAR;
+		case DOUBLE:
+			return PrimitiveTypeImpl.DOUBLE;
+		case FLOAT:
+			return PrimitiveTypeImpl.FLOAT;
+		case INT:
+			return PrimitiveTypeImpl.INT;
+		case LONG:
+			return PrimitiveTypeImpl.LONG;
+		case SHORT:
+			return PrimitiveTypeImpl.SHORT;
+		default:
+			throw new IllegalArgumentException();
+		}
+	}
+
+	public PrimitiveTypeImpl getPrimitiveType(BaseTypeBinding binding) {
+		AnnotationBinding[] annotations = binding.getTypeAnnotations();
+		if (annotations == null || annotations.length == 0) {
+			return getPrimitiveType(PrimitiveTypeImpl.getKind(binding));
+		}
+		return new PrimitiveTypeImpl(_env, binding);
+	}
+
+	/**
+	 * Given a binding of uncertain type, try to create the right sort of TypeMirror for it.
+	 */
+	public TypeMirror newTypeMirror(Binding binding) {
+		switch (binding.kind()) {
+		case Binding.FIELD:
+		case Binding.LOCAL:
+		case Binding.RECORD_COMPONENT:
+		case Binding.VARIABLE:
+			// For variables, return the type of the variable
+			return newTypeMirror(((VariableBinding)binding).type);
+
+		case Binding.PACKAGE:
+			return getNoType(TypeKind.PACKAGE);
+
+		case Binding.IMPORT:
+			throw new UnsupportedOperationException("NYI: import type " + binding.kind()); //$NON-NLS-1$
+
+		case Binding.METHOD:
+			return new ExecutableTypeImpl(_env, (MethodBinding) binding);
+
+		case Binding.TYPE:
+		case Binding.RAW_TYPE:
+		case Binding.GENERIC_TYPE:
+		case Binding.PARAMETERIZED_TYPE:
+			ReferenceBinding referenceBinding = (ReferenceBinding) binding;
+			if ((referenceBinding.tagBits & TagBits.HasMissingType) != 0) {
+				return getErrorType(referenceBinding);
+			}
+			return new DeclaredTypeImpl(_env, (ReferenceBinding)binding);
+
+		case Binding.ARRAY_TYPE:
+			return new ArrayTypeImpl(_env, (ArrayBinding)binding);
+
+		case Binding.BASE_TYPE:
+			BaseTypeBinding btb = (BaseTypeBinding)binding;
+			switch (btb.id) {
+				case TypeIds.T_void:
+					return getNoType(TypeKind.VOID);
+				case TypeIds.T_null:
+					return getNullType();
+				default:
+					return getPrimitiveType(btb);
+			}
+
+		case Binding.WILDCARD_TYPE:
+		case Binding.INTERSECTION_TYPE: // TODO compatible, but shouldn't it really be an intersection type?
+			return new WildcardTypeImpl(_env, (WildcardBinding) binding);
+
+		case Binding.TYPE_PARAMETER:
+			return new TypeVariableImpl(_env, (TypeVariableBinding) binding);
+		case Binding.MODULE:
+			return getNoType(TypeKind.MODULE);
+		}
+		return null;
+	}
+
+	/**
+	 * @param declaringElement the class, method, etc. that is parameterized by this parameter.
+	 */
+	public TypeParameterElement newTypeParameterElement(TypeVariableBinding variable, Element declaringElement)
+	{
+		return new TypeParameterElementImpl(_env, variable, declaringElement);
+	}
+
+    public ErrorType getErrorType(ReferenceBinding binding) {
+		return new ErrorTypeImpl(this._env, binding);
+	}
+
+	/**
+     * This method is derived from code in org.eclipse.jdt.apt.core.
+     *
+     * This method is designed to be invoked by the invocation handler and anywhere that requires
+     * a AnnotationValue (AnnotationMirror member values and default values from annotation member).
+     *
+     * Regardless of the path, there are common primitive type conversion that needs to take place.
+     * The type conversions respect the type widening and narrowing rules from JLS 5.1.2 and 5.1.2.
+     *
+     * The only question remains is what is the type of the return value when the type conversion fails?
+     * When <code>avoidReflectException</code> is set to <code>true</code>
+     * Return <code>false</code> if the expected type is <code>boolean</code>
+     * Return numeric 0 for all numeric primitive types and '0' for <code>char</code>
+     *
+     * Otherwise:
+     * Return the value unchanged.
+     *
+     * In the invocation handler case:
+     * The value returned by {@link java.lang.reflect.InvocationHandler#invoke}
+     * will be converted into the expected type by the {@link java.lang.reflect.Proxy}.
+     * If the value and the expected type does not agree, and the value is not null,
+     * a ClassCastException will be thrown. A NullPointerException will result if the
+     * expected type is a primitive type and the value is null.
+     * This behavior causes annotation processors a lot of pain and the decision is
+     * to not throw such unchecked exception. In the case where a ClassCastException or
+     * NullPointerException will be thrown return some dummy value. Otherwise, return
+     * the original value.
+     * Chosen dummy values:
+     * Return <code>false</code> if the expected type is <code>boolean</code>
+     * Return numeric 0 for all numeric primitive types and '0' for <code>char</code>
+     *
+     * This behavior is triggered by setting <code>avoidReflectException</code> to <code>true</code>
+     *
+     * Note: the new behavior deviates from what's documented in
+     * {@link java.lang.reflect.InvocationHandler#invoke} and also deviates from
+     * Sun's implementation.
+     *
+     * @param value the current value from the annotation instance.
+     * @param expectedType the expected type of the value.
+     *
+     */
+    public static Object performNecessaryPrimitiveTypeConversion(
+    		final Class<?> expectedType,
+    		final Object value,
+    		final boolean avoidReflectException)
+    {
+    	assert expectedType.isPrimitive() : "expectedType is not a primitive type: " + expectedType.getName(); //$NON-NLS-1$
+    	if( value == null)
+    		return avoidReflectException ? getMatchingDummyValue(expectedType) : null;
+    	// apply widening conversion based on JLS 5.1.2 and 5.1.3
+    	final String typeName = expectedType.getName();
+		final char expectedTypeChar = typeName.charAt(0);
+		final int nameLen = typeName.length();
+		// widening byte -> short, int, long, float or double
+		// narrowing byte -> char
+		if( value instanceof Byte )
+		{
+			final byte b = ((Byte)value).byteValue();
+			switch( expectedTypeChar )
+			{
+			case 'b':
+				if(nameLen == 4) // byte
+					return value; // exact match.
+				else
+					return avoidReflectException ? Boolean.FALSE : value;
+			case 'c':
+				return Character.valueOf((char) b); // narrowing.
+			case 'd':
+				return Double.valueOf(b); // widening.
+			case 'f':
+				return  Float.valueOf(b); // widening.
+			case 'i':
+				return Integer.valueOf(b); // widening.
+			case 'l':
+				return Long.valueOf(b); // widening.
+			case 's':
+				return Short.valueOf(b); // widening.
+			default:
+				throw new IllegalStateException("unknown type " + expectedTypeChar); //$NON-NLS-1$
+			}
+		}
+		// widening short -> int, long, float, or double
+		// narrowing short -> byte or char
+		else if( value instanceof Short )
+		{
+			final short s = ((Short)value).shortValue();
+			switch( expectedTypeChar )
+			{
+			case 'b':
+				if(nameLen == 4) // byte
+					return Byte.valueOf((byte) s); // narrowing.
+				else
+					return avoidReflectException ? Boolean.FALSE : value; // completely wrong.
+			case 'c':
+				return Character.valueOf((char) s); // narrowing.
+			case 'd':
+				return Double.valueOf(s); // widening.
+			case 'f':
+				return Float.valueOf(s); // widening.
+			case 'i':
+				return Integer.valueOf(s); // widening.
+			case 'l':
+				return Long.valueOf(s); // widening.
+			case 's':
+				return value; // exact match
+			default:
+				throw new IllegalStateException("unknown type " + expectedTypeChar); //$NON-NLS-1$
+			}
+		}
+		// widening char -> int, long, float, or double
+		// narrowing char -> byte or short
+		else if( value instanceof Character )
+		{
+			final char c = ((Character)value).charValue();
+			switch( expectedTypeChar )
+			{
+			case 'b':
+				if(nameLen == 4) // byte
+					return Byte.valueOf((byte) c); // narrowing.
+				else
+					return avoidReflectException ? Boolean.FALSE : value; // completely wrong.
+			case 'c':
+				return value; // exact match
+			case 'd':
+				return Double.valueOf(c); // widening.
+			case 'f':
+				return Float.valueOf(c); // widening.
+			case 'i':
+				return Integer.valueOf(c); // widening.
+			case 'l':
+				return Long.valueOf(c); // widening.
+			case 's':
+				return Short.valueOf((short) c); // narrowing.
+			default:
+				throw new IllegalStateException("unknown type " + expectedTypeChar); //$NON-NLS-1$
+			}
+		}
+
+		// widening int -> long, float, or double
+		// narrowing int -> byte, short, or char
+		else if( value instanceof Integer )
+		{
+			final int i = ((Integer)value).intValue();
+			switch( expectedTypeChar )
+			{
+			case 'b':
+				if(nameLen == 4) // byte
+					return Byte.valueOf((byte) i); // narrowing.
+				else
+					return avoidReflectException ? Boolean.FALSE : value; // completely wrong.
+			case 'c':
+				return Character.valueOf((char) i); // narrowing
+			case 'd':
+				return Double.valueOf(i); // widening.
+			case 'f':
+				return Float.valueOf(i); // widening.
+			case 'i':
+				return value; // exact match
+			case 'l':
+				return Long.valueOf(i); // widening.
+			case 's':
+				return Short.valueOf((short) i); // narrowing.
+			default:
+				throw new IllegalStateException("unknown type " + expectedTypeChar); //$NON-NLS-1$
+			}
+		}
+		// widening long -> float or double
+		else if( value instanceof Long )
+		{
+			final long l = ((Long)value).longValue();
+			switch( expectedTypeChar )
+			{
+			case 'b': // both byte and boolean
+			case 'c':
+			case 'i':
+			case 's':
+				// completely wrong.
+				return avoidReflectException ? getMatchingDummyValue(expectedType) : value;
+			case 'd':
+				return Double.valueOf(l); // widening.
+			case 'f':
+				return Float.valueOf(l); // widening.
+			case 'l':
+				return value; // exact match.
+
+			default:
+				throw new IllegalStateException("unknown type " + expectedTypeChar); //$NON-NLS-1$
+			}
+		}
+
+		// widening float -> double
+		else if( value instanceof Float )
+		{
+			final float f = ((Float)value).floatValue();
+			switch( expectedTypeChar )
+			{
+			case 'b': // both byte and boolean
+			case 'c':
+			case 'i':
+			case 's':
+			case 'l':
+				// completely wrong.
+				return avoidReflectException ? getMatchingDummyValue(expectedType) : value;
+			case 'd':
+				return Double.valueOf(f); // widening.
+			case 'f':
+				return value; // exact match.
+			default:
+				throw new IllegalStateException("unknown type " + expectedTypeChar); //$NON-NLS-1$
+			}
+		}
+		else if( value instanceof Double ){
+			if(expectedTypeChar == 'd' )
+				return value; // exact match
+			else{
+				return avoidReflectException ? getMatchingDummyValue(expectedType) : value; // completely wrong.
+			}
+		}
+		else if( value instanceof Boolean ){
+			if( expectedTypeChar == 'b' && nameLen == 7) // "boolean".length() == 7
+				return value;
+			else
+				return avoidReflectException ? getMatchingDummyValue(expectedType) : value; // completely wrong.
+		}
+		else // can't convert
+			return avoidReflectException ? getMatchingDummyValue(expectedType) : value;
+    }
+
+    /**
+     * Set an element of an array to the appropriate dummy value type
+     * @param array
+     * @param i
+     * @param expectedLeafType
+     */
+	public static void setArrayMatchingDummyValue(Object array, int i, Class<?> expectedLeafType)
+	{
+		if (boolean.class.equals(expectedLeafType)) {
+			Array.setBoolean(array, i, false);
+		}
+		else if (byte.class.equals(expectedLeafType)) {
+			Array.setByte(array, i, DUMMY_BYTE);
+		}
+		else if (char.class.equals(expectedLeafType)) {
+			Array.setChar(array, i, DUMMY_CHAR);
+		}
+		else if (double.class.equals(expectedLeafType)) {
+			Array.setDouble(array, i, DUMMY_DOUBLE);
+		}
+		else if (float.class.equals(expectedLeafType)) {
+			Array.setFloat(array, i, DUMMY_FLOAT);
+		}
+		else if (int.class.equals(expectedLeafType)) {
+			Array.setInt(array, i, DUMMY_INTEGER);
+		}
+		else if (long.class.equals(expectedLeafType)) {
+			Array.setLong(array, i, DUMMY_LONG);
+		}
+		else if (short.class.equals(expectedLeafType)) {
+			Array.setShort(array, i, DUMMY_SHORT);
+		}
+		else {
+			Array.set(array, i, null);
+		}
+	}
+
+	/* Wrap repeating annotations into their container, return an array of bindings.
+	   Incoming array is not modified. The resulting array may be null but will not contain null
+	   entries.
+	*/
+	public static AnnotationBinding [] getPackedAnnotationBindings(AnnotationBinding [] annotations) {
+
+		int length = annotations == null ? 0 : annotations.length;
+		if (length == 0)
+			return annotations;
+
+		AnnotationBinding[] repackagedBindings = annotations; // only replicate if repackaging.
+		for (int i = 0; i < length; i++) {
+			AnnotationBinding annotation = repackagedBindings[i];
+			if (annotation == null) continue;
+			ReferenceBinding annotationType = annotation.getAnnotationType();
+			if (!annotationType.isRepeatableAnnotationType())
+				continue;
+			ReferenceBinding containerType = annotationType.containerAnnotationType();
+			if (containerType == null)
+				continue; // FUBAR.
+			MethodBinding [] values = containerType.getMethods(TypeConstants.VALUE);
+			if (values == null || values.length != 1)
+				continue; // FUBAR.
+			MethodBinding value = values[0];
+			if (value.returnType == null || value.returnType.dimensions() != 1 || TypeBinding.notEquals(value.returnType.leafComponentType(), annotationType))
+				continue; // FUBAR
+
+			// We have a kosher repeatable annotation with a kosher containing type. See if actually repeats.
+			List<AnnotationBinding> containees = null;
+			for (int j = i + 1; j < length; j++) {
+				AnnotationBinding otherAnnotation = repackagedBindings[j];
+				if (otherAnnotation == null) continue;
+				if (otherAnnotation.getAnnotationType() == annotationType) { //$IDENTITY-COMPARISON$
+					if (repackagedBindings == annotations)
+						System.arraycopy(repackagedBindings, 0, repackagedBindings = new AnnotationBinding[length], 0, length);
+					repackagedBindings[j] = null; // so it is not double packed.
+					if (containees == null) {
+						containees = new ArrayList<>();
+						containees.add(annotation);
+					}
+					containees.add(otherAnnotation);
+				}
+			}
+			if (containees != null) {
+				ElementValuePair [] elementValuePairs = new ElementValuePair [] { new ElementValuePair(TypeConstants.VALUE, containees.toArray(), value) };
+				repackagedBindings[i] = new AnnotationBinding(containerType, elementValuePairs);
+			}
+		}
+
+		int finalTally = 0;
+		for (int i = 0; i < length; i++) {
+			if (repackagedBindings[i] != null)
+				finalTally++;
+		}
+
+		if (repackagedBindings == annotations && finalTally == length) {
+			return annotations;
+		}
+
+		annotations = new AnnotationBinding [finalTally];
+		for (int i = 0, j = 0; i < length; i++) {
+			if (repackagedBindings[i] != null)
+				annotations[j++] = repackagedBindings[i];
+		}
+		return annotations;
+	}
+
+	/* Unwrap container annotations into the repeated annotations, return an array of bindings that includes the container and the containees.
+	*/
+	public static AnnotationBinding [] getUnpackedAnnotationBindings(AnnotationBinding [] annotations) {
+
+		int length = annotations == null ? 0 : annotations.length;
+		if (length == 0)
+			return annotations;
+
+		List<AnnotationBinding> unpackedAnnotations = new ArrayList<>();
+		for (int i = 0; i < length; i++) {
+			AnnotationBinding annotation = annotations[i];
+			if (annotation == null) continue;
+			unpackedAnnotations.add(annotation);
+			ReferenceBinding annotationType = annotation.getAnnotationType();
+
+			MethodBinding [] values = annotationType.getMethods(TypeConstants.VALUE);
+			if (values == null || values.length != 1)
+				continue;
+			MethodBinding value = values[0];
+
+			if (value.returnType.dimensions() != 1)
+				continue;
+
+			TypeBinding containeeType = value.returnType.leafComponentType();
+			if (containeeType == null || !containeeType.isAnnotationType() || !containeeType.isRepeatableAnnotationType())
+				continue;
+
+			if (containeeType.containerAnnotationType() != annotationType) //$IDENTITY-COMPARISON$
+				continue;
+
+			// We have a kosher container: unwrap the contained annotations.
+			ElementValuePair [] elementValuePairs = annotation.getElementValuePairs();
+			for (ElementValuePair elementValuePair : elementValuePairs) {
+				if (CharOperation.equals(elementValuePair.getName(), TypeConstants.VALUE)) {
+					Object [] containees = (Object []) elementValuePair.getValue();
+					for (Object object : containees) {
+						unpackedAnnotations.add((AnnotationBinding) object);
+					}
+					break;
+				}
+			}
+		}
+		return unpackedAnnotations.toArray(new AnnotationBinding [unpackedAnnotations.size()]);
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/IElementInfo.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/IElementInfo.java
new file mode 100644
index 0000000..dc0327a
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/IElementInfo.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+/**
+ * Additional information available for Elements that are implemented
+ * within the Eclipse APT framework.
+ * @see javax.lang.model.element.Element
+ * @since 3.3
+ */
+public interface IElementInfo {
+	/**
+	 * Get the project-relative path to the source file that contains this element.
+	 * If the element is a PackageElement, the "source file" is package-info.java.
+	 * If the element is not recognized or does not exist in the project for some
+	 * reason, returns null.
+	 * @return the project-relative path, or null.
+	 */
+	public String getFileName();
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ModuleElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ModuleElementImpl.java
new file mode 100644
index 0000000..04fbde7
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/ModuleElementImpl.java
@@ -0,0 +1,370 @@
+/*******************************************************************************
+ * Copyright (c) 2018, 2020 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PlainPackageBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+
+public class ModuleElementImpl extends ElementImpl implements ModuleElement {
+
+	ModuleBinding binding;
+	private List<Directive> directives;
+	private static List<Directive> EMPTY_DIRECTIVES = Collections.emptyList();
+
+	/**
+	 * In general, clients should call
+	 * {@link Factory#newDeclaredType(org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding)} or
+	 * {@link Factory#newElement(org.eclipse.jdt.internal.compiler.lookup.Binding)}
+	 * to create new instances.
+	 */
+	ModuleElementImpl(BaseProcessingEnvImpl env, ModuleBinding binding) {
+		super(env, binding);
+		this.binding = binding;
+	}
+
+	@Override
+	public ElementKind getKind() {
+		return ElementKind.MODULE;
+	}
+
+	@Override
+	public Set<Modifier> getModifiers() {
+		int modifiers = this.binding.modifiers;
+		return Factory.getModifiers(modifiers, getKind(), false);
+	}
+
+	@Override
+	public Name getQualifiedName() {
+		return new NameImpl(this.binding.moduleName);
+	}
+
+	@Override
+	public Name getSimpleName() {
+		char[] simpleName = this.binding.moduleName;
+		for(int i = simpleName.length-1;i>=0;i--) {
+			if(simpleName[i] == '.') {
+				simpleName = Arrays.copyOfRange(simpleName, i+1, simpleName.length);
+				break;
+			}
+		}
+		return new NameImpl(simpleName);
+	}
+
+	@Override
+	public List<? extends Element> getEnclosedElements() {
+		ModuleBinding module = this.binding;
+		Set<PlainPackageBinding> unique = new HashSet<>();
+		for (PlainPackageBinding p : module.declaredPackages.values()) {
+			if (!p.hasCompilationUnit(true))
+				continue;
+			unique.add(p);
+		}
+		if (module.isUnnamed()) {
+			PlainPackageBinding def = module.environment.defaultPackage;
+			// FIXME: Does it have any impact for unnamed modules - default package combo?
+			if (def != null && def.hasCompilationUnit(true)) {
+				unique.add(def);
+			}
+		} else {
+			for (PlainPackageBinding pBinding : this.binding.getExports()) {
+				unique.add(pBinding);
+			}
+			for (PlainPackageBinding pBinding : this.binding.getOpens()) {
+				unique.add(pBinding);
+			}
+		}
+		List<Element> enclosed = new ArrayList<>(unique.size());
+		for (PlainPackageBinding p : unique) {
+			PackageElement pElement = (PackageElement) _env.getFactory().newElement(p);
+			enclosed.add(pElement);
+		}
+		return Collections.unmodifiableList(enclosed);
+	}
+
+	@Override
+	public boolean isOpen() {
+		return (this.binding.modifiers & ClassFileConstants.ACC_OPEN) != 0;
+	}
+
+	@Override
+	public boolean isUnnamed() {
+		return this.binding.moduleName.length == 0;
+	}
+
+	@Override
+	public Element getEnclosingElement() {
+		// As of today, modules have no enclosing element
+		return null;
+	}
+
+	@Override
+	public List<? extends Directive> getDirectives() {
+		if (isUnnamed()) {
+			return EMPTY_DIRECTIVES;
+		}
+		if (this.directives == null)
+			this.directives = new ArrayList<>();
+
+		PlainPackageBinding[] packs = this.binding.getExports();
+		for (PlainPackageBinding exp : packs) {
+			this.directives.add(new ExportsDirectiveImpl(exp));
+		}
+		Set<ModuleBinding> transitive = new HashSet<>();
+		for (ModuleBinding mBinding : this.binding.getRequiresTransitive()) {
+			transitive.add(mBinding);
+		}
+		ModuleBinding[] required = this.binding.getRequires();
+		for (ModuleBinding mBinding : required) {
+			if (transitive.contains(mBinding)) {
+				this.directives.add(new RequiresDirectiveImpl(mBinding, true));
+			} else {
+				this.directives.add(new RequiresDirectiveImpl(mBinding, false));
+			}
+		}
+
+		TypeBinding[] tBindings = this.binding.getUses();
+		for (TypeBinding tBinding : tBindings) {
+			this.directives.add(new UsesDirectiveImpl(tBinding));
+		}
+		tBindings = this.binding.getServices();
+		for (TypeBinding tBinding : tBindings) {
+			this.directives.add(new ProvidesDirectiveImpl(tBinding));
+		}
+		packs = this.binding.getOpens();
+		for (PlainPackageBinding exp : packs) {
+			this.directives.add(new OpensDirectiveImpl(exp));
+		}
+		return this.directives;
+	}
+
+	@Override
+	public <R, P> R accept(ElementVisitor<R, P> visitor, P param) {
+		return visitor.visitModule(this, param);
+	}
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings() {
+		return ((ModuleBinding) _binding).getAnnotations();
+	}
+
+	abstract class PackageDirectiveImpl {
+		PackageBinding binding;
+		List<ModuleElement> targets;
+
+		PackageDirectiveImpl(PackageBinding pBinding) {
+			this.binding = pBinding;
+		}
+
+		public PackageElement getPackage() {
+			return _env.getFactory().newPackageElement(binding);
+		}
+
+		public List<? extends ModuleElement> getTargetModules(String[] restrictions) {
+			if(this.targets != null) {
+				return targets;
+			}
+			if (restrictions.length == 0) {
+				return (this.targets = null);
+			}
+			List<ModuleElement> targets = new ArrayList<>(restrictions.length);
+			for (String string : restrictions) {
+				ModuleBinding target = ModuleElementImpl.this.binding.environment.getModule(string.toCharArray());
+				if (target != null) {
+					ModuleElement element = ((ModuleElement) _env.getFactory().newElement(target));
+					targets.add(element);
+				}
+			}
+			return (this.targets = Collections.unmodifiableList(targets));
+		}
+	}
+
+	class ExportsDirectiveImpl extends PackageDirectiveImpl implements ModuleElement.ExportsDirective {
+
+		ExportsDirectiveImpl(PackageBinding pBinding) {
+			super(pBinding);
+		}
+
+		@Override
+		public <R, P> R accept(DirectiveVisitor<R, P> visitor, P param) {
+			return visitor.visitExports(this, param);
+		}
+
+		@Override
+		public javax.lang.model.element.ModuleElement.DirectiveKind getKind() {
+			return DirectiveKind.EXPORTS;
+		}
+
+		@Override
+		public PackageElement getPackage() {
+			return _env.getFactory().newPackageElement(binding);
+		}
+		@Override
+		public List<? extends ModuleElement> getTargetModules() {
+			if(this.targets != null) {
+				return targets;
+			}
+			return getTargetModules(ModuleElementImpl.this.binding.getExportRestrictions(this.binding));
+		}
+
+	}
+
+	class RequiresDirectiveImpl implements ModuleElement.RequiresDirective {
+		ModuleBinding dependency;
+		boolean transitive;
+
+		RequiresDirectiveImpl(ModuleBinding dependency, boolean transitive) {
+			this.dependency = dependency;
+			this.transitive = transitive;
+		}
+
+		@Override
+		public <R, P> R accept(DirectiveVisitor<R, P> visitor, P param) {
+			return visitor.visitRequires(this, param);
+		}
+
+		@Override
+		public javax.lang.model.element.ModuleElement.DirectiveKind getKind() {
+			return DirectiveKind.REQUIRES;
+		}
+
+		@Override
+		public ModuleElement getDependency() {
+			return (ModuleElement) _env.getFactory().newElement(dependency, ElementKind.MODULE);
+		}
+
+		@Override
+		public boolean isStatic() {
+			// TODO: Yet to see this in ModuleBinding. Check again.
+			return false;
+		}
+
+		@Override
+		public boolean isTransitive() {
+			return this.transitive;
+		}
+	}
+
+	class OpensDirectiveImpl extends PackageDirectiveImpl implements ModuleElement.OpensDirective {
+
+		OpensDirectiveImpl(PackageBinding pBinding) {
+			super(pBinding);
+		}
+
+		@Override
+		public <R, P> R accept(DirectiveVisitor<R, P> visitor, P param) {
+			return visitor.visitOpens(this, param);
+		}
+
+		@Override
+		public javax.lang.model.element.ModuleElement.DirectiveKind getKind() {
+			return DirectiveKind.OPENS;
+		}
+		@Override
+		public List<? extends ModuleElement> getTargetModules() {
+			if(this.targets != null) {
+				return targets;
+			}
+			return getTargetModules(ModuleElementImpl.this.binding.getOpenRestrictions(this.binding));
+		}
+	}
+
+	class UsesDirectiveImpl implements ModuleElement.UsesDirective {
+		TypeBinding binding = null;
+
+		UsesDirectiveImpl(TypeBinding binding) {
+			this.binding = binding;
+		}
+
+		@Override
+		public <R, P> R accept(DirectiveVisitor<R, P> visitor, P param) {
+			return visitor.visitUses(this, param);
+		}
+
+		@Override
+		public DirectiveKind getKind() {
+			return DirectiveKind.USES;
+		}
+
+		@Override
+		public TypeElement getService() {
+			return (TypeElement) _env.getFactory().newElement(binding);
+		}
+
+	}
+
+	class ProvidesDirectiveImpl implements ModuleElement.ProvidesDirective {
+
+		TypeBinding service;
+		public List<? extends TypeElement> implementations;
+
+		ProvidesDirectiveImpl(TypeBinding service) {
+			this.service = service;
+		}
+
+		@Override
+		public <R, P> R accept(DirectiveVisitor<R, P> visitor, P param) {
+			return visitor.visitProvides(this, param);
+		}
+
+		@Override
+		public DirectiveKind getKind() {
+			return DirectiveKind.PROVIDES;
+		}
+
+		@Override
+		public List<? extends TypeElement> getImplementations() {
+			if (this.implementations != null)
+				return this.implementations;
+
+			TypeBinding[] implementations2 = ModuleElementImpl.this.binding.getImplementations(this.service);
+			if (implementations2.length == 0) {
+				return (this.implementations = Collections.emptyList());
+			}
+
+			List<TypeElement> list = new ArrayList<>(implementations2.length);
+			Factory factory = _env.getFactory();
+			for (TypeBinding type: implementations2) {
+				TypeElement element = (TypeElement) factory.newElement(type);
+				list.add(element);
+			}
+			return Collections.unmodifiableList(list);
+		}
+
+		@Override
+		public TypeElement getService() {
+			return (TypeElement) _env.getFactory().newElement(this.service);
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/NameImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/NameImpl.java
new file mode 100644
index 0000000..828e231
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/NameImpl.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import javax.lang.model.element.Name;
+
+/**
+ * A String-based implementation of the type used to return strings in javax.lang.model.
+ */
+public class NameImpl implements Name {
+
+	private final String _name;
+
+	/** nullary constructor is prohibited */
+	@SuppressWarnings("unused")
+	private NameImpl()
+	{
+		_name = null;
+	}
+
+	public NameImpl(CharSequence cs)
+	{
+		_name = cs.toString();
+	}
+
+	public NameImpl(char[] chars)
+	{
+		_name = String.valueOf(chars);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.element.Name#contentEquals(java.lang.CharSequence)
+	 */
+	@Override
+	public boolean contentEquals(CharSequence cs) {
+		return _name.equals(cs.toString());
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.CharSequence#charAt(int)
+	 */
+	@Override
+	public char charAt(int index) {
+		return _name.charAt(index);
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.CharSequence#length()
+	 */
+	@Override
+	public int length() {
+		return _name.length();
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.CharSequence#subSequence(int, int)
+	 */
+	@Override
+	public CharSequence subSequence(int start, int end) {
+		return _name.subSequence(start, end);
+	}
+
+	@Override
+	public String toString() {
+		return _name;
+	}
+
+	@Override
+	public int hashCode() {
+		return _name.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final NameImpl other = (NameImpl) obj;
+		return _name.equals(other._name);
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/NoTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/NoTypeImpl.java
new file mode 100644
index 0000000..884b710
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/NoTypeImpl.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2015 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - Java 8 support
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+
+/**
+ * An implementation of NoType, which is used to represent certain pseudo-types.
+ * @see NoType
+ */
+public class NoTypeImpl extends TypeMirrorImpl implements NoType, NullType
+{
+	private final TypeKind _kind;
+
+	public static final NoType NO_TYPE_NONE = new NoTypeImpl(TypeKind.NONE);
+	public static final NoType NO_TYPE_VOID = new NoTypeImpl(TypeKind.VOID, TypeBinding.VOID);
+	public static final NoType NO_TYPE_PACKAGE = new NoTypeImpl(TypeKind.PACKAGE);
+	public static final NullType NULL_TYPE = new NoTypeImpl(TypeKind.NULL, TypeBinding.NULL);
+	public static final Binding NO_TYPE_BINDING = new Binding() {
+		@Override
+		public int kind() {
+			throw new IllegalStateException();
+		}
+
+		@Override
+		public char[] readableName() {
+			throw new IllegalStateException();
+		}
+	};
+
+	public NoTypeImpl(TypeKind kind) {
+		super(null, NO_TYPE_BINDING);
+		_kind = kind;
+	}
+	public NoTypeImpl(TypeKind kind, Binding binding) {
+		super(null, binding);
+		_kind = kind;
+	}
+
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p)
+	{
+		switch(this.getKind())
+		{
+			case NULL :
+				return v.visitNull(this, p);
+			default:
+				return v.visitNoType(this, p);
+		}
+	}
+
+	@Override
+	public TypeKind getKind()
+	{
+		return _kind;
+	}
+
+	@Override
+	public String toString()
+	{
+		switch (_kind) {
+		default:
+		case NONE:
+			return "none"; //$NON-NLS-1$
+		case NULL:
+			return "null"; //$NON-NLS-1$
+		case VOID:
+			return "void"; //$NON-NLS-1$
+		case PACKAGE:
+			return "package"; //$NON-NLS-1$
+		case MODULE:
+			return "module"; //$NON-NLS-1$
+		}
+	}
+
+	@Override
+	public List<? extends AnnotationMirror> getAnnotationMirrors() {
+		return Factory.EMPTY_ANNOTATION_MIRRORS;
+	}
+
+	@Override
+	public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+		return null;
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+		return (A[]) Array.newInstance(annotationType, 0);
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/PackageElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/PackageElementImpl.java
new file mode 100644
index 0000000..79a2bd5
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/PackageElementImpl.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2017 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+
+/**
+ * Implementation of PackageElement, which represents a package
+ */
+public class PackageElementImpl extends ElementImpl implements PackageElement {
+
+	PackageElementImpl(BaseProcessingEnvImpl env, PackageBinding binding) {
+		super(env, binding);
+	}
+
+	@Override
+	public <R, P> R accept(ElementVisitor<R, P> v, P p)
+	{
+		return v.visitPackage(this, p);
+	}
+
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings()
+	{
+		PackageBinding packageBinding = (PackageBinding) this._binding;
+		char[][] compoundName = CharOperation.arrayConcat(packageBinding.compoundName, TypeConstants.PACKAGE_INFO_NAME);
+		ReferenceBinding type = packageBinding.environment.getType(compoundName);
+		AnnotationBinding[] annotations = null;
+		if (type != null && type.isValidBinding()) {
+			annotations = type.getAnnotations();
+		}
+		return annotations;
+	}
+
+	@Override
+	public List<? extends Element> getEnclosedElements() {
+		PackageBinding binding = (PackageBinding)_binding;
+		LookupEnvironment environment = binding.environment;
+		char[][][] typeNames = null;
+		INameEnvironment nameEnvironment = binding.environment.nameEnvironment;
+		if (nameEnvironment instanceof FileSystem) {
+			typeNames = ((FileSystem) nameEnvironment).findTypeNames(binding.compoundName);
+		}
+		HashSet<Element> set = new HashSet<>();
+		Set<ReferenceBinding> types = new HashSet<>();
+		if (typeNames != null) {
+			for (char[][] typeName : typeNames) {
+				if (typeName == null) continue;
+				ReferenceBinding type = environment.getType(typeName);
+				if (type == null || type.isMemberType()) continue;
+				if (type.isValidBinding()) {
+					Element newElement = _env.getFactory().newElement(type);
+					if (newElement.getKind() != ElementKind.PACKAGE) {
+						set.add(newElement);
+						types.add(type);
+					}
+				}
+			}
+		}
+		if (binding.knownTypes != null) {
+			ReferenceBinding[] knownTypes = binding.knownTypes.valueTable;
+			for (ReferenceBinding referenceBinding : knownTypes) {
+				if (referenceBinding != null && referenceBinding.isValidBinding() && referenceBinding.enclosingType() == null) {
+					if (!types.contains(referenceBinding)) {
+						Element newElement = _env.getFactory().newElement(referenceBinding);
+						if (newElement.getKind() != ElementKind.PACKAGE)
+							set.add(newElement);
+					}
+				}
+			}
+		}
+		ArrayList<Element> list = new ArrayList<>(set.size());
+		list.addAll(set);
+		return Collections.unmodifiableList(list);
+	}
+
+	@Override
+	public Element getEnclosingElement() {
+		if (super._env.getCompiler().options.sourceLevel < ClassFileConstants.JDK9) {
+			return null;
+		}
+		PackageBinding pBinding = (PackageBinding) _binding;
+		ModuleBinding module = pBinding.enclosingModule;
+		if (module == null)
+			return null;
+		return new ModuleElementImpl(_env, module);
+	}
+
+	@Override
+	public ElementKind getKind() {
+		return ElementKind.PACKAGE;
+	}
+
+	@Override
+	PackageElement getPackage()
+	{
+		return this;
+	}
+
+	@Override
+	public Name getSimpleName() {
+		char[][] compoundName = ((PackageBinding)_binding).compoundName;
+		int length = compoundName.length;
+		if (length == 0) {
+			return new NameImpl(CharOperation.NO_CHAR);
+		}
+		return new NameImpl(compoundName[length - 1]);
+	}
+
+	@Override
+	public Name getQualifiedName() {
+		return new NameImpl(CharOperation.concatWith(((PackageBinding)_binding).compoundName, '.'));
+	}
+
+	@Override
+	public boolean isUnnamed() {
+		PackageBinding binding = (PackageBinding)_binding;
+		return binding.compoundName == CharOperation.NO_CHAR_CHAR;
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/PrimitiveTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/PrimitiveTypeImpl.java
new file mode 100644
index 0000000..8f4021a
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/PrimitiveTypeImpl.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2014 BEA Systems, Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - Java 8 support
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+/**
+ *
+ * @since 3.3
+ */
+public class PrimitiveTypeImpl extends TypeMirrorImpl implements PrimitiveType {
+
+	public final static PrimitiveTypeImpl BOOLEAN = new PrimitiveTypeImpl(TypeBinding.BOOLEAN);
+	public final static PrimitiveTypeImpl BYTE = new PrimitiveTypeImpl(TypeBinding.BYTE);
+	public final static PrimitiveTypeImpl CHAR = new PrimitiveTypeImpl(TypeBinding.CHAR);
+	public final static PrimitiveTypeImpl DOUBLE = new PrimitiveTypeImpl(TypeBinding.DOUBLE);
+	public final static PrimitiveTypeImpl FLOAT = new PrimitiveTypeImpl(TypeBinding.FLOAT);
+	public final static PrimitiveTypeImpl INT = new PrimitiveTypeImpl(TypeBinding.INT);
+	public final static PrimitiveTypeImpl LONG = new PrimitiveTypeImpl(TypeBinding.LONG);
+	public final static PrimitiveTypeImpl SHORT = new PrimitiveTypeImpl(TypeBinding.SHORT);
+
+	/**
+	 * Clients should call {@link Factory#getPrimitiveType(TypeKind)},
+	 * rather than creating new objects.
+	 */
+	private PrimitiveTypeImpl(BaseTypeBinding binding) {
+		// Primitive types do not need an environment!
+		super(null, binding);
+	}
+
+	PrimitiveTypeImpl(BaseProcessingEnvImpl env, BaseTypeBinding binding) {
+		// From Java 8, base type bindings can hold annotations and hence need the environment.
+		super(env, binding);
+	}
+
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p)
+	{
+		return v.visitPrimitive(this, p);
+	}
+
+	@Override
+	public TypeKind getKind() {
+		return getKind((BaseTypeBinding)_binding);
+	}
+
+	public static TypeKind getKind(BaseTypeBinding binding) {
+		switch (binding.id) {
+		case TypeIds.T_boolean:
+			return TypeKind.BOOLEAN;
+		case TypeIds.T_byte:
+			return TypeKind.BYTE;
+		case TypeIds.T_char:
+			return TypeKind.CHAR;
+		case TypeIds.T_double:
+			return TypeKind.DOUBLE;
+		case TypeIds.T_float:
+			return TypeKind.FLOAT;
+		case TypeIds.T_int:
+			return TypeKind.INT;
+		case TypeIds.T_long:
+			return TypeKind.LONG;
+		case TypeIds.T_short:
+			return TypeKind.SHORT;
+		default:
+			throw new IllegalArgumentException("BaseTypeBinding of unexpected id " + binding.id); //$NON-NLS-1$
+		}
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/RecordComponentElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/RecordComponentElementImpl.java
new file mode 100644
index 0000000..06c36f8
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/RecordComponentElementImpl.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2020, 2021 IBM Corporation.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.RecordComponentElement;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+
+public class RecordComponentElementImpl extends VariableElementImpl implements RecordComponentElement {
+
+	protected RecordComponentElementImpl(BaseProcessingEnvImpl env, RecordComponentBinding binding) {
+		super(env, binding);
+	}
+
+	@Override
+	public ElementKind getKind() {
+		return ElementKind.RECORD_COMPONENT;
+	}
+
+	@Override
+	public ExecutableElement getAccessor() {
+		RecordComponentBinding comp = (RecordComponentBinding) this._binding;
+		ReferenceBinding binding = comp.declaringRecord;
+		if (binding instanceof SourceTypeBinding) {
+			MethodBinding accessor = ((SourceTypeBinding) binding).getRecordComponentAccessor(comp.name);
+			return new ExecutableElementImpl(_env, accessor);
+		}
+		return null;
+	}
+
+	@Override
+	public <R, P> R accept(ElementVisitor<R, P> visitor, P param) {
+		return visitor.visitRecordComponent(this, param);
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeElementImpl.java
new file mode 100644
index 0000000..435b19c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeElementImpl.java
@@ -0,0 +1,398 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2021 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.RecordComponentElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+public class TypeElementImpl extends ElementImpl implements TypeElement {
+
+	/**
+	 * Compares Element instances possibly returned by
+	 * {@link TypeElement#getEnclosedElements()} based on their source location, if available.
+	 *
+	 */
+	private static final class SourceLocationComparator implements Comparator<Element> {
+		private final IdentityHashMap<ElementImpl, Integer> sourceStartCache = new IdentityHashMap<>();
+
+		@Override
+		public int compare(Element o1, Element o2) {
+			ElementImpl e1 = (ElementImpl) o1;
+			ElementImpl e2 = (ElementImpl) o2;
+
+			return getSourceStart(e1) - getSourceStart(e2);
+		}
+
+		private int getSourceStart(ElementImpl e) {
+			Integer value = sourceStartCache.get(e);
+
+			if (value == null) {
+				value = determineSourceStart(e);
+				sourceStartCache.put(e, value);
+			}
+
+			return value;
+		}
+
+		private int determineSourceStart(ElementImpl e) {
+			switch(e.getKind()) {
+				case ANNOTATION_TYPE :
+				case INTERFACE :
+				case CLASS :
+				case ENUM :
+				case RECORD :
+					TypeElementImpl typeElementImpl = (TypeElementImpl) e;
+					Binding typeBinding = typeElementImpl._binding;
+					if (typeBinding instanceof SourceTypeBinding) {
+						SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) typeBinding;
+						TypeDeclaration typeDeclaration = (TypeDeclaration) sourceTypeBinding.scope.referenceContext();
+						return typeDeclaration.sourceStart;
+					}
+					break;
+				case CONSTRUCTOR :
+				case METHOD :
+					ExecutableElementImpl executableElementImpl = (ExecutableElementImpl) e;
+					Binding binding = executableElementImpl._binding;
+					if (binding instanceof MethodBinding) {
+						MethodBinding methodBinding = (MethodBinding) binding;
+						return methodBinding.sourceStart();
+					}
+					break;
+				case ENUM_CONSTANT :
+				case FIELD :
+				case RECORD_COMPONENT :
+					VariableElementImpl variableElementImpl = (VariableElementImpl) e;
+					binding = variableElementImpl._binding;
+					if (binding instanceof FieldBinding) {
+						FieldBinding fieldBinding = (FieldBinding) binding;
+						FieldDeclaration fieldDeclaration = fieldBinding.sourceField();
+						if (fieldDeclaration != null) {
+							return fieldDeclaration.sourceStart;
+						}
+					}
+					break;
+				default:
+					break;
+			}
+
+			return -1;
+		}
+	}
+
+	private final ElementKind _kindHint;
+
+	/**
+	 * In general, clients should call {@link Factory#newDeclaredType(ReferenceBinding)} or
+	 * {@link Factory#newElement(org.eclipse.jdt.internal.compiler.lookup.Binding)} to
+	 * create new instances.
+	 */
+	TypeElementImpl(BaseProcessingEnvImpl env, ReferenceBinding binding, ElementKind kindHint) {
+		super(env, binding);
+		_kindHint = kindHint;
+	}
+
+	@Override
+	public <R, P> R accept(ElementVisitor<R, P> v, P p)
+	{
+		return v.visitType(this, p);
+	}
+
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings()
+	{
+		return ((ReferenceBinding)_binding).getAnnotations();
+	}
+
+	@Override
+	public List<? extends Element> getEnclosedElements() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		List<Element> enclosed = new ArrayList<>(binding.fieldCount() + binding.methods().length + binding.memberTypes().length);
+		for (MethodBinding method : binding.methods()) {
+			ExecutableElement executable = new ExecutableElementImpl(_env, method);
+			enclosed.add(executable);
+		}
+		for (FieldBinding field : binding.fields()) {
+			// TODO no field should be excluded according to the JLS
+			if (!field.isSynthetic()) {
+				 VariableElement variable = new VariableElementImpl(_env, field);
+				 enclosed.add(variable);
+			}
+		}
+		if (binding.isRecord()) {
+			RecordComponentBinding[] components = binding.components();
+			for (RecordComponentBinding comp : components) {
+				RecordComponentElement rec = new RecordComponentElementImpl(_env, comp);
+				enclosed.add(rec);
+			}
+		}
+		for (ReferenceBinding memberType : binding.memberTypes()) {
+			TypeElement type = new TypeElementImpl(_env, memberType, null);
+			enclosed.add(type);
+		}
+		Collections.sort(enclosed, new SourceLocationComparator());
+		return Collections.unmodifiableList(enclosed);
+	}
+
+	@Override
+    public List<? extends RecordComponentElement> getRecordComponents() {
+		if (_binding instanceof SourceTypeBinding) {
+			SourceTypeBinding binding = (SourceTypeBinding) _binding;
+			List<RecordComponentElement> enclosed = new ArrayList<>();
+			for (RecordComponentBinding comp : binding.components()) {
+				RecordComponentElement variable = new RecordComponentElementImpl(_env, comp);
+				enclosed.add(variable);
+			}
+			Collections.sort(enclosed, new SourceLocationComparator());
+			return Collections.unmodifiableList(enclosed);
+		}
+		// TODO: Add code for BinaryTypeBinding, which, as of now doesn't seem to contain components
+		return Collections.emptyList();
+    }
+
+	@Override
+	public List<? extends TypeMirror> getPermittedSubclasses() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		if (binding.isSealed()) {
+			List<TypeMirror> permitted = new ArrayList<>();
+			for (ReferenceBinding type : binding.permittedTypes()) {
+				TypeMirror typeMirror = _env.getFactory().newTypeMirror(type);
+				permitted.add(typeMirror);
+			}
+			return Collections.unmodifiableList(permitted);
+		}
+		return Collections.emptyList();
+    }
+	@Override
+	public Element getEnclosingElement() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		ReferenceBinding enclosingType = binding.enclosingType();
+		if (null == enclosingType) {
+			// this is a top level type; get its package
+			return _env.getFactory().newPackageElement(binding.fPackage);
+		}
+		else {
+			return _env.getFactory().newElement(binding.enclosingType());
+		}
+	}
+
+	@Override
+	public String getFileName() {
+		char[] name = ((ReferenceBinding)_binding).getFileName();
+		if (name == null)
+			return null;
+		return new String(name);
+	}
+
+	@Override
+	public List<? extends TypeMirror> getInterfaces() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		if (null == binding.superInterfaces() || binding.superInterfaces().length == 0) {
+			return Collections.emptyList();
+		}
+		List<TypeMirror> interfaces = new ArrayList<>(binding.superInterfaces().length);
+		for (ReferenceBinding interfaceBinding : binding.superInterfaces()) {
+			TypeMirror interfaceType = _env.getFactory().newTypeMirror(interfaceBinding);
+			if (interfaceType.getKind() == TypeKind.ERROR) {
+				if (this._env.getSourceVersion().compareTo(SourceVersion.RELEASE_6) > 0) {
+					// for jdk 7 and above, add error types
+					interfaces.add(interfaceType);
+				}
+			} else {
+				interfaces.add(interfaceType);
+			}
+		}
+		return Collections.unmodifiableList(interfaces);
+	}
+
+	@Override
+	public ElementKind getKind() {
+		if (null != _kindHint) {
+			return _kindHint;
+		}
+		ReferenceBinding refBinding = (ReferenceBinding)_binding;
+		// The order of these comparisons is important: e.g., enum is subset of class
+		if (refBinding.isEnum()) {
+			return ElementKind.ENUM;
+		}
+		else if (refBinding.isRecord()) {
+			return ElementKind.RECORD;
+		}
+		else if (refBinding.isAnnotationType()) {
+			return ElementKind.ANNOTATION_TYPE;
+		}
+		else if (refBinding.isInterface()) {
+			return ElementKind.INTERFACE;
+		}
+		else if (refBinding.isClass()) {
+			return ElementKind.CLASS;
+		}
+		else {
+			throw new IllegalArgumentException("TypeElement " + new String(refBinding.shortReadableName()) +  //$NON-NLS-1$
+					" has unexpected attributes " + refBinding.modifiers); //$NON-NLS-1$
+		}
+	}
+
+	@Override
+	public Set<Modifier> getModifiers()
+	{
+		ReferenceBinding refBinding = (ReferenceBinding)_binding;
+		int modifiers = refBinding.modifiers;
+		if (refBinding.isInterface() && refBinding.isNestedType()) {
+			modifiers |= ClassFileConstants.AccStatic;
+		}
+		
+		return Factory.getModifiers(modifiers, getKind(), refBinding.isBinaryBinding());
+	}
+
+	@Override
+	public NestingKind getNestingKind() {
+		ReferenceBinding refBinding = (ReferenceBinding)_binding;
+		if (refBinding.isAnonymousType()) {
+			return NestingKind.ANONYMOUS;
+		} else if (refBinding.isLocalType()) {
+			return NestingKind.LOCAL;
+		} else if (refBinding.isMemberType()) {
+			return NestingKind.MEMBER;
+		}
+		return NestingKind.TOP_LEVEL;
+	}
+
+	@Override
+	PackageElement getPackage()
+	{
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		return _env.getFactory().newPackageElement(binding.fPackage);
+	}
+
+	@Override
+	public Name getQualifiedName() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		char[] qName;
+		if (binding.isMemberType()) {
+			qName = CharOperation.concatWith(binding.enclosingType().compoundName, binding.sourceName, '.');
+			CharOperation.replace(qName, '$', '.');
+		} else {
+			qName = CharOperation.concatWith(binding.compoundName, '.');
+		}
+		return new NameImpl(qName);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see org.eclipse.jdt.internal.compiler.apt.model.ElementImpl#getSimpleName()
+	 * @return last segment of name, e.g. for pa.pb.X.Y return Y.
+	 */
+	@Override
+	public Name getSimpleName()
+	{
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		return new NameImpl(binding.sourceName());
+	}
+
+	@Override
+	public TypeMirror getSuperclass() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		ReferenceBinding superBinding = binding.superclass();
+		if (null == superBinding || binding.isInterface()) {
+			return _env.getFactory().getNoType(TypeKind.NONE);
+		}
+		// superclass of a type must be a DeclaredType
+		return _env.getFactory().newTypeMirror(superBinding);
+	}
+
+	@Override
+	public List<? extends TypeParameterElement> getTypeParameters() {
+		ReferenceBinding binding = (ReferenceBinding)_binding;
+		TypeVariableBinding[] variables = binding.typeVariables();
+		if (variables.length == 0) {
+			return Collections.emptyList();
+		}
+		List<TypeParameterElement> params = new ArrayList<>(variables.length);
+		for (TypeVariableBinding variable : variables) {
+			params.add(_env.getFactory().newTypeParameterElement(variable, this));
+		}
+		return Collections.unmodifiableList(params);
+	}
+
+	@Override
+	public boolean hides(Element hidden)
+	{
+		if (!(hidden instanceof TypeElementImpl)) {
+			return false;
+		}
+		ReferenceBinding hiddenBinding = (ReferenceBinding)((TypeElementImpl)hidden)._binding;
+		if (hiddenBinding.isPrivate()) {
+			return false;
+		}
+		ReferenceBinding hiderBinding = (ReferenceBinding)_binding;
+		if (TypeBinding.equalsEquals(hiddenBinding, hiderBinding)) {
+			return false;
+		}
+		if (!hiddenBinding.isMemberType() || !hiderBinding.isMemberType()) {
+			return false;
+		}
+		if (!CharOperation.equals(hiddenBinding.sourceName, hiderBinding.sourceName)) {
+			return false;
+		}
+		return null != hiderBinding.enclosingType().findSuperTypeOriginatingFrom(hiddenBinding.enclosingType());
+	}
+
+	@Override
+	public String toString() {
+		ReferenceBinding binding = (ReferenceBinding) this._binding;
+		char[] concatWith = CharOperation.concatWith(binding.compoundName, '.');
+		if (binding.isNestedType()) {
+			CharOperation.replace(concatWith, '$', '.');
+			return new String(concatWith);
+		}
+		return new String(concatWith);
+
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeMirrorImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeMirrorImpl.java
new file mode 100644
index 0000000..14218e9
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeMirrorImpl.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2014 BEA Systems, Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - Java 8 support
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+
+/**
+ * Implementation of a TypeMirror.  TypeMirror represents a type, including
+ * types that have no declaration, such as primitives (int, boolean) and
+ * types that are specializations of declarations (List<String>).
+ */
+public class TypeMirrorImpl implements TypeMirror {
+
+	// Caution: _env will be NULL for unannotated primitive types (PrimitiveTypeImpl).
+	protected final BaseProcessingEnvImpl _env;
+	protected final Binding _binding;
+
+	/* package */ TypeMirrorImpl(BaseProcessingEnvImpl env, Binding binding) {
+		_env = env;
+		_binding = binding;
+	}
+
+	/* package */ Binding binding() {
+		return _binding;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#accept(javax.lang.model.type.TypeVisitor, java.lang.Object)
+	 */
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+		return v.visit(this, p);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#getKind()
+	 */
+	@Override
+	public TypeKind getKind() {
+		switch (_binding.kind()) {
+		// case Binding.TYPE:
+		// case Binding.RAW_TYPE:
+		// case Binding.GENERIC_TYPE:
+		// case Binding.PARAMETERIZED_TYPE:
+		// handled by DeclaredTypeImpl, etc.
+		// case Binding.BASE_TYPE: handled by PrimitiveTypeImpl
+		// case Binding.METHOD: handled by ExecutableTypeImpl
+		// case Binding.PACKAGE: handled by NoTypeImpl
+		// case Binding.WILDCARD_TYPE: handled by WildcardTypeImpl
+		// case Binding.ARRAY_TYPE: handled by ArrayTypeImpl
+		// case Binding.TYPE_PARAMETER: handled by TypeVariableImpl
+		// TODO: fill in the rest of these
+		case Binding.FIELD:
+		case Binding.LOCAL:
+		case Binding.RECORD_COMPONENT:
+		case Binding.VARIABLE:
+		case Binding.IMPORT:
+			throw new IllegalArgumentException("Invalid binding kind: " + _binding.kind()); //$NON-NLS-1$
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Object#toString()
+	 */
+	@Override
+	public String toString() {
+		return new String(_binding.readableName());
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Object#hashCode()
+	 */
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((_binding == null) ? 0 : _binding.hashCode());
+		return result;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!(obj instanceof TypeMirrorImpl))
+			return false;
+		final TypeMirrorImpl other = (TypeMirrorImpl) obj;
+		return _binding == other._binding;
+	}
+
+	/* Package any repeating annotations into containers, return others as is.
+	   In the compiler bindings repeating annotations are left in as is, hence
+	   this step. The return value would match what one would expect to see in
+	   a class file.
+	*/
+	public final AnnotationBinding [] getPackedAnnotationBindings() {
+		return Factory.getPackedAnnotationBindings(getAnnotationBindings());
+	}
+
+	protected AnnotationBinding[] getAnnotationBindings() {
+		return ((TypeBinding)_binding).getTypeAnnotations();
+	}
+
+	@Override
+	public List<? extends AnnotationMirror> getAnnotationMirrors() {
+		return _env == null ? Factory.EMPTY_ANNOTATION_MIRRORS :
+								_env.getFactory().getAnnotationMirrors(getPackedAnnotationBindings());
+	}
+
+	@Override
+	public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+		return _env == null ? null : _env.getFactory().getAnnotation(getPackedAnnotationBindings(), annotationType);
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+		if (_env == null)
+			return (A[]) Array.newInstance(annotationType, 0);
+		return _env.getFactory().getAnnotationsByType(Factory.getUnpackedAnnotationBindings(getPackedAnnotationBindings()), annotationType);
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeParameterElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeParameterElementImpl.java
new file mode 100644
index 0000000..6e0ab70
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeParameterElementImpl.java
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2015 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *    IBM Corporation - fix for 342470
+ *    IBM Corporation - fix for 342598
+ *    IBM Corporation - Java 8 support
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.type.TypeMirror;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+/**
+ *
+ */
+public class TypeParameterElementImpl extends ElementImpl implements TypeParameterElement
+{
+	private final Element _declaringElement;
+
+	// Cache the bounds, because they're expensive to compute
+	private List<? extends TypeMirror> _bounds = null;
+
+	/* package */ TypeParameterElementImpl(BaseProcessingEnvImpl env, TypeVariableBinding binding, Element declaringElement) {
+		super(env, binding);
+		_declaringElement = declaringElement;
+	}
+
+	/* package */ TypeParameterElementImpl(BaseProcessingEnvImpl env, TypeVariableBinding binding) {
+		super(env, binding);
+		_declaringElement = _env.getFactory().newElement(binding.declaringElement);
+	}
+
+	@Override
+	public List<? extends TypeMirror> getBounds()
+	{
+		if (null == _bounds) {
+			_bounds = calculateBounds();
+		}
+		return _bounds;
+	}
+
+	// This code is drawn from org.eclipse.jdt.core.dom.TypeBinding.getTypeBounds()
+	private List<? extends TypeMirror> calculateBounds() {
+		TypeVariableBinding typeVariableBinding = (TypeVariableBinding)_binding;
+		ReferenceBinding varSuperclass = typeVariableBinding.superclass();
+		TypeBinding firstClassOrArrayBound = typeVariableBinding.firstBound;
+		int boundsLength = 0;
+		boolean isFirstBoundATypeVariable = false;
+		if (firstClassOrArrayBound != null) {
+			if (firstClassOrArrayBound.isTypeVariable()) {
+				isFirstBoundATypeVariable = true;
+			}
+			if (TypeBinding.equalsEquals(firstClassOrArrayBound, varSuperclass)) {
+				boundsLength++;
+				if (firstClassOrArrayBound.isTypeVariable()) {
+					isFirstBoundATypeVariable = true;
+				}
+			} else if (firstClassOrArrayBound.isArrayType()) { // capture of ? extends/super arrayType
+				boundsLength++;
+			} else {
+				firstClassOrArrayBound = null;
+			}
+		}
+		ReferenceBinding[] superinterfaces = typeVariableBinding.superInterfaces();
+		int superinterfacesLength = 0;
+		if (superinterfaces != null) {
+			superinterfacesLength = superinterfaces.length;
+			boundsLength += superinterfacesLength;
+		}
+		List<TypeMirror> typeBounds = new ArrayList<>(boundsLength);
+		if (boundsLength != 0) {
+			if (firstClassOrArrayBound != null) {
+				TypeMirror typeBinding = _env.getFactory().newTypeMirror(firstClassOrArrayBound);
+				if (typeBinding == null) {
+					return Collections.emptyList();
+				}
+				typeBounds.add(typeBinding);
+			}
+			// we need to filter out remaining bounds if the first bound is a type variable
+			if (superinterfaces != null && !isFirstBoundATypeVariable) {
+				for (int i = 0; i < superinterfacesLength; i++) {
+					TypeMirror typeBinding = _env.getFactory().newTypeMirror(superinterfaces[i]);
+					if (typeBinding == null) {
+						return Collections.emptyList();
+					}
+					typeBounds.add(typeBinding);
+				}
+			}
+		} else {
+			// at least we must add java.lang.Object
+			typeBounds.add(_env.getFactory().newTypeMirror(_env.getLookupEnvironment().getType(LookupEnvironment.JAVA_LANG_OBJECT)));
+		}
+		return Collections.unmodifiableList(typeBounds);
+	}
+
+	@Override
+	public Element getGenericElement()
+	{
+		return _declaringElement;
+	}
+
+	@Override
+	public <R, P> R accept(ElementVisitor<R, P> v, P p)
+	{
+		return v.visitTypeParameter(this, p);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * Java supports annotations on type parameters from JLS8
+	 * @see javax.lang.model.element.Element#getAnnotationMirrors()
+	 */
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings()
+	{
+		return ((TypeVariableBinding)_binding).getTypeAnnotations();
+	}
+
+	private boolean shouldEmulateJavacBug() {
+		if (_env.getLookupEnvironment().globalOptions.emulateJavacBug8031744) {
+			AnnotationBinding [] annotations = getAnnotationBindings();
+			for (int i = 0, length = annotations.length; i < length; i++) {
+				ReferenceBinding firstAnnotationType = annotations[i].getAnnotationType();
+				for (int j = i+1; j < length; j++) {
+					ReferenceBinding secondAnnotationType = annotations[j].getAnnotationType();
+					if (firstAnnotationType == secondAnnotationType) //$IDENTITY-COMPARISON$
+						return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public List<? extends AnnotationMirror> getAnnotationMirrors() {
+		if (shouldEmulateJavacBug())
+			return Collections.emptyList();
+		return super.getAnnotationMirrors();
+	}
+
+	@Override
+	@SuppressWarnings("unchecked") // for the cast to A
+	public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+		if (shouldEmulateJavacBug())
+			return (A[]) Array.newInstance(annotationType, 0);
+		return super.getAnnotationsByType(annotationType);
+	}
+
+	@Override
+	public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+		if (shouldEmulateJavacBug())
+			return null;
+		return super.getAnnotation(annotationType);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * Always return an empty list; type parameters do not enclose other elements.
+	 * @see javax.lang.model.element.Element#getEnclosedElements()
+	 */
+	@Override
+	public List<? extends Element> getEnclosedElements()
+	{
+		return Collections.emptyList();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * Always return null.
+	 * @see javax.lang.model.element.Element#getEnclosingElement()
+	 */
+	@Override
+	public Element getEnclosingElement()
+	{
+		return getGenericElement();
+	}
+
+	@Override
+	public ElementKind getKind()
+	{
+		return ElementKind.TYPE_PARAMETER;
+	}
+
+	@Override
+	PackageElement getPackage()
+	{
+		// TODO what is the package of a type parameter?
+		return null;
+	}
+
+	@Override
+	public String toString() {
+		return new String(_binding.readableName());
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java
new file mode 100644
index 0000000..21ad258
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2013 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.TypeVisitor;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+/**
+ * Implementation of TypeVariable
+ */
+public class TypeVariableImpl extends TypeMirrorImpl implements TypeVariable {
+
+	TypeVariableImpl(BaseProcessingEnvImpl env, TypeVariableBinding binding) {
+		super(env, binding);
+	}
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeVariable#asElement()
+	 */
+	@Override
+	public Element asElement() {
+		return _env.getFactory().newElement(this._binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeVariable#getLowerBound()
+	 */
+	@Override
+	public TypeMirror getLowerBound() {
+		// TODO might be more complex than this
+		return this._env.getFactory().getNullType();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeVariable#getUpperBound()
+	 */
+	@Override
+	public TypeMirror getUpperBound() {
+		TypeVariableBinding typeVariableBinding = (TypeVariableBinding) this._binding;
+		TypeBinding firstBound = typeVariableBinding.firstBound;
+		ReferenceBinding[] superInterfaces = typeVariableBinding.superInterfaces;
+		if (firstBound == null || superInterfaces.length == 0) {
+			// no explicit bound
+			return _env.getFactory().newTypeMirror(typeVariableBinding.upperBound());
+		}
+		if (firstBound != null && superInterfaces.length == 1 && TypeBinding.equalsEquals(superInterfaces[0], firstBound)) {
+			// only one bound that is an interface
+			return _env.getFactory().newTypeMirror(typeVariableBinding.upperBound());
+		}
+		return this._env.getFactory().newTypeMirror((TypeVariableBinding) this._binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#accept(javax.lang.model.type.TypeVisitor, java.lang.Object)
+	 */
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+		return v.visitTypeVariable(this, p);
+	}
+
+	@Override
+	public TypeKind getKind() {
+		return TypeKind.TYPEVAR;
+	}
+
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypesImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypesImpl.java
new file mode 100644
index 0000000..370cacd
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypesImpl.java
@@ -0,0 +1,584 @@
+/*******************************************************************************
+ * Copyright (c) 2007 - 2017 BEA Systems, Inc. and others
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Walter Harley - initial API and implementation
+ *    IBM Corporation - fix for 342598, 382590
+ *    Jean-Marie Henaff <jmhenaff@google.com> (Google) - Bug 481555
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.Types;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
+
+/**
+ * Utilities for working with types (as opposed to elements).
+ * There is one of these for every ProcessingEnvironment.
+ */
+public class TypesImpl implements Types {
+
+    private final BaseProcessingEnvImpl _env;
+
+    /*
+     * The processing env creates and caches a TypesImpl.  Other clients should
+     * not create their own; they should ask the env for it.
+     */
+    public TypesImpl(BaseProcessingEnvImpl env) {
+        _env = env;
+    }
+
+    /* (non-Javadoc)
+     * @see javax.lang.model.util.Types#asElement(javax.lang.model.type.TypeMirror)
+     */
+    @Override
+    public Element asElement(TypeMirror t) {
+        switch(t.getKind()) {
+        case DECLARED :
+        case TYPEVAR :
+            return _env.getFactory().newElement(((TypeMirrorImpl)t).binding());
+        default:
+            break;
+        }
+        return null;
+    }
+
+    @Override
+	public TypeMirror asMemberOf(DeclaredType containing, Element element) {
+		// throw new UnsupportedOperationException("NYI: TypesImpl.asMemberOf("
+		// + containing + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+		// //$NON-NLS-3$
+		ElementImpl elementImpl = (ElementImpl) element;
+		DeclaredTypeImpl declaredTypeImpl = (DeclaredTypeImpl) containing;
+		ReferenceBinding referenceBinding = (ReferenceBinding) declaredTypeImpl._binding;
+		TypeMirror typeMirror;
+
+		switch (element.getKind()) {
+		case CONSTRUCTOR:
+		case METHOD:
+			typeMirror = findMemberInHierarchy(referenceBinding, elementImpl._binding, new MemberInTypeFinder() {
+				@Override
+				public TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding) {
+					MethodBinding methodBinding = ((MethodBinding) memberBinding);
+					for (MethodBinding method : typeBinding.methods()) {
+						if (CharOperation.equals(method.selector, methodBinding.selector)
+								&& (method.original() == methodBinding
+										|| method.areParameterErasuresEqual(methodBinding))) {
+							return TypesImpl.this._env.getFactory().newTypeMirror(method);
+						}
+					}
+					return null;
+				}
+			});
+
+			if (typeMirror != null) {
+				return typeMirror;
+			}
+			break;
+		case TYPE_PARAMETER:
+			typeMirror = findMemberInHierarchy(referenceBinding, elementImpl._binding, new MemberInTypeFinder() {
+				@Override
+				public TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding) {
+					if (typeBinding instanceof ParameterizedTypeBinding) {
+						TypeVariableBinding variableBinding = ((TypeVariableBinding) memberBinding);
+						ReferenceBinding binding = ((ParameterizedTypeBinding) typeBinding).genericType();
+						if (variableBinding.declaringElement == binding) { // check in advance avoid looking into type parameters unnecessarily.
+							TypeVariableBinding[] typeVariables = binding.typeVariables();
+							TypeBinding[] typeArguments = ((ParameterizedTypeBinding) typeBinding).typeArguments();
+							if (typeVariables.length == typeArguments.length) {
+								for(int i = 0; i < typeVariables.length; i++) {
+									if (typeVariables[i] == memberBinding) {
+										return TypesImpl.this._env.getFactory().newTypeMirror(typeArguments[i]);
+									}
+								}
+							}
+						}
+					}
+					return null;
+				}
+			});
+
+			if (typeMirror != null) {
+				return typeMirror;
+			}
+			break;
+		case FIELD:
+		case ENUM_CONSTANT:
+		case RECORD_COMPONENT:
+			typeMirror = findMemberInHierarchy(referenceBinding, elementImpl._binding, new MemberInTypeFinder() {
+				@Override
+				public TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding) {
+					VariableBinding variableBinding = (VariableBinding) memberBinding;
+					for (FieldBinding field : typeBinding.fields()) {
+						if (CharOperation.equals(field.name, variableBinding.name)) {
+							return TypesImpl.this._env.getFactory().newTypeMirror(field);
+						}
+					}
+					return null;
+				}
+			});
+
+			if (typeMirror != null) {
+				return typeMirror;
+			}
+			break;
+		case ENUM:
+		case ANNOTATION_TYPE:
+		case INTERFACE:
+		case CLASS:
+		case RECORD:
+			typeMirror = findMemberInHierarchy(referenceBinding, elementImpl._binding, new MemberInTypeFinder() {
+				@Override
+				public TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding) {
+					ReferenceBinding elementBinding = (ReferenceBinding) memberBinding;
+					// If referenceBinding is a ParameterizedTypeBinding, this
+					// will return only ParameterizedTypeBindings
+					// for member types, even if the member happens to be a
+					// static nested class. That's probably a bug;
+					// static nested classes are not parameterized by their
+					// outer class.
+					for (ReferenceBinding memberReferenceBinding : typeBinding.memberTypes()) {
+						if (CharOperation.equals(elementBinding.compoundName, memberReferenceBinding.compoundName)) {
+							return TypesImpl.this._env.getFactory().newTypeMirror(memberReferenceBinding);
+						}
+					}
+					return null;
+				}
+			});
+
+			if (typeMirror != null) {
+				return typeMirror;
+			}
+			break;
+		default:
+			throw new IllegalArgumentException("element " + element + //$NON-NLS-1$
+					" has unrecognized element kind " + element.getKind()); //$NON-NLS-1$
+		}
+		throw new IllegalArgumentException("element " + element + //$NON-NLS-1$
+				" is not a member of the containing type " + containing + //$NON-NLS-1$
+				" nor any of its superclasses"); //$NON-NLS-1$
+	}
+
+	private static interface MemberInTypeFinder {
+		TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding);
+	}
+
+	private TypeMirror findMemberInHierarchy(ReferenceBinding typeBinding, Binding memberBinding,
+			MemberInTypeFinder finder) {
+		TypeMirror result = null;
+
+		if (typeBinding == null) {
+			return null;
+		}
+
+		result = finder.find(typeBinding, memberBinding);
+		if (result != null) {
+			return result;
+		}
+
+		result = findMemberInHierarchy(typeBinding.superclass(), memberBinding, finder);
+		if (result != null) {
+			return result;
+		}
+
+		for (ReferenceBinding superInterface : typeBinding.superInterfaces()) {
+			result = findMemberInHierarchy(superInterface, memberBinding, finder);
+			if (result != null) {
+				return result;
+			}
+		}
+
+		return null;
+	}
+	private void validateRealType(TypeMirror t) {
+		switch (t.getKind()) {
+			case EXECUTABLE:
+			case PACKAGE:
+			case MODULE:
+				throw new IllegalArgumentException(
+						"Executable, package and module are illegal argument for Types.contains(..)"); //$NON-NLS-1$
+			default:
+				break;
+		}
+	}
+	private void validateRealTypes(TypeMirror t1, TypeMirror t2) {
+		validateRealType(t1);
+		validateRealType(t2);
+	}
+
+    @Override
+    public TypeElement boxedClass(PrimitiveType p) {
+        PrimitiveTypeImpl primitiveTypeImpl = (PrimitiveTypeImpl) p;
+        BaseTypeBinding baseTypeBinding = (BaseTypeBinding)primitiveTypeImpl._binding;
+        TypeBinding boxed = _env.getLookupEnvironment().computeBoxingType(baseTypeBinding);
+        return (TypeElement) _env.getFactory().newElement(boxed);
+    }
+
+    @Override
+    public TypeMirror capture(TypeMirror t) {
+    	validateRealType(t);
+    	TypeMirrorImpl typeMirrorImpl = (TypeMirrorImpl) t;
+    	if (typeMirrorImpl._binding instanceof ParameterizedTypeBinding) {
+    		throw new UnsupportedOperationException("NYI: TypesImpl.capture(...)"); //$NON-NLS-1$
+    	}
+        return t;
+    }
+
+    @Override
+    public boolean contains(TypeMirror t1, TypeMirror t2) {
+    	validateRealTypes(t1, t2);
+        throw new UnsupportedOperationException("NYI: TypesImpl.contains(" + t1 + ", " + t2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+
+    @Override
+    public List<? extends TypeMirror> directSupertypes(TypeMirror t) {
+    	validateRealType(t);
+        TypeMirrorImpl typeMirrorImpl = (TypeMirrorImpl) t;
+        Binding binding = typeMirrorImpl._binding;
+        if (binding instanceof ReferenceBinding) {
+            ReferenceBinding referenceBinding = (ReferenceBinding) binding;
+            ArrayList<TypeMirror> list = new ArrayList<>();
+            ReferenceBinding superclass = referenceBinding.superclass();
+            if (superclass != null) {
+                list.add(this._env.getFactory().newTypeMirror(superclass));
+            }
+            for (ReferenceBinding interfaceBinding : referenceBinding.superInterfaces()) {
+                list.add(this._env.getFactory().newTypeMirror(interfaceBinding));
+            }
+            return Collections.unmodifiableList(list);
+        }
+        return Collections.emptyList();
+    }
+
+    @Override
+    public TypeMirror erasure(TypeMirror t) {
+    	validateRealType(t);
+        TypeMirrorImpl typeMirrorImpl = (TypeMirrorImpl) t;
+        Binding binding = typeMirrorImpl._binding;
+        if (binding instanceof ReferenceBinding) {
+        	TypeBinding type = ((ReferenceBinding) binding).erasure();
+        	if (type.isGenericType()) {
+        		type = _env.getLookupEnvironment().convertToRawType(type, false);
+        	}
+            return _env.getFactory().newTypeMirror(type);
+        }
+        if (binding instanceof ArrayBinding) {
+            TypeBinding typeBinding = (TypeBinding) binding;
+            TypeBinding leafType = typeBinding.leafComponentType().erasure();
+            if (leafType.isGenericType()) {
+            	leafType = _env.getLookupEnvironment().convertToRawType(leafType, false);
+            }
+            return _env.getFactory().newTypeMirror(
+                    this._env.getLookupEnvironment().createArrayType(leafType,
+                            typeBinding.dimensions()));
+        }
+        return t;
+    }
+
+    @Override
+    public ArrayType getArrayType(TypeMirror componentType) {
+        TypeMirrorImpl typeMirrorImpl = (TypeMirrorImpl) componentType;
+        TypeBinding typeBinding = (TypeBinding) typeMirrorImpl._binding;
+        return (ArrayType) _env.getFactory().newTypeMirror(
+                this._env.getLookupEnvironment().createArrayType(
+                        typeBinding.leafComponentType(),
+                        typeBinding.dimensions() + 1));
+    }
+
+    /*
+     * (non-Javadoc)
+     * Create a type instance by parameterizing a type element. If the element is a member type,
+     * its container won't be parameterized (if it needs to be, you would need to use the form of
+     * getDeclaredType that takes a container TypeMirror). If typeArgs is empty, and typeElem
+     * is not generic, then you should use TypeElem.asType().  If typeArgs is empty and typeElem
+     * is generic, this method will create the raw type.
+     */
+    @Override
+    public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
+        int typeArgsLength = typeArgs.length;
+        TypeElementImpl typeElementImpl = (TypeElementImpl) typeElem;
+        ReferenceBinding elementBinding = (ReferenceBinding) typeElementImpl._binding;
+        TypeVariableBinding[] typeVariables = elementBinding.typeVariables();
+        int typeVariablesLength = typeVariables.length;
+        if (typeArgsLength == 0) {
+            if (elementBinding.isGenericType()) {
+                // per javadoc,
+                return (DeclaredType) _env.getFactory().newTypeMirror(this._env.getLookupEnvironment().createRawType(elementBinding, null));
+            }
+            return (DeclaredType)typeElem.asType();
+        } else if (typeArgsLength != typeVariablesLength) {
+            throw new IllegalArgumentException("Number of typeArguments doesn't match the number of formal parameters of typeElem"); //$NON-NLS-1$
+        }
+        TypeBinding[] typeArguments = new TypeBinding[typeArgsLength];
+        for (int i = 0; i < typeArgsLength; i++) {
+            TypeMirrorImpl typeMirrorImpl = (TypeMirrorImpl) typeArgs[i];
+            Binding binding = typeMirrorImpl._binding;
+            if (!(binding instanceof TypeBinding)) {
+                throw new IllegalArgumentException("Invalid type argument: " + typeMirrorImpl); //$NON-NLS-1$
+            }
+            typeArguments[i] = (TypeBinding) binding;
+        }
+
+        ReferenceBinding enclosing = elementBinding.enclosingType();
+        if (enclosing != null) {
+            enclosing = this._env.getLookupEnvironment().createRawType(enclosing, null);
+        }
+
+        return (DeclaredType) _env.getFactory().newTypeMirror(
+                this._env.getLookupEnvironment().createParameterizedType(elementBinding, typeArguments, enclosing));
+    }
+
+    /* (non-Javadoc)
+     * Create a specific type from a member element. The containing type can be parameterized,
+     * e.g. Outer<String>.Inner, but it cannot be generic, i.e., Outer<T>.Inner. It only makes
+     * sense to use this method when the member element is parameterized by its container; so,
+     * for example, it makes sense for an inner class but not for a static member class.
+     * Otherwise you should just use getDeclaredType(TypeElement, TypeMirror ...), if you need
+     * to specify type arguments, or TypeElement.asType() directly, if not.
+     */
+    @Override
+    public DeclaredType getDeclaredType(DeclaredType containing, TypeElement typeElem,
+            TypeMirror... typeArgs) {
+        int typeArgsLength = typeArgs.length;
+        TypeElementImpl typeElementImpl = (TypeElementImpl) typeElem;
+        ReferenceBinding elementBinding = (ReferenceBinding) typeElementImpl._binding;
+        TypeVariableBinding[] typeVariables = elementBinding.typeVariables();
+        int typeVariablesLength = typeVariables.length;
+        DeclaredTypeImpl declaredTypeImpl = (DeclaredTypeImpl) containing;
+        ReferenceBinding enclosingType = (ReferenceBinding) declaredTypeImpl._binding;
+        if (typeArgsLength == 0) {
+            if (elementBinding.isGenericType()) {
+                // e.g., Outer.Inner<T> but T is not specified
+                // Per javadoc on interface, must return the raw type Outer.Inner
+                return (DeclaredType) _env.getFactory().newTypeMirror(
+                        _env.getLookupEnvironment().createRawType(elementBinding, enclosingType));
+            } else {
+                // e.g., Outer<Long>.Inner
+                ParameterizedTypeBinding ptb = _env.getLookupEnvironment().createParameterizedType(elementBinding, null, enclosingType);
+                return (DeclaredType) _env.getFactory().newTypeMirror(ptb);
+            }
+        } else if (typeArgsLength != typeVariablesLength) {
+            throw new IllegalArgumentException("Number of typeArguments doesn't match the number of formal parameters of typeElem"); //$NON-NLS-1$
+        }
+        TypeBinding[] typeArguments = new TypeBinding[typeArgsLength];
+        for (int i = 0; i < typeArgsLength; i++) {
+            TypeMirrorImpl typeMirrorImpl = (TypeMirrorImpl) typeArgs[i];
+            Binding binding = typeMirrorImpl._binding;
+            if (!(binding instanceof TypeBinding)) {
+                throw new IllegalArgumentException("Invalid type for a type arguments : " + typeMirrorImpl); //$NON-NLS-1$
+            }
+            typeArguments[i] = (TypeBinding) binding;
+        }
+        return (DeclaredType) _env.getFactory().newTypeMirror(
+                this._env.getLookupEnvironment().createParameterizedType(elementBinding, typeArguments, enclosingType));
+    }
+
+    @Override
+    public NoType getNoType(TypeKind kind) {
+        return _env.getFactory().getNoType(kind);
+    }
+
+    @Override
+    public NullType getNullType() {
+        return _env.getFactory().getNullType();
+    }
+
+    @Override
+    public PrimitiveType getPrimitiveType(TypeKind kind) {
+        return _env.getFactory().getPrimitiveType(kind);
+    }
+
+    @Override
+    public WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound) {
+        if (extendsBound != null && superBound != null) {
+            throw new IllegalArgumentException("Extends and super bounds cannot be set at the same time"); //$NON-NLS-1$
+        }
+        if (extendsBound != null) {
+            TypeMirrorImpl extendsBoundMirrorType = (TypeMirrorImpl) extendsBound;
+            TypeBinding typeBinding = (TypeBinding) extendsBoundMirrorType._binding;
+            return (WildcardType) _env.getFactory().newTypeMirror(
+                    this._env.getLookupEnvironment().createWildcard(
+                            null,
+                            0,
+                            typeBinding,
+                            null,
+                            Wildcard.EXTENDS));
+        }
+        if (superBound != null) {
+            TypeMirrorImpl superBoundMirrorType = (TypeMirrorImpl) superBound;
+            TypeBinding typeBinding = (TypeBinding) superBoundMirrorType._binding;
+            return new WildcardTypeImpl(_env, this._env.getLookupEnvironment().createWildcard(
+                    null,
+                    0,
+                    typeBinding,
+                    null,
+                    Wildcard.SUPER));
+        }
+        return new WildcardTypeImpl(_env, this._env.getLookupEnvironment().createWildcard(
+                null,
+                0,
+                null,
+                null,
+                Wildcard.UNBOUND));
+    }
+
+    /* (non-Javadoc)
+     * @return true if a value of type t1 can be assigned to a variable of type t2, i.e., t2 = t1.
+     */
+    @Override
+    public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
+    	validateRealTypes(t1, t2);
+        if (!(t1 instanceof TypeMirrorImpl) || !(t2 instanceof TypeMirrorImpl)) {
+        	return false;
+        }
+        Binding b1 = ((TypeMirrorImpl)t1).binding();
+        Binding b2 = ((TypeMirrorImpl)t2).binding();
+        if (!(b1 instanceof TypeBinding) || !(b2 instanceof TypeBinding)) {
+            // package, method, import, etc.
+            throw new IllegalArgumentException();
+        }
+        if (((TypeBinding)b1).isCompatibleWith((TypeBinding)b2)) {
+            return true;
+        }
+
+        TypeBinding convertedType = _env.getLookupEnvironment().computeBoxingType((TypeBinding)b1);
+        return null != convertedType && convertedType.isCompatibleWith((TypeBinding)b2);
+    }
+
+    @Override
+    public boolean isSameType(TypeMirror t1, TypeMirror t2) {
+        if (t1 instanceof NoTypeImpl) {
+            if (t2 instanceof NoTypeImpl) {
+                return ((NoTypeImpl) t1).getKind() == ((NoTypeImpl) t2).getKind();
+            }
+            return false;
+        } else if (t2 instanceof NoTypeImpl) {
+            return false;
+        }
+        if (t1.getKind() == TypeKind.WILDCARD || t2.getKind() == TypeKind.WILDCARD) {
+            // Wildcard types are never equal, according to the spec of this method
+            return false;
+        }
+        if (t1 == t2) {
+            return true;
+        }
+        if (!(t1 instanceof TypeMirrorImpl) || !(t2 instanceof TypeMirrorImpl)) {
+            return false;
+        }
+        Binding b1 = ((TypeMirrorImpl)t1).binding();
+        Binding b2 = ((TypeMirrorImpl)t2).binding();
+
+        if (b1 == b2) {
+            return true;
+        }
+        if (!(b1 instanceof TypeBinding) || !(b2 instanceof TypeBinding)) {
+            return false;
+        }
+        TypeBinding type1 = ((TypeBinding) b1);
+        TypeBinding type2 = ((TypeBinding) b2);
+        if (TypeBinding.equalsEquals(type1,  type2))
+        	return true;
+        return CharOperation.equals(type1.computeUniqueKey(), type2.computeUniqueKey());
+    }
+
+    @Override
+    public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
+        MethodBinding methodBinding1 = (MethodBinding) ((ExecutableTypeImpl) m1)._binding;
+        MethodBinding methodBinding2 = (MethodBinding) ((ExecutableTypeImpl) m2)._binding;
+        if (!CharOperation.equals(methodBinding1.selector, methodBinding2.selector))
+            return false;
+        return methodBinding1.areParameterErasuresEqual(methodBinding2) && methodBinding1.areTypeVariableErasuresEqual(methodBinding2);
+    }
+
+    /* (non-Javadoc)
+     * @return true if t1 is a subtype of t2, or if t1 == t2.
+     */
+    @Override
+    public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
+    	validateRealTypes(t1, t2);
+        if (t1 instanceof NoTypeImpl) {
+            if (t2 instanceof NoTypeImpl) {
+                return ((NoTypeImpl) t1).getKind() == ((NoTypeImpl) t2).getKind();
+            }
+            return false;
+        } else if (t2 instanceof NoTypeImpl) {
+            return false;
+        }
+        if (!(t1 instanceof TypeMirrorImpl) || !(t2 instanceof TypeMirrorImpl)) {
+        	throw new IllegalArgumentException();
+        }
+        if (t1 == t2) {
+            return true;
+        }
+        Binding b1 = ((TypeMirrorImpl)t1).binding();
+        Binding b2 = ((TypeMirrorImpl)t2).binding();
+        if (b1 == b2) {
+            return true;
+        }
+        if (!(b1 instanceof TypeBinding) || !(b2 instanceof TypeBinding)) {
+            // package, method, import, etc.
+        	 throw new IllegalArgumentException();
+        }
+        if (b1.kind() == Binding.BASE_TYPE || b2.kind() == Binding.BASE_TYPE) {
+            if (b1.kind() != b2.kind()) {
+                return false;
+            }
+            else {
+                // for primitives, compatibility implies subtype
+                return ((TypeBinding)b1).isCompatibleWith((TypeBinding)b2);
+            }
+        }
+        return ((TypeBinding)b1).isCompatibleWith((TypeBinding)b2);
+    }
+
+    @Override
+    public PrimitiveType unboxedType(TypeMirror t) {
+        if (!(((TypeMirrorImpl)t)._binding instanceof ReferenceBinding)) {
+            // Not an unboxable type - could be primitive, array, not a type at all, etc.
+            throw new IllegalArgumentException("Given type mirror cannot be unboxed"); //$NON-NLS-1$
+        }
+        ReferenceBinding boxed = (ReferenceBinding)((TypeMirrorImpl)t)._binding;
+        TypeBinding unboxed = _env.getLookupEnvironment().computeBoxingType(boxed);
+        if (unboxed.kind() != Binding.BASE_TYPE) {
+            // No boxing conversion was found
+            throw new IllegalArgumentException();
+        }
+        return (PrimitiveType) _env.getFactory().newTypeMirror((BaseTypeBinding)unboxed);
+    }
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/VariableElementImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/VariableElementImpl.java
new file mode 100644
index 0000000..9a4c8c2
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/VariableElementImpl.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2015 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.VariableElement;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+import org.eclipse.jdt.internal.compiler.lookup.AptBinaryLocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.AptSourceLocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
+import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
+
+/**
+ * Implementation of VariableElement, which represents a a field, enum constant,
+ * method or constructor parameter, local variable, or exception parameter.
+ */
+public class VariableElementImpl extends ElementImpl implements VariableElement {
+
+	/**
+	 * @param binding might be a FieldBinding (for a field) or a LocalVariableBinding (for a method param)
+	 */
+	VariableElementImpl(BaseProcessingEnvImpl env, VariableBinding binding) {
+		super(env, binding);
+	}
+
+	@Override
+	public <R, P> R accept(ElementVisitor<R, P> v, P p)
+	{
+		return v.visitVariable(this, p);
+	}
+
+	@Override
+	protected AnnotationBinding[] getAnnotationBindings()
+	{
+		return ((VariableBinding)_binding).getAnnotations();
+	}
+
+	@Override
+	public Object getConstantValue() {
+		VariableBinding variableBinding = (VariableBinding) _binding;
+		Constant constant = variableBinding.constant();
+		if (constant == null || constant == Constant.NotAConstant) return null;
+		TypeBinding type = variableBinding.type;
+		switch (type.id) {
+			case TypeIds.T_boolean:
+				return constant.booleanValue();
+			case TypeIds.T_byte:
+				return constant.byteValue();
+			case TypeIds.T_char:
+				return constant.charValue();
+			case TypeIds.T_double:
+				return constant.doubleValue();
+			case TypeIds.T_float:
+				return constant.floatValue();
+			case TypeIds.T_int:
+				return constant.intValue();
+			case TypeIds.T_JavaLangString:
+				return constant.stringValue();
+			case TypeIds.T_long:
+				return constant.longValue();
+			case TypeIds.T_short:
+				return constant.shortValue();
+		}
+		return null;
+	}
+
+	@Override
+	public List<? extends Element> getEnclosedElements() {
+		return Collections.emptyList();
+	}
+
+	@Override
+	public Element getEnclosingElement() {
+		if (_binding instanceof FieldBinding) {
+			return _env.getFactory().newElement(((FieldBinding)_binding).declaringClass);
+		}
+		else if (_binding instanceof AptSourceLocalVariableBinding){
+			return _env.getFactory().newElement(((AptSourceLocalVariableBinding) _binding).methodBinding);
+		} else if (_binding instanceof AptBinaryLocalVariableBinding) {
+			return _env.getFactory().newElement(((AptBinaryLocalVariableBinding) _binding).methodBinding);
+		} else if (_binding instanceof RecordComponentBinding) {
+			return _env.getFactory().newElement(((RecordComponentBinding)_binding).declaringRecord);
+		}
+		return null;
+	}
+
+	@Override
+	public ElementKind getKind() {
+		if (_binding instanceof FieldBinding) {
+			if ((((FieldBinding) _binding).modifiers & ClassFileConstants.AccEnum) != 0) {
+				return ElementKind.ENUM_CONSTANT;
+			}
+			else {
+				return ElementKind.FIELD;
+			}
+		}
+		else {
+			return ElementKind.PARAMETER;
+		}
+	}
+
+	@Override
+	public Set<Modifier> getModifiers()
+	{
+		if (_binding instanceof VariableBinding) {
+			return Factory.getModifiers(((VariableBinding)_binding).modifiers, getKind());
+		}
+		return Collections.emptySet();
+	}
+
+	@Override
+	PackageElement getPackage()
+	{
+		if (_binding instanceof FieldBinding) {
+			PackageBinding pkgBinding = ((FieldBinding)_binding).declaringClass.fPackage;
+			return _env.getFactory().newPackageElement(pkgBinding);
+		}
+		else {
+			// TODO: what is the package of a method parameter?
+			throw new UnsupportedOperationException("NYI: VariableElmentImpl.getPackage() for method parameter"); //$NON-NLS-1$
+		}
+	}
+
+	@Override
+	public Name getSimpleName() {
+		return new NameImpl(((VariableBinding)_binding).name);
+	}
+
+	@Override
+	public boolean hides(Element hiddenElement)
+	{
+		if (_binding instanceof FieldBinding) {
+			if (!(((ElementImpl)hiddenElement)._binding instanceof FieldBinding)) {
+				return false;
+			}
+			FieldBinding hidden = (FieldBinding)((ElementImpl)hiddenElement)._binding;
+			if (hidden.isPrivate()) {
+				return false;
+			}
+			FieldBinding hider = (FieldBinding)_binding;
+			if (hidden == hider) {
+				return false;
+			}
+			if (!CharOperation.equals(hider.name, hidden.name)) {
+				return false;
+			}
+			return null != hider.declaringClass.findSuperTypeOriginatingFrom(hidden.declaringClass);
+		}
+		// TODO: should we implement hides() for method parameters?
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		return new String(((VariableBinding) _binding).name);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final VariableElementImpl other = (VariableElementImpl) obj;
+		return Objects.equals(this._binding, other._binding);
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/WildcardTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/WildcardTypeImpl.java
new file mode 100644
index 0000000..9c97895
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/WildcardTypeImpl.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.type.WildcardType;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
+
+/**
+ * Implementation of the WildcardType
+ */
+public class WildcardTypeImpl extends TypeMirrorImpl implements WildcardType {
+
+	WildcardTypeImpl(BaseProcessingEnvImpl env, WildcardBinding binding) {
+		super(env, binding);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.WildcardType#getExtendsBound()
+	 */
+	@Override
+	public TypeMirror getExtendsBound() {
+		WildcardBinding wildcardBinding = (WildcardBinding) this._binding;
+		if (wildcardBinding.boundKind != Wildcard.EXTENDS) return null;
+		TypeBinding bound = wildcardBinding.bound;
+		if (bound == null) return null;
+		return _env.getFactory().newTypeMirror(bound);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.TypeMirror#getKind()
+	 */
+	@Override
+	public TypeKind getKind() {
+		return TypeKind.WILDCARD;
+	}
+	/* (non-Javadoc)
+	 * @see javax.lang.model.type.WildcardType#getSuperBound()
+	 */
+	@Override
+	public TypeMirror getSuperBound() {
+		WildcardBinding wildcardBinding = (WildcardBinding) this._binding;
+		if (wildcardBinding.boundKind != Wildcard.SUPER) return null;
+		TypeBinding bound = wildcardBinding.bound;
+		if (bound == null) return null;
+		return _env.getFactory().newTypeMirror(bound);
+	}
+
+	@Override
+	public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+		return v.visitWildcard(this, p);
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/Archive.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/Archive.java
new file mode 100644
index 0000000..049964c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/Archive.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+import org.eclipse.jdt.internal.compiler.util.JRTUtil;
+
+/**
+ * Used as a zip file cache.
+ */
+public class Archive implements Closeable {
+
+	public static final Archive UNKNOWN_ARCHIVE = new Archive();
+
+	ZipFile zipFile;
+	File file;
+
+	protected Hashtable<String, ArrayList<String[]>> packagesCache;
+
+	protected Archive() {
+		// used to construct UNKNOWN_ARCHIVE
+	}
+
+	public Archive(File file) throws ZipException, IOException {
+		this.file = file;
+		this.zipFile = new ZipFile(file);
+		initialize();
+	}
+
+	private void initialize() {
+		// initialize packages
+		this.packagesCache = new Hashtable<>();
+		nextEntry : for (Enumeration<? extends ZipEntry> e = this.zipFile.entries(); e.hasMoreElements(); ) {
+			String fileName = ((ZipEntry) e.nextElement()).getName();
+
+			// add the package name & all of its parent packages
+			int last = fileName.lastIndexOf('/');
+			// extract the package name
+			String packageName = fileName.substring(0, last + 1);
+			String typeName = fileName.substring(last + 1);
+			ArrayList<String[]> types = this.packagesCache.get(packageName);
+			if (types == null) {
+				// might be empty if this is a directory entry
+				if (typeName.length() == 0) {
+					continue nextEntry;
+				}
+				types = new ArrayList<>();
+				types.add(new String[]{typeName, null});
+				this.packagesCache.put(packageName, types);
+			} else {
+				types.add(new String[]{typeName, null});
+			}
+		}
+	}
+
+	public ArchiveFileObject getArchiveFileObject(String fileName, String module, Charset charset) {
+		return new ArchiveFileObject(this.file, fileName, charset);
+	}
+
+	public boolean contains(String entryName) {
+		return this.zipFile.getEntry(entryName) != null;
+	}
+
+	public Set<String> allPackages() {
+		if (this.packagesCache == null) {
+			this.initialize();
+		}
+		return this.packagesCache.keySet();
+	}
+
+	/**
+	 * Returns an array of String - the array contains exactly two elements. The first element
+	 * is the name of the type and the second being the module that contains the type. For a regular
+	 * Jar archive, the module element will be null. This is applicable only to Jimage files
+	 * where types are contained by multiple modules.
+	 */
+	public List<String[]> getTypes(String packageName) {
+		// package name is expected to ends with '/'
+		if (this.packagesCache == null) {
+			try {
+				this.zipFile = new ZipFile(this.file);
+			} catch(IOException e) {
+				String error = "Failed to read types from archive " + this.file; //$NON-NLS-1$
+				if (JRTUtil.PROPAGATE_IO_ERRORS) {
+					throw new IllegalStateException(error, e);
+				} else {
+					System.err.println(error);
+					e.printStackTrace();
+				}
+				return Collections.<String[]>emptyList();
+			}
+			this.initialize();
+		}
+		return this.packagesCache.get(packageName);
+	}
+
+	public void flush() {
+		this.packagesCache = null;
+	}
+
+	@Override
+	public void close() {
+		this.packagesCache = null;
+		try {
+			if (this.zipFile != null) {
+				this.zipFile.close();
+			}
+		} catch (IOException e) {
+			// ignore
+		}
+	}
+
+	@Override
+	public String toString() {
+		return "Archive: " + (this.file == null ? "UNKNOWN_ARCHIVE" : this.file.getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ArchiveFileObject.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ArchiveFileObject.java
new file mode 100644
index 0000000..c301c9d
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ArchiveFileObject.java
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2021 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.JavaFileObject;
+
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.util.JRTUtil;
+
+/**
+ * Implementation of a Java file object that corresponds to an entry in a zip/jar file
+ */
+public class ArchiveFileObject implements JavaFileObject {
+	protected String entryName;
+	protected File file;
+	protected Charset charset;
+
+	public ArchiveFileObject(File file, String entryName, Charset charset) {
+		this.entryName = entryName;
+		this.file = file;
+		this.charset = charset;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileObject#getAccessLevel()
+	 */
+	@Override
+	public Modifier getAccessLevel() {
+		// cannot express multiple modifier
+		if (getKind() != Kind.CLASS) {
+			return null;
+		}
+		ClassFileReader reader = getClassReader();
+
+		if (reader == null) {
+			return null;
+		}
+		final int accessFlags = reader.accessFlags();
+		if ((accessFlags & ClassFileConstants.AccPublic) != 0) {
+			return Modifier.PUBLIC;
+		}
+		if ((accessFlags & ClassFileConstants.AccAbstract) != 0) {
+			return Modifier.ABSTRACT;
+		}
+		if ((accessFlags & ClassFileConstants.AccFinal) != 0) {
+			return Modifier.FINAL;
+		}
+		return null;
+	}
+
+	protected ClassFileReader getClassReader() {
+		ClassFileReader reader = null;
+		try {
+			try (ZipFile zip = new ZipFile(this.file)) {
+				reader = ClassFileReader.read(zip, this.entryName);
+			}
+		} catch (ClassFormatException e) {
+			// ignore
+		} catch (IOException e) {
+			String error = "Failed to read entry " + this.entryName + " from archive " + this.file; //$NON-NLS-1$ //$NON-NLS-2$
+			if (JRTUtil.PROPAGATE_IO_ERRORS) {
+				throw new IllegalStateException(error, e);
+			} else {
+				System.err.println(error);
+				e.printStackTrace();
+			}
+		}
+		return reader;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileObject#getKind()
+	 */
+	@Override
+	public Kind getKind() {
+		String name = this.entryName.toLowerCase();
+		if (name.endsWith(Kind.CLASS.extension)) {
+			return Kind.CLASS;
+		} else if (name.endsWith(Kind.SOURCE.extension)) {
+			return Kind.SOURCE;
+		} else if (name.endsWith(Kind.HTML.extension)) {
+			return Kind.HTML;
+		}
+		return Kind.OTHER;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileObject#getNestingKind()
+	 */
+	@Override
+	public NestingKind getNestingKind() {
+		switch(getKind()) {
+		case SOURCE :
+			return NestingKind.TOP_LEVEL;
+		case CLASS :
+			ClassFileReader reader = getClassReader();
+			if (reader == null) {
+				return null;
+			}
+			if (reader.isAnonymous()) {
+				return NestingKind.ANONYMOUS;
+			}
+			if (reader.isLocal()) {
+				return NestingKind.LOCAL;
+			}
+			if (reader.isMember()) {
+				return NestingKind.MEMBER;
+			}
+			return NestingKind.TOP_LEVEL;
+		default:
+			return null;
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileObject#isNameCompatible(java.lang.String, javax.tools.JavaFileObject.Kind)
+	 */
+	@Override
+	public boolean isNameCompatible(String simpleName, Kind kind) {
+		return this.entryName.endsWith(simpleName + kind.extension);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#delete()
+	 */
+	@Override
+	public boolean delete() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (!(o instanceof ArchiveFileObject)) {
+			return false;
+		}
+		ArchiveFileObject archiveFileObject = (ArchiveFileObject) o;
+		return archiveFileObject.toUri().equals(this.toUri());
+	}
+
+	@Override
+	public int hashCode() {
+		return this.toUri().hashCode();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#getCharContent(boolean)
+	 */
+	@Override
+	public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+		if (getKind() == Kind.SOURCE) {
+			try (ZipFile zipFile2 = new ZipFile(this.file)) {
+				ZipEntry zipEntry = zipFile2.getEntry(this.entryName);
+				return Util.getCharContents(this, ignoreEncodingErrors, org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(zipEntry, zipFile2), this.charset.name());
+			}
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#getLastModified()
+	 */
+	@Override
+	public long getLastModified() {
+		try (ZipFile zip = new ZipFile(this.file)) {
+			ZipEntry zipEntry = zip.getEntry(this.entryName);
+			return zipEntry.getTime(); // looks the closest from the last modification
+		} catch(IOException e) {
+			// ignore
+		}
+		return 0;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#getName()
+	 */
+	@Override
+	public String getName() {
+		return this.entryName;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#openInputStream()
+	 */
+	@Override
+	public InputStream openInputStream() throws IOException {
+		try (ZipFile zipFile = new ZipFile(this.file);
+				InputStream inputStream = zipFile.getInputStream(zipFile.getEntry(this.entryName));) {
+			ByteArrayInputStream buffer = new ByteArrayInputStream(inputStream.readAllBytes());
+			return buffer;
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#openOutputStream()
+	 */
+	@Override
+	public OutputStream openOutputStream() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#openReader(boolean)
+	 */
+	@Override
+	public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#openWriter()
+	 */
+	@Override
+	public Writer openWriter() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.FileObject#toUri()
+	 */
+	@Override
+	public URI toUri() {
+		try {
+			return new URI("jar:" + this.file.toURI().getPath() + "!" + this.entryName); //$NON-NLS-1$//$NON-NLS-2$
+		} catch (URISyntaxException e) {
+			return null;
+		}
+	}
+
+
+	@Override
+	public String toString() {
+		return this.file.getAbsolutePath() + "[" + this.entryName + "]";//$NON-NLS-1$//$NON-NLS-2$
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java
new file mode 100644
index 0000000..b63a09f
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java
@@ -0,0 +1,1615 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2022 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.lang.model.SourceVersion;
+import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
+import org.eclipse.jdt.internal.compiler.apt.util.JrtFileSystem.JrtFileObject;
+import org.eclipse.jdt.internal.compiler.apt.util.ModuleLocationHandler.LocationContainer;
+import org.eclipse.jdt.internal.compiler.apt.util.ModuleLocationHandler.LocationWrapper;
+import org.eclipse.jdt.internal.compiler.apt.util.ModuleLocationHandler.ModuleLocationWrapper;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
+import org.eclipse.jdt.internal.compiler.batch.Main;
+import org.eclipse.jdt.internal.compiler.batch.Main.ResourceBundleFactory;
+import org.eclipse.jdt.internal.compiler.batch.ModuleFinder;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
+import org.eclipse.jdt.internal.compiler.env.AccessRule;
+import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
+import org.eclipse.jdt.internal.compiler.util.JRTUtil;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+/**
+ * Implementation of the Standard Java File Manager
+ */
+public class EclipseFileManager implements StandardJavaFileManager {
+	private static final String NO_EXTENSION = "";//$NON-NLS-1$
+	static final int HAS_EXT_DIRS = 1;
+	static final int HAS_BOOTCLASSPATH = 2;
+	static final int HAS_ENDORSED_DIRS = 4;
+	static final int HAS_PROCESSORPATH = 8;
+	static final int HAS_PROC_MODULEPATH = 16;
+
+	Map<File, Archive> archivesCache;
+	Charset charset;
+	Locale locale;
+	ModuleLocationHandler locationHandler;
+	final Map<Location, URLClassLoader> classloaders;
+	int flags;
+	boolean isOnJvm9;
+	File jrtHome;
+	JrtFileSystem jrtSystem;
+	public ResourceBundle bundle;
+	String releaseVersion;
+
+	public EclipseFileManager(Locale locale, Charset charset) {
+		this.locale = locale == null ? Locale.getDefault() : locale;
+		this.charset = charset == null ? Charset.defaultCharset() : charset;
+		this.locationHandler = new ModuleLocationHandler();
+		this.classloaders = new HashMap<>();
+		this.archivesCache = new HashMap<>();
+		this.isOnJvm9 = isRunningJvm9();
+		try {
+			initialize(Util.getJavaHome());
+		} catch (IOException e) {
+			String error = "Failed to init EclipseFileManager from " + Util.getJavaHome(); //$NON-NLS-1$
+			if (JRTUtil.PROPAGATE_IO_ERRORS) {
+				throw new IllegalStateException(error, e);
+			} else {
+				System.err.println(error);
+				e.printStackTrace();
+			}
+		}
+		try {
+			this.bundle = ResourceBundleFactory.getBundle(this.locale);
+		} catch(MissingResourceException e) {
+			System.out.println("Missing resource : " + Main.bundleName.replace('.', '/') + ".properties for locale " + locale); //$NON-NLS-1$//$NON-NLS-2$
+		}
+	}
+	protected void initialize(File javahome) throws IOException {
+		if (this.isOnJvm9) {
+			this.jrtSystem = new JrtFileSystem(javahome);
+			try (Archive previous = this.archivesCache.put(javahome, this.jrtSystem)) {
+				// nothing. Only theoretically autoclose the previous instance - which does not exist at this time
+			}
+			this.jrtHome = javahome;
+			this.locationHandler.newSystemLocation(StandardLocation.SYSTEM_MODULES, this.jrtSystem);
+		} else {
+			this.setLocation(StandardLocation.PLATFORM_CLASS_PATH, getDefaultBootclasspath());
+		}
+		Iterable<? extends File> defaultClasspath = getDefaultClasspath();
+		this.setLocation(StandardLocation.CLASS_PATH, defaultClasspath);
+		// No annotation module path by default
+		this.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, defaultClasspath);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#close()
+	 */
+	@Override
+	public void close() throws IOException {
+		this.locationHandler.close();
+		for (Archive archive : this.archivesCache.values()) {
+			if (archive != null) {
+				archive.close();
+			}
+		}
+		this.archivesCache.clear();
+		for (URLClassLoader cl : this.classloaders.values()) {
+			cl.close();
+		}
+		this.classloaders.clear();
+	}
+
+	private void collectAllMatchingFiles(Location location, File file, String normalizedPackageName, Set<Kind> kinds, boolean recurse, ArrayList<JavaFileObject> collector) {
+		if (file.equals(this.jrtHome)) {
+			if (location instanceof ModuleLocationWrapper) {
+				List<JrtFileObject> list = this.jrtSystem.list((ModuleLocationWrapper) location, normalizedPackageName, kinds, recurse, this.charset);
+				for (JrtFileObject fo : list) {
+					Kind kind = getKind(getExtension(fo.entryName));
+					if (kinds.contains(kind)) {
+						collector.add(fo);
+					}
+				}
+			}
+		} else if (isArchive(file)) {
+			@SuppressWarnings("resource") // cached archive is closed in EclipseFileManager.close()
+			Archive archive = this.getArchive(file);
+			if (archive != Archive.UNKNOWN_ARCHIVE) {
+				String key = normalizedPackageName;
+				if (!normalizedPackageName.endsWith("/")) {//$NON-NLS-1$
+					key += '/';
+				}
+				// we have an archive file
+				if (recurse) {
+					for (String packageName : archive.allPackages()) {
+						if (packageName.startsWith(key)) {
+							List<String[]> types = archive.getTypes(packageName);
+							if (types != null) {
+								for (String[] entry : types) {
+									final Kind kind = getKind(getExtension(entry[0]));
+									if (kinds.contains(kind)) {
+										collector.add(archive.getArchiveFileObject(packageName + entry[0], entry[1],
+												this.charset));
+									}
+								}
+							}
+						}
+					}
+				} else {
+					List<String[]> types = archive.getTypes(key);
+					if (types != null) {
+						for (String[] entry : types) {
+							final Kind kind = getKind(getExtension(entry[0]));
+							if (kinds.contains(kind)) {
+								collector.add(archive.getArchiveFileObject(key + entry[0], entry[1], this.charset));
+							}
+						}
+					}
+				}
+			}
+		} else {
+			// we must have a directory
+			File currentFile = new File(file, normalizedPackageName);
+			if (!currentFile.exists()) return;
+			String path;
+			try {
+				path = currentFile.getCanonicalPath();
+			} catch (IOException e) {
+				return;
+			}
+			if (File.separatorChar == '/') {
+				if (!path.endsWith(normalizedPackageName)) return;
+			} else if (!path.endsWith(normalizedPackageName.replace('/', File.separatorChar))) return;
+			File[] files = currentFile.listFiles();
+			if (files != null) {
+				// this was a directory
+				for (File f : files) {
+					if (f.isDirectory() && recurse) {
+						collectAllMatchingFiles(location, file, normalizedPackageName + '/' + f.getName(), kinds, recurse, collector);
+					} else {
+						final Kind kind = getKind(f);
+						if (kinds.contains(kind)) {
+							collector.add(new EclipseFileObject(normalizedPackageName + f.getName(), f.toURI(), kind, this.charset));
+						}
+					}
+				}
+			}
+		}
+	}
+
+	private Iterable<? extends File> concatFiles(Iterable<? extends File> iterable, Iterable<? extends File> iterable2) {
+		ArrayList<File> list = new ArrayList<>();
+		if (iterable2 == null) return iterable;
+		for (Iterator<? extends File> iterator = iterable.iterator(); iterator.hasNext(); ) {
+			list.add(iterator.next());
+		}
+		for (Iterator<? extends File> iterator = iterable2.iterator(); iterator.hasNext(); ) {
+			list.add(iterator.next());
+		}
+		return list;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#flush()
+	 */
+	@Override
+	public void flush() throws IOException {
+		for (Archive archive : this.archivesCache.values()) {
+			if (archive != null) {
+				archive.flush();
+			}
+		}
+	}
+
+	JrtFileSystem getJrtFileSystem(File f){
+		return (JrtFileSystem) getArchive(f);
+	}
+
+	private Archive getArchive(File f) {
+		// check the archive (jar/zip) cache
+		Archive existing = this.archivesCache.get(f);
+		if (existing != null) {
+			return existing;
+		}
+		Archive archive = Archive.UNKNOWN_ARCHIVE;
+		// create a new archive
+		if (f.exists()) {
+			try {
+				if (isJrt(f)) {
+					archive = new JrtFileSystem(f);
+				} else {
+					archive = new Archive(f);
+				}
+			} catch (IOException e) {
+				String error = "Failed to create archive from " + f; //$NON-NLS-1$
+				if (JRTUtil.PROPAGATE_IO_ERRORS) {
+					throw new IllegalStateException(error, e);
+				} else {
+					System.err.println(error);
+					e.printStackTrace();
+				}
+			}
+		}
+		try (Archive previous = this.archivesCache.put(f, archive)) {
+			// Nothing but closing previous instance - which should not exist at this time
+		}
+		return archive;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#getClassLoader(javax.tools.JavaFileManager.Location)
+	 */
+	@Override
+	public ClassLoader getClassLoader(Location location) {
+		validateNonModuleLocation(location);
+		Iterable<? extends File> files = getLocation(location);
+		if (files == null) {
+			// location is unknown
+			return null;
+		}
+		URLClassLoader cl = this.classloaders.get(location);
+		if (cl == null) {
+			ArrayList<URL> allURLs = new ArrayList<>();
+			for (File f : files) {
+				try {
+					allURLs.add(f.toURI().toURL());
+				} catch (MalformedURLException e) {
+					// the url is malformed - this should not happen
+					throw new RuntimeException(e);
+				}
+			}
+			URL[] result = new URL[allURLs.size()];
+			cl = new URLClassLoader(allURLs.toArray(result), getClass().getClassLoader());
+			try (URLClassLoader previous = this.classloaders.put(location, cl)) {
+				// Nothing but closing previous instance - which should not exist at this time
+			} catch (IOException e) {
+				//ignore
+			}
+		}
+		return cl;
+	}
+
+	private Iterable<? extends File> getPathsFrom(String path) {
+		ArrayList<FileSystem.Classpath> paths = new ArrayList<>();
+		ArrayList<File> files = new ArrayList<>();
+		try {
+			this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false);
+		} catch (IllegalArgumentException e) {
+			return null;
+		}
+		for (FileSystem.Classpath classpath : paths) {
+			files.add(new File(classpath.getPath()));
+		}
+		return files;
+	}
+
+	Iterable<? extends File> getDefaultBootclasspath() {
+		List<File> files = new ArrayList<>();
+		String javaversion = System.getProperty("java.version");//$NON-NLS-1$
+		if(javaversion.length() > 3)
+			javaversion = javaversion.substring(0, 3);
+		long jdkLevel = CompilerOptions.versionToJdkLevel(javaversion);
+		if (jdkLevel < ClassFileConstants.JDK1_6) {
+			// wrong jdk - 1.6 or above is required
+			return null;
+		}
+
+		for (FileSystem.Classpath classpath : org.eclipse.jdt.internal.compiler.util.Util.collectFilesNames()) {
+			files.add(new File(classpath.getPath()));
+		}
+		return files;
+	}
+
+	Iterable<? extends File> getDefaultClasspath() {
+		// default classpath
+		ArrayList<File> files = new ArrayList<>();
+		String classProp = System.getProperty("java.class.path"); //$NON-NLS-1$
+		if ((classProp == null) || (classProp.length() == 0)) {
+			return null;
+		} else {
+			StringTokenizer tokenizer = new StringTokenizer(classProp, File.pathSeparator);
+			String token;
+			while (tokenizer.hasMoreTokens()) {
+				token = tokenizer.nextToken();
+				File file = new File(token);
+				if (file.exists()) {
+					files.add(file);
+				}
+			}
+		}
+		return files;
+	}
+
+	private Iterable<? extends File> getEndorsedDirsFrom(String path) {
+		ArrayList<FileSystem.Classpath> paths = new ArrayList<>();
+		ArrayList<File> files = new ArrayList<>();
+		try {
+			this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false);
+		} catch (IllegalArgumentException e) {
+			return null;
+		}
+		for (FileSystem.Classpath classpath : paths) {
+			files.add(new File(classpath.getPath()));
+		}
+		return files;
+	}
+
+	private Iterable<? extends File> getExtdirsFrom(String path) {
+		ArrayList<FileSystem.Classpath> paths = new ArrayList<>();
+		ArrayList<File> files = new ArrayList<>();
+		try {
+			this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false);
+		} catch (IllegalArgumentException e) {
+			return null;
+		}
+		for (FileSystem.Classpath classpath : paths) {
+			files.add(new File(classpath.getPath()));
+		}
+		return files;
+	}
+
+	private String getExtension(File file) {
+		String name = file.getName();
+		return getExtension(name);
+	}
+	private String getExtension(String name) {
+		int index = name.lastIndexOf('.');
+		if (index == -1) {
+			return EclipseFileManager.NO_EXTENSION;
+		}
+		return name.substring(index);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#getFileForInput(javax.tools.JavaFileManager.Location, java.lang.String, java.lang.String)
+	 */
+	@Override
+	public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+		validateNonModuleLocation(location);
+		Iterable<? extends File> files = getLocation(location);
+		if (files == null) {
+			throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$
+		}
+		String normalizedFileName = normalizedFileName(packageName, relativeName);
+		for (File file : files) {
+			if (file.isDirectory()) {
+				// handle directory
+				File f = new File(file, normalizedFileName);
+				if (f.exists()) {
+					return new EclipseFileObject(packageName + File.separator + relativeName, f.toURI(), getKind(f), this.charset);
+				} else {
+					continue; // go to next entry in the location
+				}
+			} else if (isArchive(file)) {
+				// handle archive file
+				ArchiveFileObject fileObject = getFileObject(file, normalizedFileName);
+				if (fileObject!=null) {
+					return fileObject;
+				}
+			}
+		}
+		return null;
+	}
+
+	@SuppressWarnings("resource") // cached archive is closed in EclipseFileManager.close()
+	private ArchiveFileObject getFileObject(File archiveFile, String normalizedFileName) {
+		Archive archive = getArchive(archiveFile);
+		if (archive == Archive.UNKNOWN_ARCHIVE) {
+			return null;
+		}
+		if (archive.contains(normalizedFileName)) {
+			return archive.getArchiveFileObject(normalizedFileName, null, this.charset);
+		}
+		return null;
+	}
+
+	@SuppressWarnings("resource") // cached archive is closed in EclipseFileManager.close()
+	private Boolean containsFileObject(File archiveFile, String normalizedFileName) {
+		Archive archive = getArchive(archiveFile);
+		if (archive == Archive.UNKNOWN_ARCHIVE) {
+			return null;
+		}
+		return archive.contains(normalizedFileName);
+	}
+
+
+	private String normalizedFileName(String packageName, String relativeName) {
+		StringBuilder sb = new StringBuilder();
+		sb.append(normalized(packageName));
+		if (sb.length() > 0) {
+			sb.append('/');
+		}
+		sb.append(relativeName.replace('\\', '/'));
+		return sb.toString();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#getFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, java.lang.String, javax.tools.FileObject)
+	 */
+	@Override
+	public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling)
+			throws IOException {
+		validateOutputLocation(location);
+		Iterable<? extends File> files = getLocation(location);
+		if (files == null) {
+			throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$
+		}
+		final Iterator<? extends File> iterator = files.iterator();
+		if (iterator.hasNext()) {
+			File file = iterator.next();
+			String normalizedFileName = normalized(packageName) + '/' + relativeName.replace('\\', '/');
+			File f = new File(file, normalizedFileName);
+			return new EclipseFileObject(packageName + File.separator + relativeName, f.toURI(), getKind(f), this.charset);
+		} else {
+			throw new IllegalArgumentException("location is empty : " + location);//$NON-NLS-1$
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#getJavaFileForInput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind)
+	 */
+	@Override
+	public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
+		validateNonModuleLocation(location);
+		if (kind != Kind.CLASS && kind != Kind.SOURCE) {
+			throw new IllegalArgumentException("Invalid kind : " + kind);//$NON-NLS-1$
+		}
+		Iterable<? extends File> files = getLocation(location);
+		if (files == null) {
+			throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$
+		}
+		String normalizedFileName = normalized(className);
+		normalizedFileName += kind.extension;
+		for (File file : files) {
+			if (file.equals(this.jrtHome)) {
+				String modName;
+				if (location instanceof ModuleLocationWrapper) {
+					modName = ((ModuleLocationWrapper) location).modName;
+				} else {
+					modName = ""; //$NON-NLS-1$
+				}
+				return this.jrtSystem.getArchiveFileObject(normalizedFileName, modName, this.charset);
+			} else  if (file.isDirectory()) {
+				// handle directory
+				File f = new File(file, normalizedFileName);
+				if (f.exists()) {
+					return new EclipseFileObject(className, f.toURI(), kind, this.charset);
+				} else {
+					continue; // go to next entry in the location
+				}
+			} else if (isArchive(file)) {
+				// handle archive file
+				ArchiveFileObject fileObject = getFileObject(file, normalizedFileName);
+				if (fileObject!=null) {
+					return fileObject;
+				}
+			}
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
+	 */
+	@Override
+	public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling)
+			throws IOException {
+		validateOutputLocation(location);
+		if (kind != Kind.CLASS && kind != Kind.SOURCE) {
+			throw new IllegalArgumentException("Invalid kind : " + kind);//$NON-NLS-1$
+		}
+		Iterable<? extends File> files = getLocation(location);
+		if (files == null) {
+			if (!location.equals(StandardLocation.CLASS_OUTPUT)
+					&& !location.equals(StandardLocation.SOURCE_OUTPUT))
+				throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$
+			// we will use either the sibling or user.dir
+			if (sibling != null) {
+				String normalizedFileName = normalized(className);
+				int index = normalizedFileName.lastIndexOf('/');
+				if (index != -1) {
+					normalizedFileName = normalizedFileName.substring(index + 1);
+				}
+				normalizedFileName += kind.extension;
+				URI uri = sibling.toUri();
+				URI uri2 = null;
+				try {
+					String path = uri.getPath();
+					index = path.lastIndexOf('/');
+					if (index != -1) {
+						path = path.substring(0, index + 1);
+						path += normalizedFileName;
+					}
+					uri2 = new URI(uri.getScheme(), uri.getHost(), path, uri.getFragment());
+				} catch (URISyntaxException e) {
+					throw new IllegalArgumentException("invalid sibling", e);//$NON-NLS-1$
+				}
+				return new EclipseFileObject(className, uri2, kind, this.charset);
+			} else {
+				String normalizedFileName = normalized(className);
+				normalizedFileName += kind.extension;
+				File f = new File(System.getProperty("user.dir"), normalizedFileName);//$NON-NLS-1$
+				return new EclipseFileObject(className, f.toURI(), kind, this.charset);
+			}
+		}
+		final Iterator<? extends File> iterator = files.iterator();
+		if (iterator.hasNext()) {
+			File file = iterator.next();
+			String normalizedFileName = normalized(className);
+			normalizedFileName += kind.extension;
+			File f = new File(file, normalizedFileName);
+			return new EclipseFileObject(className, f.toURI(), kind, this.charset);
+		} else {
+			throw new IllegalArgumentException("location is empty : " + location);//$NON-NLS-1$
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.StandardJavaFileManager#getJavaFileObjects(java.io.File[])
+	 */
+	@Override
+	public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
+		return getJavaFileObjectsFromFiles(Arrays.asList(files));
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.StandardJavaFileManager#getJavaFileObjects(java.lang.String[])
+	 */
+	@Override
+	public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
+		return getJavaFileObjectsFromStrings(Arrays.asList(names));
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.StandardJavaFileManager#getJavaFileObjectsFromFiles(java.lang.Iterable)
+	 */
+	@Override
+	public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) {
+		ArrayList<JavaFileObject> javaFileArrayList = new ArrayList<>();
+		for (File f : files) {
+			if (f.isDirectory()) {
+				throw new IllegalArgumentException("file : " + f.getAbsolutePath() + " is a directory"); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+			javaFileArrayList.add(new EclipseFileObject(f.getAbsolutePath(), f.toURI(), getKind(f), this.charset));
+		}
+		return javaFileArrayList;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.StandardJavaFileManager#getJavaFileObjectsFromStrings(java.lang.Iterable)
+	 */
+	@Override
+	public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
+		ArrayList<File> files = new ArrayList<>();
+		for (String name : names) {
+			files.add(new File(name));
+		}
+		return getJavaFileObjectsFromFiles(files);
+	}
+
+	public Kind getKind(File f) {
+		return getKind(getExtension(f));
+	}
+
+	private Kind getKind(String extension) {
+		if (Kind.CLASS.extension.equals(extension)) {
+			return Kind.CLASS;
+		} else if (Kind.SOURCE.extension.equals(extension)) {
+			return Kind.SOURCE;
+		} else if (Kind.HTML.extension.equals(extension)) {
+			return Kind.HTML;
+		}
+		return Kind.OTHER;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.StandardJavaFileManager#getLocation(javax.tools.JavaFileManager.Location)
+	 */
+	@Override
+	public Iterable<? extends File> getLocation(Location location) {
+		if (location instanceof LocationWrapper) {
+			return getFiles(((LocationWrapper) location).paths);
+		}
+		LocationWrapper loc = this.locationHandler.getLocation(location, ""); //$NON-NLS-1$
+		if (loc == null) {
+			return null;
+		}
+		return getFiles(loc.getPaths());
+	}
+
+	private Iterable<? extends File> getOutputDir(String string) {
+		if ("none".equals(string)) {//$NON-NLS-1$
+			return null;
+		}
+		File file = new File(string);
+		if (file.exists() && !file.isDirectory()) {
+			throw new IllegalArgumentException("file : " + file.getAbsolutePath() + " is not a directory");//$NON-NLS-1$//$NON-NLS-2$
+		}
+		ArrayList<File> list = new ArrayList<>(1);
+		list.add(file);
+		return list;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#handleOption(java.lang.String, java.util.Iterator)
+	 */
+	@Override
+	public boolean handleOption(String current, Iterator<String> remaining) {
+		try {
+			switch(current) {
+				case "-bootclasspath": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> bootclasspaths = getPathsFrom(remaining.next());
+						if (bootclasspaths != null) {
+							Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH);
+							if ((this.flags & EclipseFileManager.HAS_ENDORSED_DIRS) == 0
+									&& (this.flags & EclipseFileManager.HAS_EXT_DIRS) == 0) {
+								// override default bootclasspath
+								setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootclasspaths);
+							} else if ((this.flags & EclipseFileManager.HAS_ENDORSED_DIRS) != 0) {
+								// endorseddirs have been processed first
+								setLocation(StandardLocation.PLATFORM_CLASS_PATH,
+										concatFiles(iterable, bootclasspaths));
+							} else {
+								// extdirs have been processed first
+								setLocation(StandardLocation.PLATFORM_CLASS_PATH,
+										prependFiles(iterable, bootclasspaths));
+							}
+						}
+						this.flags |= EclipseFileManager.HAS_BOOTCLASSPATH;
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "--system": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> classpaths = getPathsFrom(remaining.next());
+						if (classpaths != null) {
+							Iterable<? extends File> iterable = getLocation(StandardLocation.SYSTEM_MODULES);
+							if (iterable != null) {
+								setLocation(StandardLocation.SYSTEM_MODULES, concatFiles(iterable, classpaths));
+							} else {
+								setLocation(StandardLocation.SYSTEM_MODULES, classpaths);
+							}
+						}
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "--upgrade-module-path": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> classpaths = getPathsFrom(remaining.next());
+						if (classpaths != null) {
+							Iterable<? extends File> iterable = getLocation(StandardLocation.UPGRADE_MODULE_PATH);
+							if (iterable != null) {
+								setLocation(StandardLocation.UPGRADE_MODULE_PATH,
+									concatFiles(iterable, classpaths));
+							} else {
+								setLocation(StandardLocation.UPGRADE_MODULE_PATH, classpaths);
+							}
+						}
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "-classpath": //$NON-NLS-1$
+				case "-cp": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> classpaths = getPathsFrom(remaining.next());
+						if (classpaths != null) {
+							Iterable<? extends File> iterable = getLocation(StandardLocation.CLASS_PATH);
+							if (iterable != null) {
+								setLocation(StandardLocation.CLASS_PATH,
+									concatFiles(iterable, classpaths));
+							} else {
+								setLocation(StandardLocation.CLASS_PATH, classpaths);
+							}
+							if ((this.flags & EclipseFileManager.HAS_PROCESSORPATH) == 0) {
+								setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, classpaths);
+							} else if ((this.flags & EclipseFileManager.HAS_PROC_MODULEPATH) == 0) {
+								if (this.isOnJvm9)
+									setLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, classpaths);
+							}
+						}
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "--module-path": //$NON-NLS-1$
+				case "-p": //$NON-NLS-1$
+					final Iterable<? extends File> classpaths = getPathsFrom(remaining.next());
+					if (classpaths != null) {
+						Iterable<? extends File> iterable = getLocation(StandardLocation.MODULE_PATH);
+						if (iterable != null) {
+							setLocation(StandardLocation.MODULE_PATH, concatFiles(iterable, classpaths));
+						} else {
+							setLocation(StandardLocation.MODULE_PATH, classpaths);
+						}
+						if ((this.flags & EclipseFileManager.HAS_PROCESSORPATH) == 0) {
+							setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, classpaths);
+						} else if ((this.flags & EclipseFileManager.HAS_PROC_MODULEPATH) == 0) {
+							if (this.isOnJvm9)
+								setLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, classpaths);
+						}
+					}
+					return true;
+				case "-encoding": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						this.charset = Charset.forName(remaining.next());
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "-sourcepath": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> sourcepaths = getPathsFrom(remaining.next());
+						if (sourcepaths != null) setLocation(StandardLocation.SOURCE_PATH, sourcepaths);
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "--module-source-path": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> sourcepaths = getPathsFrom(remaining.next());
+						if (sourcepaths != null && this.isOnJvm9)
+							setLocation(StandardLocation.MODULE_SOURCE_PATH, sourcepaths);
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "-extdirs": //$NON-NLS-1$
+					if (this.isOnJvm9) {
+						throw new IllegalArgumentException();
+					}
+					if (remaining.hasNext()) {
+						Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH);
+						setLocation(StandardLocation.PLATFORM_CLASS_PATH,
+								concatFiles(iterable, getExtdirsFrom(remaining.next())));
+						this.flags |= EclipseFileManager.HAS_EXT_DIRS;
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "-endorseddirs": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH);
+						setLocation(StandardLocation.PLATFORM_CLASS_PATH,
+								prependFiles(iterable, getEndorsedDirsFrom(remaining.next())));
+						this.flags |= EclipseFileManager.HAS_ENDORSED_DIRS;
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "-d": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> outputDir = getOutputDir(remaining.next());
+						if (outputDir != null) {
+							setLocation(StandardLocation.CLASS_OUTPUT, outputDir);
+						}
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "-s": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> outputDir = getOutputDir(remaining.next());
+						if (outputDir != null) {
+							setLocation(StandardLocation.SOURCE_OUTPUT, outputDir);
+						}
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "-processorpath": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> processorpaths = getPathsFrom(remaining.next());
+						if (processorpaths != null) {
+							setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, processorpaths);
+						}
+						this.flags |= EclipseFileManager.HAS_PROCESSORPATH;
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "--processor-module-path": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						final Iterable<? extends File> processorpaths = getPathsFrom(remaining.next());
+						if (processorpaths != null && this.isOnJvm9) {
+							setLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, processorpaths);
+							this.flags |= EclipseFileManager.HAS_PROC_MODULEPATH;
+						}
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+				case "--release": //$NON-NLS-1$
+					if (remaining.hasNext()) {
+						this.releaseVersion = remaining.next();
+						return true;
+					} else {
+						throw new IllegalArgumentException();
+					}
+			}
+		} catch (IOException e) {
+			String error = "Failed to handle option " + current; //$NON-NLS-1$
+			if (JRTUtil.PROPAGATE_IO_ERRORS) {
+				throw new IllegalStateException(error, e);
+			} else {
+				System.err.println(error);
+				e.printStackTrace();
+			}
+		}
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#hasLocation(javax.tools.JavaFileManager.Location)
+	 */
+	@Override
+	public boolean hasLocation(Location location) {
+		try {
+			return getLocationForModule(location, "") != null; //$NON-NLS-1$
+		} catch (IOException e) {
+			// nothing to do
+		}
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#inferBinaryName(javax.tools.JavaFileManager.Location, javax.tools.JavaFileObject)
+	 */
+	@Override
+	public String inferBinaryName(Location location, JavaFileObject file) {
+		validateNonModuleLocation(location);
+		Iterable<? extends Path> paths = getLocationAsPaths(location);
+		if (paths == null) {
+			return null;
+		}
+		if (file instanceof JrtFileObject) {
+			Path filePath = ((JrtFileObject) file).path;
+			filePath = filePath.subpath(2, filePath.getNameCount());
+			String name = filePath.toString();
+			int index = name.lastIndexOf('.');
+			if (index != -1) {
+				name = name.substring(0, index);
+			}
+			return name.replace('/', '.');
+		}
+		String name = file.getName();
+		JavaFileObject javaFileObject = null;
+		int index = name.lastIndexOf('.');
+		if (index != -1) {
+			name = name.substring(0, index);
+		}
+		try {
+			javaFileObject = getJavaFileForInput(location, name, file.getKind());
+		} catch (IOException e) {
+			// ignore
+		} catch (IllegalArgumentException iae) {
+			return null; // Either unknown kind or location not present
+		}
+		if (javaFileObject == null) {
+			return null;
+		}
+		return name.replace('/', '.');
+	}
+
+	private boolean isArchive(File f) {
+		if (isJrt(f))
+			return false;
+		String extension = getExtension(f);
+		return extension.equalsIgnoreCase(".jar") || extension.equalsIgnoreCase(".zip"); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	private boolean isJrt(File f) {
+		return f.getName().toLowerCase().equals(JrtFileSystem.BOOT_MODULE);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.StandardJavaFileManager#isSameFile(javax.tools.FileObject, javax.tools.FileObject)
+	 */
+	@Override
+	public boolean isSameFile(FileObject fileObject1, FileObject fileObject2) {
+		// EclipseFileManager creates only EcliseFileObject
+		if (!(fileObject1 instanceof EclipseFileObject)) throw new IllegalArgumentException("Unsupported file object class : " + fileObject1.getClass());//$NON-NLS-1$
+		if (!(fileObject2 instanceof EclipseFileObject)) throw new IllegalArgumentException("Unsupported file object class : " + fileObject2.getClass());//$NON-NLS-1$
+		return fileObject1.equals(fileObject2);
+	}
+	/* (non-Javadoc)
+	 * @see javax.tools.OptionChecker#isSupportedOption(java.lang.String)
+	 */
+	@Override
+	public int isSupportedOption(String option) {
+		return Options.processOptionsFileManager(option);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileManager#list(javax.tools.JavaFileManager.Location, java.lang.String, java.util.Set, boolean)
+	 */
+	@Override
+	public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse)
+			throws IOException {
+		validateNonModuleLocation(location);
+		Iterable<? extends File> allFilesInLocations = getLocation(location);
+		if (allFilesInLocations == null) {
+			throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$
+		}
+
+		ArrayList<JavaFileObject> collector = new ArrayList<>();
+		String normalizedPackageName = normalized(packageName);
+		for (File file : allFilesInLocations) {
+			collectAllMatchingFiles(location, file, normalizedPackageName, kinds, recurse, collector);
+		}
+		return collector;
+	}
+
+	private String normalized(String className) {
+		char[] classNameChars = className.toCharArray();
+		for (int i = 0, max = classNameChars.length; i < max; i++) {
+			switch(classNameChars[i]) {
+				case '\\' :
+					classNameChars[i] = '/';
+					break;
+				case '.' :
+					classNameChars[i] = '/';
+			}
+		}
+		return new String(classNameChars);
+	}
+
+	private Iterable<? extends File> prependFiles(Iterable<? extends File> iterable,
+			Iterable<? extends File> iterable2) {
+		if (iterable2 == null) return iterable;
+		ArrayList<File> list = new ArrayList<>();
+		for (Iterator<? extends File> iterator = iterable2.iterator(); iterator.hasNext(); ) {
+			list.add(iterator.next());
+		}
+		if (iterable != null) {
+			for (Iterator<? extends File> iterator = iterable.iterator(); iterator.hasNext(); ) {
+				list.add(iterator.next());
+			}
+		}
+		return list;
+	}
+	private boolean isRunningJvm9() {
+		return (SourceVersion.latest().compareTo(SourceVersion.RELEASE_8) > 0);
+	}
+	/* (non-Javadoc)
+	 * @see javax.tools.StandardJavaFileManager#setLocation(javax.tools.JavaFileManager.Location, java.lang.Iterable)
+	 */
+	@Override
+	public void setLocation(Location location, Iterable<? extends File> files) throws IOException {
+		if (location.isOutputLocation() && files != null) {
+			// output location
+			int count = 0;
+			for (Iterator<? extends File> iterator = files.iterator(); iterator.hasNext(); ) {
+				iterator.next();
+				count++;
+			}
+			if (count != 1) {
+				throw new IllegalArgumentException("output location can only have one path");//$NON-NLS-1$
+			}
+		}
+		this.locationHandler.setLocation(location, "", getPaths(files)); //$NON-NLS-1$
+	}
+
+	public void setLocale(Locale locale) {
+		this.locale = locale == null ? Locale.getDefault() : locale;
+		try {
+			this.bundle = ResourceBundleFactory.getBundle(this.locale);
+		} catch(MissingResourceException e) {
+			System.out.println("Missing resource : " + Main.bundleName.replace('.', '/') + ".properties for locale " + locale); //$NON-NLS-1$//$NON-NLS-2$
+			throw e;
+		}
+	}
+
+	@SuppressWarnings({"unchecked", "rawtypes"})
+	public void processPathEntries(final int defaultSize, final ArrayList paths,
+			final String currentPath, String customEncoding, boolean isSourceOnly,
+			boolean rejectDestinationPathOnJars) {
+
+		String currentClasspathName = null;
+		String currentDestinationPath = null;
+		ArrayList currentRuleSpecs = new ArrayList(defaultSize);
+		StringTokenizer tokenizer = new StringTokenizer(currentPath,
+				File.pathSeparator + "[]", true); //$NON-NLS-1$
+		ArrayList tokens = new ArrayList();
+		while (tokenizer.hasMoreTokens()) {
+			tokens.add(tokenizer.nextToken());
+		}
+		// state machine
+		final int start = 0;
+		final int readyToClose = 1;
+		// 'path' 'path1[rule];path2'
+		final int readyToCloseEndingWithRules = 2;
+		// 'path[rule]' 'path1;path2[rule]'
+		final int readyToCloseOrOtherEntry = 3;
+		// 'path[rule];' 'path;' 'path1;path2;'
+		final int rulesNeedAnotherRule = 4;
+		// 'path[rule1;'
+		final int rulesStart = 5;
+		// 'path[' 'path1;path2['
+		final int rulesReadyToClose = 6;
+		// 'path[rule' 'path[rule1;rule2'
+		final int destinationPathReadyToClose = 7;
+		// 'path[-d bin'
+		final int readyToCloseEndingWithDestinationPath = 8;
+		// 'path[-d bin]' 'path[rule][-d bin]'
+		final int destinationPathStart = 9;
+		// 'path[rule]['
+		final int bracketOpened = 10;
+		// '.*[.*'
+		final int bracketClosed = 11;
+		// '.*([.*])+'
+
+		final int error = 99;
+		int state = start;
+		String token = null;
+		int cursor = 0, tokensNb = tokens.size(), bracket = -1;
+		while (cursor < tokensNb && state != error) {
+			token = (String) tokens.get(cursor++);
+			if (token.equals(File.pathSeparator)) {
+				switch (state) {
+				case start:
+				case readyToCloseOrOtherEntry:
+				case bracketOpened:
+					break;
+				case readyToClose:
+				case readyToCloseEndingWithRules:
+				case readyToCloseEndingWithDestinationPath:
+					state = readyToCloseOrOtherEntry;
+					addNewEntry(paths, currentClasspathName, currentRuleSpecs,
+							customEncoding, currentDestinationPath, isSourceOnly,
+							rejectDestinationPathOnJars);
+					currentRuleSpecs.clear();
+					break;
+				case rulesReadyToClose:
+					state = rulesNeedAnotherRule;
+					break;
+				case destinationPathReadyToClose:
+					throw new IllegalArgumentException(
+							this.bind("configure.incorrectDestinationPathEntry", //$NON-NLS-1$
+									currentPath));
+				case bracketClosed:
+					cursor = bracket + 1;
+					state = rulesStart;
+					break;
+				default:
+					state = error;
+				}
+			} else if (token.equals("[")) { //$NON-NLS-1$
+				switch (state) {
+				case start:
+					currentClasspathName = ""; //$NON-NLS-1$
+					//$FALL-THROUGH$
+				case readyToClose:
+					bracket = cursor - 1;
+					//$FALL-THROUGH$
+				case bracketClosed:
+					state = bracketOpened;
+					break;
+				case readyToCloseEndingWithRules:
+					state = destinationPathStart;
+					break;
+				case readyToCloseEndingWithDestinationPath:
+					state = rulesStart;
+					break;
+				case bracketOpened:
+				default:
+					state = error;
+				}
+			} else if (token.equals("]")) { //$NON-NLS-1$
+				switch (state) {
+				case rulesReadyToClose:
+					state = readyToCloseEndingWithRules;
+					break;
+				case destinationPathReadyToClose:
+					state = readyToCloseEndingWithDestinationPath;
+					break;
+				case bracketOpened:
+					state = bracketClosed;
+					break;
+				case bracketClosed:
+				default:
+					state = error;
+				}
+			} else {
+				// regular word
+				switch (state) {
+				case start:
+				case readyToCloseOrOtherEntry:
+					state = readyToClose;
+					currentClasspathName = token;
+					break;
+				case rulesStart:
+					if (token.startsWith("-d ")) { //$NON-NLS-1$
+						if (currentDestinationPath != null) {
+							throw new IllegalArgumentException(
+									this.bind("configure.duplicateDestinationPathEntry", //$NON-NLS-1$
+											currentPath));
+						}
+						currentDestinationPath = token.substring(3).trim();
+						state = destinationPathReadyToClose;
+						break;
+					} // else we proceed with a rule
+					//$FALL-THROUGH$
+				case rulesNeedAnotherRule:
+					if (currentDestinationPath != null) {
+						throw new IllegalArgumentException(
+								this.bind("configure.accessRuleAfterDestinationPath", //$NON-NLS-1$
+									currentPath));
+					}
+					state = rulesReadyToClose;
+					currentRuleSpecs.add(token);
+					break;
+				case destinationPathStart:
+					if (!token.startsWith("-d ")) { //$NON-NLS-1$
+						state = error;
+					} else {
+						currentDestinationPath = token.substring(3).trim();
+						state = destinationPathReadyToClose;
+					}
+					break;
+				case bracketClosed:
+					for (int i = bracket; i < cursor ; i++) {
+						currentClasspathName += (String) tokens.get(i);
+					}
+					state = readyToClose;
+					break;
+				case bracketOpened:
+					break;
+				default:
+					state = error;
+				}
+			}
+			if (state == bracketClosed && cursor == tokensNb) {
+				cursor = bracket + 1;
+				state = rulesStart;
+			}
+		}
+		switch(state) {
+			case readyToCloseOrOtherEntry:
+				break;
+			case readyToClose:
+			case readyToCloseEndingWithRules:
+			case readyToCloseEndingWithDestinationPath:
+				addNewEntry(paths, currentClasspathName, currentRuleSpecs,
+					customEncoding, currentDestinationPath, isSourceOnly,
+					rejectDestinationPathOnJars);
+				break;
+			case bracketOpened:
+			case bracketClosed:
+			default :
+				// we go on anyway
+		}
+	}
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	protected void addNewEntry(ArrayList paths, String currentClasspathName,
+			ArrayList currentRuleSpecs, String customEncoding,
+			String destPath, boolean isSourceOnly,
+			boolean rejectDestinationPathOnJars) {
+
+		int rulesSpecsSize = currentRuleSpecs.size();
+		AccessRuleSet accessRuleSet = null;
+		if (rulesSpecsSize != 0) {
+			AccessRule[] accessRules = new AccessRule[currentRuleSpecs.size()];
+			boolean rulesOK = true;
+			Iterator i = currentRuleSpecs.iterator();
+			int j = 0;
+			while (i.hasNext()) {
+				String ruleSpec = (String) i.next();
+				char key = ruleSpec.charAt(0);
+				String pattern = ruleSpec.substring(1);
+				if (pattern.length() > 0) {
+					switch (key) {
+						case '+':
+							accessRules[j++] = new AccessRule(pattern
+									.toCharArray(), 0);
+							break;
+						case '~':
+							accessRules[j++] = new AccessRule(pattern
+									.toCharArray(),
+									IProblem.DiscouragedReference);
+							break;
+						case '-':
+							accessRules[j++] = new AccessRule(pattern
+									.toCharArray(),
+									IProblem.ForbiddenReference);
+							break;
+						case '?':
+							accessRules[j++] = new AccessRule(pattern
+									.toCharArray(),
+									IProblem.ForbiddenReference, true/*keep looking for accessible type*/);
+							break;
+						default:
+							rulesOK = false;
+					}
+				} else {
+					rulesOK = false;
+				}
+			}
+			if (rulesOK) {
+	    		accessRuleSet = new AccessRuleSet(accessRules, AccessRestriction.COMMAND_LINE, currentClasspathName);
+			} else {
+				return;
+			}
+		}
+		if (Main.NONE.equals(destPath)) {
+			destPath = Main.NONE; // keep == comparison valid
+		}
+		if (rejectDestinationPathOnJars && destPath != null &&
+				(currentClasspathName.endsWith(".jar") || //$NON-NLS-1$
+					currentClasspathName.endsWith(".zip"))) { //$NON-NLS-1$
+			throw new IllegalArgumentException(
+					this.bind("configure.unexpectedDestinationPathEntryFile", //$NON-NLS-1$
+								currentClasspathName));
+			}
+		FileSystem.Classpath currentClasspath = FileSystem.getClasspath(
+				currentClasspathName,
+				customEncoding,
+				isSourceOnly,
+				accessRuleSet,
+				destPath,
+				null,
+				this.releaseVersion);
+		if (currentClasspath != null) {
+			paths.add(currentClasspath);
+		}
+	}
+	/*
+	 * Lookup the message with the given ID in this catalog and bind its
+	 * substitution locations with the given string.
+	 */
+	private String bind(String id, String binding) {
+		return bind(id, new String[] { binding });
+	}
+
+	/*
+	 * Lookup the message with the given ID in this catalog and bind its
+	 * substitution locations with the given string values.
+	 */
+	private String bind(String id, String[] arguments) {
+		if (id == null)
+			return "No message available"; //$NON-NLS-1$
+		String message = null;
+		try {
+			message = this.bundle.getString(id);
+		} catch (MissingResourceException e) {
+			// If we got an exception looking for the message, fail gracefully by just returning
+			// the id we were looking for.  In most cases this is semi-informative so is not too bad.
+			return "Missing message: " + id + " in: " + Main.bundleName; //$NON-NLS-2$ //$NON-NLS-1$
+		}
+		return MessageFormat.format(message, (Object[]) arguments);
+	}
+
+	private Iterable<? extends File> getFiles(final Iterable<? extends Path> paths) {
+		if (paths == null)
+			return null;
+		return () -> new Iterator<>() {
+			Iterator<? extends Path> original = paths.iterator();
+			@Override
+			public boolean hasNext() {
+				return this.original.hasNext();
+			}
+			@Override
+			public File next() {
+				return this.original.next().toFile();
+			}
+		};
+	}
+	private Iterable<? extends Path> getPaths(final Iterable<? extends File> files) {
+		if (files == null)
+			return null;
+		return () -> new Iterator<>() {
+			Iterator<? extends File> original = files.iterator();
+			@Override
+			public boolean hasNext() {
+				return this.original.hasNext();
+			}
+			@Override
+			public Path next() {
+				return this.original.next().toPath();
+			}
+		};
+	}
+
+	private void validateFileObject(FileObject file) {
+		// FIXME: fill-up
+	}
+	private void validateModuleLocation(Location location, String modName) {
+		Objects.requireNonNull(location);
+		if (modName == null) {
+			throw new IllegalArgumentException("module must not be null"); //$NON-NLS-1$
+		}
+		if (this.isOnJvm9) {
+			if (!location.isModuleOrientedLocation() && !location.isOutputLocation()) {
+				throw new IllegalArgumentException("location is module related :" + location.getName()); //$NON-NLS-1$
+			}
+		}
+	}
+	private void validateNonModuleLocation(Location location) {
+		Objects.requireNonNull(location);
+		if (this.isOnJvm9) {
+			if (location.isModuleOrientedLocation() && location.isOutputLocation()) {
+				throw new IllegalArgumentException("location is module related :" + location.getName()); //$NON-NLS-1$
+			}
+		}
+	}
+	private void validateOutputLocation(Location location) {
+		Objects.requireNonNull(location);
+		if (!location.isOutputLocation()) {
+			throw new IllegalArgumentException("location is not output location :" + location.getName()); //$NON-NLS-1$
+		}
+	}
+	@Override
+	public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) {
+		return getJavaFileObjectsFromPaths(Arrays.asList(paths));
+	}
+
+	@Override
+	public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths(Iterable<? extends Path> paths) {
+		return getJavaFileObjectsFromFiles(getFiles(paths));
+	}
+
+	@Override
+	public Iterable<? extends Path> getLocationAsPaths(Location location) {
+		if (location instanceof LocationWrapper) {
+			return ((LocationWrapper) location).paths;
+		}
+		LocationWrapper loc = this.locationHandler.getLocation(location);
+		if (loc == null) {
+			return null;
+		}
+		return loc.getPaths();
+	}
+
+	@Override
+	public void setLocationFromPaths(Location location, Collection<? extends Path> paths) throws IOException {
+		setLocation(location, getFiles(paths));
+		if (location == StandardLocation.MODULE_PATH || location == StandardLocation.MODULE_SOURCE_PATH) {
+			// FIXME: same for module source path?
+			Map<String, String> options = new HashMap<>();
+			// FIXME: Find a way to get the options from the EclipseCompiler and pass it to the parser.
+			String latest = CompilerOptions.getLatestVersion();
+			options.put(CompilerOptions.OPTION_Compliance, latest);
+			options.put(CompilerOptions.OPTION_Source, latest);
+			options.put(CompilerOptions.OPTION_TargetPlatform, latest);
+			CompilerOptions compilerOptions = new CompilerOptions(options);
+			ProblemReporter problemReporter =
+					new ProblemReporter(
+						DefaultErrorHandlingPolicies.proceedWithAllProblems(),
+						compilerOptions,
+						new DefaultProblemFactory());
+			for (Path path : paths) {
+				List<Classpath> mp = ModuleFinder.findModules(path.toFile(), null,
+						new Parser(problemReporter, true), null, true, this.releaseVersion);
+				for (Classpath cp : mp) {
+					Collection<String> moduleNames = cp.getModuleNames(null);
+					for (String string : moduleNames) {
+						Path p = Paths.get(cp.getPath());
+						setLocationForModule(location, string,  Collections.singletonList(p));
+					}
+				}
+			}
+		}
+	}
+
+	@Override
+	public boolean contains(Location location, FileObject fo) throws IOException {
+		validateFileObject(fo);
+		Iterable<? extends File> files = getLocation(location);
+		if (files == null) {
+			throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$
+		}
+		for (File file : files) {
+			if (file.isDirectory()) {
+				if (fo instanceof EclipseFileObject) {
+					Path filepath = ((EclipseFileObject) fo).f.toPath();
+					if (filepath.startsWith(Paths.get(file.toURI()).toAbsolutePath())) {
+						return true;
+					}
+				}
+			} else if (isArchive(file)) {
+				if (fo instanceof ArchiveFileObject) {
+					Boolean contains = containsFileObject(file, ((ArchiveFileObject) fo).entryName);
+					if (contains != null) {
+						return contains;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public Location getLocationForModule(Location location, String moduleName) throws IOException {
+		validateModuleLocation(location, moduleName);
+		Location result = this.locationHandler.getLocation(location, moduleName);
+		if (result == null && location == StandardLocation.CLASS_OUTPUT) {
+			LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.MODULE_SOURCE_PATH, moduleName);
+			// There are cases where we don't have module source path in that case we need to create
+			// classes in default location
+			if (wrapper == null) {
+				result = location;
+			} else {
+				deriveOutputLocationForModules(moduleName, wrapper.paths);
+				result = getLocationForModule(location, moduleName);
+			}
+		} else if (result == null && location == StandardLocation.SOURCE_OUTPUT) {
+			LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.MODULE_SOURCE_PATH, moduleName);
+			deriveSourceOutputLocationForModules(moduleName, wrapper.paths);
+			result = getLocationForModule(location, moduleName);
+		}
+		return result;
+	}
+
+	@Override
+	public Location getLocationForModule(Location location, JavaFileObject fo) {
+		validateModuleLocation(location, ""); //$NON-NLS-1$
+		Path path = null;
+		if (fo instanceof ArchiveFileObject) {
+			path = ((ArchiveFileObject) fo).file.toPath();
+			return this.locationHandler.getLocation(location, path);
+		} else if (fo instanceof EclipseFileObject) {
+			path = ((EclipseFileObject) fo).f.toPath();
+			try {
+				path = path.toRealPath();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			LocationContainer container = this.locationHandler.getLocation(location);
+			while (path != null) {
+				Location loc = container.get(path);
+				if (loc != null)
+					return loc;
+				path = path.getParent();
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws IOException {
+		// FIXME: Need special handling in case of module class loaders.
+		return ServiceLoader.load(service, getClassLoader(location));
+	}
+
+	@Override
+	public String inferModuleName(Location location) throws IOException {
+		if (location instanceof ModuleLocationWrapper) {
+			ModuleLocationWrapper wrapper = (ModuleLocationWrapper) location;
+			return wrapper.modName;
+		}
+		return null;
+	}
+
+	@Override
+	public Iterable<Set<Location>> listLocationsForModules(Location location) {
+		validateModuleLocation(location, ""); //$NON-NLS-1$
+		return this.locationHandler.listLocationsForModules(location);
+	}
+
+	@Override
+	public Path asPath(FileObject file) {
+		validateFileObject(file);
+		EclipseFileObject eclFile = (EclipseFileObject) file;
+		if (eclFile.f != null) {
+			return eclFile.f.toPath();
+		}
+		return null;
+	}
+	private void deriveOutputLocationForModules(String moduleName, Collection<? extends Path> paths) {
+		LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT, moduleName);
+		if (wrapper == null) {
+			// First get from our internally known location for legacy/unnamed location
+			wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT, ""); //$NON-NLS-1$
+			if (wrapper == null) {
+				wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT);
+			}
+			if (wrapper != null) {
+				Iterator<? extends Path> iterator = wrapper.paths.iterator();
+				if (iterator.hasNext()) {
+					try {
+					// Per module output location is always a singleton list
+					Path path = iterator.next().resolve(moduleName);
+					this.locationHandler.setLocation(StandardLocation.CLASS_OUTPUT, moduleName, Collections.singletonList(path));
+					} catch(Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
+	private void deriveSourceOutputLocationForModules(String moduleName, Collection<? extends Path> paths) {
+		LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT, moduleName);
+		if (wrapper == null) {
+			// First get from our internally known location for legacy/unnamed location
+			wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT, ""); //$NON-NLS-1$
+			if (wrapper == null) {
+				wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT);
+			}
+			if (wrapper != null) {
+				Iterator<? extends Path> iterator = wrapper.paths.iterator();
+				if (iterator.hasNext()) {
+					try {
+					// Per module output location is always a singleton list
+					Path path = iterator.next().resolve(moduleName);
+					this.locationHandler.setLocation(StandardLocation.SOURCE_OUTPUT, moduleName, Collections.singletonList(path));
+					} catch(Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
+	@Override
+	public void setLocationForModule(Location location, String moduleName, Collection<? extends Path> paths) throws IOException {
+		validateModuleLocation(location, moduleName);
+		this.locationHandler.setLocation(location, moduleName, paths);
+		if (location == StandardLocation.MODULE_SOURCE_PATH) {
+			LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT, moduleName);
+			if (wrapper == null) {
+				wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT, ""); //$NON-NLS-1$
+				if (wrapper != null) {
+					Iterator<? extends Path> iterator = wrapper.paths.iterator();
+					if (iterator.hasNext()) {
+						// Per module output location is always a singleton list
+						Path path = iterator.next().resolve(moduleName);
+						this.locationHandler.setLocation(StandardLocation.CLASS_OUTPUT, moduleName, Collections.singletonList(path));
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileObject.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileObject.java
new file mode 100644
index 0000000..b45307c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileObject.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Walter Harley   - Patch for ensuring the parent folders are created
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.SimpleJavaFileObject;
+
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.util.JRTUtil;
+
+/**
+ * Implementation of a Java file object that corresponds to a file on the file system
+ */
+public class EclipseFileObject extends SimpleJavaFileObject {
+	File f;
+	private Charset charset;
+	private boolean parentsExist; // parent directories exist
+
+	public EclipseFileObject(String className, URI uri, Kind kind, Charset charset) {
+		super(uri, kind);
+		this.f = new File(this.uri);
+		this.charset = charset;
+		this.parentsExist = false;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileObject#getAccessLevel()
+	 */
+	@Override
+	public Modifier getAccessLevel() {
+		// cannot express multiple modifier
+		if (getKind() != Kind.CLASS) {
+			return null;
+		}
+		ClassFileReader reader = null;
+   		try {
+			reader = ClassFileReader.read(this.f);
+		} catch (ClassFormatException e) {
+			// ignore
+		} catch (IOException e) {
+			String error = "Failed to read access level from " + this.f; //$NON-NLS-1$
+			if (JRTUtil.PROPAGATE_IO_ERRORS) {
+				throw new IllegalStateException(error, e);
+			} else {
+				System.err.println(error);
+				e.printStackTrace();
+			}
+		}
+		if (reader == null) {
+			return null;
+		}
+		final int accessFlags = reader.accessFlags();
+		if ((accessFlags & ClassFileConstants.AccPublic) != 0) {
+			return Modifier.PUBLIC;
+		}
+		if ((accessFlags & ClassFileConstants.AccAbstract) != 0) {
+			return Modifier.ABSTRACT;
+		}
+		if ((accessFlags & ClassFileConstants.AccFinal) != 0) {
+			return Modifier.FINAL;
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.tools.JavaFileObject#getNestingKind()
+	 */
+	@Override
+	public NestingKind getNestingKind() {
+		switch(this.kind) {
+			case SOURCE :
+				return NestingKind.TOP_LEVEL;
+			case CLASS :
+        		ClassFileReader reader = null;
+        		try {
+        			reader = ClassFileReader.read(this.f);
+        		} catch (ClassFormatException e) {
+        			// ignore
+        		} catch (IOException e) {
+        			String error = "Failed to read access nesting kind from " + this.f; //$NON-NLS-1$
+        			if (JRTUtil.PROPAGATE_IO_ERRORS) {
+        				throw new IllegalStateException(error, e);
+        			} else {
+        				System.err.println(error);
+        				e.printStackTrace();
+        			}
+        		}
+        		if (reader == null) {
+        			return null;
+        		}
+        		if (reader.isAnonymous()) {
+        			return NestingKind.ANONYMOUS;
+        		}
+        		if (reader.isLocal()) {
+        			return NestingKind.LOCAL;
+        		}
+        		if (reader.isMember()) {
+        			return NestingKind.MEMBER;
+        		}
+        		return NestingKind.TOP_LEVEL;
+        	default:
+        		return null;
+		}
+	}
+
+	/**
+	 * @see javax.tools.FileObject#delete()
+	 */
+	@Override
+	public boolean delete() {
+		return this.f.delete();
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (!(o instanceof EclipseFileObject)) {
+			return false;
+		}
+		EclipseFileObject eclipseFileObject = (EclipseFileObject) o;
+		return eclipseFileObject.toUri().equals(this.uri);
+	}
+
+	/**
+	 * @see javax.tools.FileObject#getCharContent(boolean)
+	 */
+	@Override
+	public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+		return Util.getCharContents(this, ignoreEncodingErrors, org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(this.f), this.charset.name());
+	}
+
+	/**
+	 * @see javax.tools.FileObject#getLastModified()
+	 */
+	@Override
+	public long getLastModified() {
+		return this.f.lastModified();
+	}
+
+	@Override
+	public String getName() {
+        return this.f.getPath();
+    }
+
+	@Override
+	public int hashCode() {
+		return this.f.hashCode();
+	}
+
+	/**
+	 * @see javax.tools.FileObject#openInputStream()
+	 */
+	@Override
+	public InputStream openInputStream() throws IOException {
+		return new BufferedInputStream(new FileInputStream(this.f));
+	}
+
+	/**
+	 * @see javax.tools.FileObject#openOutputStream()
+	 */
+	@Override
+	public OutputStream openOutputStream() throws IOException {
+		ensureParentDirectoriesExist();
+		return new BufferedOutputStream(new FileOutputStream(this.f));
+	}
+
+	/**
+	 * @see javax.tools.FileObject#openReader(boolean)
+	 */
+	@Override
+	public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+		return new BufferedReader(new FileReader(this.f));
+	}
+
+	/**
+	 * @see javax.tools.FileObject#openWriter()
+	 */
+	@Override
+	public Writer openWriter() throws IOException {
+		ensureParentDirectoriesExist();
+		return new BufferedWriter(new FileWriter(this.f));
+	}
+
+	@Override
+	public String toString() {
+		return this.f.getAbsolutePath();
+	}
+
+    private void ensureParentDirectoriesExist() throws IOException {
+        if (!this.parentsExist) {
+            File parent = this.f.getParentFile();
+            if (parent != null && !parent.exists()) {
+                if (!parent.mkdirs()) {
+                    // could have been concurrently created
+                    if (!parent.exists() || !parent.isDirectory())
+                        throw new IOException("Unable to create parent directories for " + this.f); //$NON-NLS-1$
+                }
+            }
+            this.parentsExist = true;
+        }
+    }
+
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/JrtFileSystem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/JrtFileSystem.java
new file mode 100644
index 0000000..04c1976
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/JrtFileSystem.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2017 IBM Corporation.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.Charset;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.zip.ZipException;
+
+import javax.tools.JavaFileObject;
+
+import org.eclipse.jdt.internal.compiler.apt.util.ModuleLocationHandler.ModuleLocationWrapper;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.util.JRTUtil;
+
+public class JrtFileSystem extends Archive {
+
+	private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$
+
+	static final String BOOT_MODULE = "jrt-fs.jar"; //$NON-NLS-1$
+
+	public HashMap<String, Path> modulePathMap;
+	Path modules;
+	private java.nio.file.FileSystem jrtfs;
+
+	public JrtFileSystem(File file) throws ZipException, IOException {
+		this.file = file;
+		initialize();
+	}
+
+	public void initialize() throws IOException {
+		// initialize packages
+		this.modulePathMap = new HashMap<>();
+		URL jrtPath = null;
+
+		if (this.file.exists()) {
+			jrtPath = Paths.get(this.file.toPath().toString(), "lib", JRTUtil.JRT_FS_JAR).toUri().toURL(); //$NON-NLS-1$
+			try (URLClassLoader loader = new URLClassLoader(new URL[] { jrtPath })) {
+				HashMap<String, ?> env = new HashMap<>();
+				this.jrtfs = FileSystems.newFileSystem(JRT_URI, env, loader);
+				this.modules = this.jrtfs.getPath("/modules"); //$NON-NLS-1$
+			}
+		} else {
+			return;
+		}
+
+		org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(this.file,
+				new org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor<Path>() {
+			@Override
+			public FileVisitResult visitModule(Path path, String name) throws IOException {
+				JrtFileSystem.this.modulePathMap.put(name, path);
+				return FileVisitResult.CONTINUE;
+			}
+		}, JRTUtil.NOTIFY_MODULES);
+	}
+
+	public List<JrtFileObject> list(ModuleLocationWrapper location, String packageName,
+			Set<JavaFileObject.Kind> kinds, boolean recurse, Charset charset) {
+    	String module = location.modName;
+    	Path mPath = this.modules.resolve(module);
+    	Path resolve = mPath.resolve(packageName);
+    	java.util.List<Path> files = null;
+        try (Stream<Path> p = Files.list(resolve)) {
+            files = p.filter((path) -> {
+            	if (Files.isDirectory(path))
+            		return false;
+            	else
+            		return true;
+            }).collect(Collectors.toList());
+        } catch (IOException e) {
+        	String error = "Failed to read files from " + resolve; //$NON-NLS-1$
+			if (JRTUtil.PROPAGATE_IO_ERRORS) {
+				throw new IllegalStateException(error, e);
+			} else {
+				System.err.println(error);
+				e.printStackTrace();
+			}
+        }
+        List<JrtFileObject> result = new ArrayList<>();
+        for (Path p: files) {
+        	result.add(new JrtFileObject(this.file, p, module, charset));
+        }
+        return result;
+    }
+	@Override
+	public ArchiveFileObject getArchiveFileObject(String fileName, String module, Charset charset) {
+		return new JrtFileObject(this.file, this.modules.resolve(module).resolve(fileName), module, charset);
+	}
+
+	@Override
+	public boolean contains(String entryName) {
+		// FIXME
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		return "JRT: " + (this.file == null ? "UNKNOWN_ARCHIVE" : this.file.getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	class JrtFileObject extends ArchiveFileObject {
+		String module;
+		Path path;
+		private JrtFileObject(File file, Path path, String module, Charset charset) {
+			super(file, path.toString(), charset);
+			this.path = path;
+			this.module = module;
+		}
+
+		@Override
+		protected ClassFileReader getClassReader() {
+			ClassFileReader reader = null;
+			try {
+				byte[] content = JRTUtil.getClassfileContent(this.file, this.entryName, this.module);
+				if (content == null) return null;
+				return new ClassFileReader(content, this.entryName.toCharArray());
+			} catch (ClassFormatException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			return reader;
+		}
+
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#getCharContent(boolean)
+		 */
+		@Override
+		public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+			return Util.getCharContents(this, ignoreEncodingErrors,
+					org.eclipse.jdt.internal.compiler.util.JRTUtil.getClassfileContent(this.file, this.entryName, this.module),
+					this.charset.name());
+		}
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#getLastModified()
+		 */
+		@Override
+		public long getLastModified() {
+			return 0;
+		}
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#getName()
+		 */
+		@Override
+		public String getName() {
+			return this.path.toString();
+		}
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#openInputStream()
+		 */
+		@Override
+		public InputStream openInputStream() throws IOException {
+			return Files.newInputStream(this.path);
+		}
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#openOutputStream()
+		 */
+		@Override
+		public OutputStream openOutputStream() throws IOException {
+			throw new UnsupportedOperationException();
+		}
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#openReader(boolean)
+		 */
+		@Override
+		public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+			throw new UnsupportedOperationException();
+		}
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#openWriter()
+		 */
+		@Override
+		public Writer openWriter() throws IOException {
+			throw new UnsupportedOperationException();
+		}
+
+		/* (non-Javadoc)
+		 * @see javax.tools.FileObject#toUri()
+		 */
+		@Override
+		public URI toUri() {
+			try {
+				return new URI("JRT:" + this.file.toURI().getPath() + "!" + this.entryName); //$NON-NLS-1$//$NON-NLS-2$
+			} catch (URISyntaxException e) {
+				return null;
+			}
+		}
+
+
+		@Override
+		public String toString() {
+			return this.file.getAbsolutePath() + "[" + this.entryName + "]";//$NON-NLS-1$//$NON-NLS-2$
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ManyToMany.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ManyToMany.java
new file mode 100644
index 0000000..d1f0c57
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ManyToMany.java
@@ -0,0 +1,368 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2017 BEA Systems, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    wharley@bea.com - initial API and implementation
+ *                      (originally in org.eclipse.jdt.apt.core)
+ *    IBM Corporation - Bug 513790
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manage a Map<T1, Set<T2>>, with reverse links so that it is possible to
+ * efficiently find all T1s that have a particular T2 associated with them.
+ * Access to the map is synchronized, so that it is possible to read and
+ * write simultaneously from multiple threads.
+ * <p>
+ * The map permits the null value for keys nor for value elements.
+ * <p>
+ * Design invariants preserved by all operations on this map are as follows:
+ * <ul>
+ * <li> If a key exists, it has at least one value associated with it; that is,
+ * for all k such that null != containsKey(k), getValues(k) returns a non-empty
+ * set.</li>
+ * <li> If a value exists, it has at least one key associated with it; that is,
+ * for all v such that null != containsValue(v), getKeys(v) returns a non-empty
+ * set.</li>
+ */
+public class ManyToMany<T1, T2> {
+
+	private final Map<T1, Set<T2>> _forward = new HashMap<>();
+	private final Map<T2, Set<T1>> _reverse = new HashMap<>();
+	private boolean _dirty = false;
+
+	/**
+	 * Empty all maps.  If the maps previously contained entries,
+	 * this will set the dirty bit.
+	 * @return true if the maps contained any entries prior to being cleared
+	 */
+	public synchronized boolean clear() {
+		boolean hadContent = !_forward.isEmpty() || !_reverse.isEmpty();
+		_reverse.clear();
+		_forward.clear();
+		_dirty |= hadContent;
+		return hadContent;
+	}
+
+	/**
+	 * Sets the dirty bit to false.  Internal operations do not use the dirty
+	 * bit; clearing it will not affect behavior of the map.  It's just there
+	 * for the convenience of callers who don't want to keep track of every
+	 * put() and remove().
+	 */
+	public synchronized void clearDirtyBit() {
+		_dirty = false;
+	}
+
+	/**
+	 * Equivalent to keySet().contains(key).
+	 * @return true if the map contains the specified key.
+	 */
+	public synchronized boolean containsKey(T1 key) {
+		return _forward.containsKey(key);
+	}
+
+	/**
+	 * Is there a key that is mapped to the specified value?
+	 * Search within the forward map.
+	 * @return true if such a key exists
+	 */
+	public synchronized boolean containsKeyValuePair(T1 key, T2 value) {
+		Set<T2> values = _forward.get(key);
+		if (null == values) {
+			return false;
+		}
+		return values.contains(value);
+	}
+
+	/**
+	 * Equivalent to values().contains(value).
+	 * @return true if the map contains the specified value (regardless
+	 * of what key it might be associated with).
+	 */
+	public synchronized boolean containsValue(T2 value) {
+		return _reverse.containsKey(value);
+	}
+
+	/**
+	 * Search the reverse map for all keys that have been associated with
+	 * a particular value.
+	 * @return the set of keys that are associated with the specified value,
+	 * or an empty set if the value does not exist in the map.
+	 */
+	public synchronized Set<T1> getKeys(T2 value) {
+		Set<T1> keys = _reverse.get(value);
+		if (null == keys) {
+			return Collections.emptySet();
+		}
+		return new HashSet<>(keys);
+	}
+
+	/**
+	 * Search the forward map for all values associated with a particular key.
+	 * Returns a copy of the set of values.
+	 * @return a copy of the set of values that are associated with the
+	 * specified key, or an empty set if the key does not exist in the map.
+	 */
+	public synchronized Set<T2> getValues(T1 key) {
+		Set<T2> values = _forward.get(key);
+		if (null == values) {
+			return Collections.emptySet();
+		}
+		return new HashSet<>(values);
+	}
+
+	/**
+	 * @return a copy of the set of all keys (that is, all items of type T1).
+	 * If the maps are empty, the returned set will be empty, not null.  The
+	 * returned set can be modified by the caller without affecting the map.
+	 * @see #getValueSet()
+	 */
+	public synchronized Set<T1> getKeySet() {
+		Set<T1> keys = new HashSet<>(_forward.keySet());
+		return keys;
+	}
+
+	/**
+	 * @return a copy of the set of all values (that is, all items of type T2).
+	 * If the maps are empty, the returned set will be empty, not null.  The
+	 * returned set can be modified by the caller without affecting the map.
+	 * @see #getKeySet()
+	 */
+	public synchronized Set<T2> getValueSet() {
+		Set<T2> values = new HashSet<>(_reverse.keySet());
+		return values;
+	}
+
+	/**
+	 * Return the state of the dirty bit.  All operations that change the state
+	 * of the maps, including @see #clear(), set the dirty bit if any content actually
+	 * changed.  The only way to clear the dirty bit is to call @see #clearDirtyBit().
+	 * @return true if the map content has changed since it was created or since
+	 * the last call to clearDirtyBit().
+	 * @see #clearDirtyBit()
+	 */
+	public synchronized boolean isDirty() {
+		return _dirty;
+	}
+
+	/**
+	 * Check whether <code>key</code> has an association to any values other
+	 * than <code>value</code> - that is, whether the same key has been added
+	 * with multiple values.  Equivalent to asking whether the intersection of
+	 * <code>getValues(key)</code> and the set containing <code>value</code> is
+	 * non-empty.
+	 * @return true iff <code>key</code> is in the map and is associated
+	 * with values other than <code>value</code>.
+	 * @see #valueHasOtherKeys(Object, Object)
+	 */
+	public synchronized boolean keyHasOtherValues(T1 key, T2 value) {
+		Set<T2> values = _forward.get(key);
+		if (values == null)
+			return false;
+		int size = values.size();
+		if (size == 0)
+			return false;
+		else if (size > 1)
+			return true;
+		else // size == 1
+			return !values.contains(value);
+	}
+
+	/**
+	 * Associate the specified value with the key.  Adds the entry
+	 * to both the forward and reverse maps.  Adding the same value
+	 * twice to a particular key has no effect.  Because this is a
+	 * many-to-many map, adding a new value for an existing key does
+	 * not change the existing association, it adds a new one.
+	 * @param key can be null
+	 * @param value can be null
+	 * @return true if the key/value pair did not exist prior to being added
+	 */
+	public synchronized boolean put(T1 key, T2 value) {
+		// Add to forward map
+		Set<T2> values = _forward.get(key);
+		if (null == values) {
+			values = new HashSet<>();
+			_forward.put(key, values);
+		}
+		boolean added = values.add(value);
+		_dirty |= added;
+
+		// Add to reverse map
+		Set<T1> keys = _reverse.get(value);
+		if (null == keys) {
+			keys = new HashSet<>();
+			_reverse.put(value, keys);
+		}
+		keys.add(key);
+
+		assert checkIntegrity();
+		return added;
+	}
+
+	/**
+	 * Remove a particular key-value association.  This is the inverse
+	 * of put(key, value).  If the key does not exist, or the value
+	 * does not exist, or the association does not exist, this call
+	 * has no effect.
+	 * @return true if the key/value pair existed in the map prior to removal
+	 */
+	public synchronized boolean remove(T1 key, T2 value) {
+		Set<T2> values = _forward.get(key);
+		if (values == null) {
+			assert checkIntegrity();
+			return false;
+		}
+		boolean removed = values.remove(value);
+		if (values.isEmpty()) {
+			_forward.remove(key);
+		}
+		if (removed) {
+			_dirty = true;
+			// it existed, so we need to remove from reverse map as well
+			Set<T1> keys = _reverse.get(value);
+			keys.remove(key);
+			if (keys.isEmpty()) {
+				_reverse.remove(value);
+			}
+		}
+		assert checkIntegrity();
+		return removed;
+	}
+
+	/**
+	 * Remove the key and its associated key/value entries.
+	 * Calling removeKey(k) is equivalent to calling remove(k,v)
+	 * for every v in getValues(k).
+	 * @return true if the key existed in the map prior to removal
+	 */
+	public synchronized boolean removeKey(T1 key) {
+		// Remove all back-references to key.
+		Set<T2> values = _forward.get(key);
+		if (null == values) {
+			// key does not exist in map.
+			assert checkIntegrity();
+			return false;
+		}
+		for (T2 value : values) {
+			Set<T1> keys = _reverse.get(value);
+			if (null != keys) {
+				keys.remove(key);
+				if (keys.isEmpty()) {
+					_reverse.remove(value);
+				}
+			}
+		}
+		// Now remove the forward references from key.
+		_forward.remove(key);
+		_dirty = true;
+		assert checkIntegrity();
+		return true;
+	}
+
+	/**
+	 * Remove the value and its associated key/value entries.
+	 * Calling removeValue(v) is equivalent to calling remove(k,v)
+	 * for every k in getKeys(v).
+	 * @return true if the value existed in the map prior to removal.
+	 */
+	public synchronized boolean removeValue(T2 value) {
+		// Remove any forward references to value
+		Set<T1> keys = _reverse.get(value);
+		if (null == keys) {
+			// value does not exist in map.
+			assert checkIntegrity();
+			return false;
+		}
+		for (T1 key : keys) {
+			Set<T2> values = _forward.get(key);
+			if (null != values) {
+				values.remove(value);
+				if (values.isEmpty()) {
+					_forward.remove(key);
+				}
+			}
+		}
+		// Now remove the reverse references from value.
+		_reverse.remove(value);
+		_dirty = true;
+		assert checkIntegrity();
+		return true;
+	}
+
+	/**
+	 * Check whether <code>value</code> has an association from any keys other
+	 * than <code>key</code> - that is, whether the same value has been added
+	 * with multiple keys.  Equivalent to asking whether the intersection of
+	 * <code>getKeys(value)</code> and the set containing <code>key</code> is
+	 * non-empty.
+	 * @return true iff <code>value</code> is in the map and is associated
+	 * with keys other than <code>key</code>.
+	 * @see #keyHasOtherValues(Object, Object)
+	 */
+	public synchronized boolean valueHasOtherKeys(T2 value, T1 key) {
+		Set<T1> keys = _reverse.get(value);
+		if (keys == null)
+			return false;
+		int size = keys.size();
+		if (size == 0)
+			return false;
+		else if (size > 1)
+			return true;
+		else // size == 1
+			return !keys.contains(key);
+	}
+
+	/**
+	 * Check the integrity of the internal data structures.  This is intended to
+	 * be called within an assert, so that if asserts are disabled the integrity
+	 * checks will not cause a performance impact.
+	 * @return true if everything is okay.
+	 * @throws IllegalStateException if there is a problem.
+	 */
+	private boolean checkIntegrity() {
+		// For every T1->T2 mapping in the forward map, there should be a corresponding
+		// T2->T1 mapping in the reverse map.
+		for (Map.Entry<T1, Set<T2>> entry : _forward.entrySet()) {
+			Set<T2> values = entry.getValue();
+			if (values.isEmpty()) {
+				throw new IllegalStateException("Integrity compromised: forward map contains an empty set"); //$NON-NLS-1$
+			}
+			for (T2 value : values) {
+				Set<T1> keys = _reverse.get(value);
+				if (null == keys || !keys.contains(entry.getKey())) {
+					throw new IllegalStateException("Integrity compromised: forward map contains an entry missing from reverse map: " + value); //$NON-NLS-1$
+				}
+			}
+		}
+		// And likewise in the other direction.
+		for (Map.Entry<T2, Set<T1>> entry : _reverse.entrySet()) {
+			Set<T1> keys = entry.getValue();
+			if (keys.isEmpty()) {
+				throw new IllegalStateException("Integrity compromised: reverse map contains an empty set"); //$NON-NLS-1$
+			}
+			for (T1 key : keys) {
+				Set<T2> values = _forward.get(key);
+				if (null == values || !values.contains(entry.getKey())) {
+					throw new IllegalStateException("Integrity compromised: reverse map contains an entry missing from forward map: " + key); //$NON-NLS-1$
+				}
+			}
+		}
+		return true;
+	}
+
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ModuleLocationHandler.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ModuleLocationHandler.java
new file mode 100644
index 0000000..533ccbb
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/ModuleLocationHandler.java
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * Copyright (c) 2017 IBM Corporation.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.tools.JavaFileManager.Location;
+import javax.tools.StandardLocation;
+
+public class ModuleLocationHandler {
+
+	Map<Location, LocationContainer> containers;
+
+	ModuleLocationHandler() {
+		this.containers = new HashMap<>();
+	}
+
+	public void newSystemLocation(Location loc, JrtFileSystem jrt) throws IOException {
+		SystemLocationContainer systemLocationWrapper = new SystemLocationContainer(StandardLocation.SYSTEM_MODULES, jrt);
+		this.containers.put(loc, systemLocationWrapper);
+	}
+
+	public LocationWrapper getLocation(Location loc, String moduleName) {
+		if (loc instanceof LocationWrapper) {
+			loc = ((LocationWrapper) loc).loc;
+		}
+		LocationContainer forwarder = this.containers.get(loc);
+		if (forwarder != null) {
+			return forwarder.get(moduleName);
+		}
+		return null;
+	}
+
+	public Location getLocation(Location loc, Path path) {
+		LocationContainer forwarder = this.containers.get(loc);
+		if (forwarder != null) {
+			return forwarder.get(path);
+		}
+		return null;
+	}
+	public LocationContainer getLocation(Location location) {
+		return this.containers.get(location);
+	}
+	public void setLocation(Location location, Iterable<? extends Path> paths) {
+		LocationContainer container = this.containers.get(location);
+		if (container == null) {
+			container = new LocationContainer(location);
+			this.containers.put(location, container);
+		}
+		container.setPaths(paths);
+	}
+	public void setLocation(Location location, String moduleName, Iterable<? extends Path> paths) {
+		LocationWrapper wrapper = null;
+		LocationContainer container = this.containers.get(location);
+		if (container != null) {
+			wrapper = container.get(moduleName);
+		} else {
+			container = new LocationContainer(location);
+			this.containers.put(location, container);
+		}
+		if (wrapper == null) {
+			// module name can't be null
+			// TODO: Check unnamed modules can have their own module specific path - probably not
+			if (moduleName.equals("")) { //$NON-NLS-1$
+				wrapper = new LocationWrapper(location, location.isOutputLocation(), paths);
+			} else {
+				wrapper = new ModuleLocationWrapper(location, moduleName, location.isOutputLocation(), paths);
+				for (Path path : paths) {
+					container.put(path, wrapper);
+				}
+			}
+		} else {
+			wrapper.setPaths(paths);
+		}
+		container.put(moduleName, wrapper);
+	}
+	public Iterable<Set<Location>> listLocationsForModules(Location location) {
+		LocationContainer locationContainer = this.containers.get(location);
+		if (locationContainer == null) {
+			return Collections.emptyList();
+		}
+		Set<Location> set = new HashSet<>(locationContainer.locationNames.values());
+		List<Set<Location>> singletonList = Collections.singletonList(set);
+		return singletonList;
+	}
+
+	class LocationContainer extends LocationWrapper {
+
+		Map<String, LocationWrapper> locationNames;
+		Map<Path, LocationWrapper> locationPaths;
+		LocationContainer(Location loc) {
+			super();
+			this.loc = loc;
+			this.locationNames = new HashMap<>();
+			this.locationPaths = new HashMap<>();
+		}
+
+		LocationWrapper get(String moduleName) {
+			return this.locationNames.get(moduleName);
+		}
+
+		void put(String moduleName, LocationWrapper impl) {
+			this.locationNames.put(moduleName, impl);
+			this.paths = null;
+		}
+
+		void put(Path path, LocationWrapper impl) {
+			this.locationPaths.put(path, impl);
+			this.paths = null;
+		}
+
+		Location get(Path path) {
+			return this.locationPaths.get(path);
+		}
+
+		@Override
+		void setPaths(Iterable<? extends Path> paths) {
+			 super.setPaths(paths);
+			 this.clear();
+		 }
+		@Override
+		Iterable<? extends Path> getPaths() {
+			if (this.paths != null)
+				return this.paths;
+			return this.locationPaths.keySet();
+		}
+
+		public void clear() {
+			this.locationNames.clear();
+			this.locationPaths.clear();
+		}
+	}
+
+	class SystemLocationContainer extends LocationContainer {
+
+		public SystemLocationContainer(Location loc, JrtFileSystem jrt) throws IOException {
+			super(loc);
+			jrt.initialize();
+			HashMap<String, Path> modulePathMap = jrt.modulePathMap;
+			Set<String> keySet = modulePathMap.keySet();
+			for (String mod : keySet) {
+				Path path = jrt.file.toPath();
+				ModuleLocationWrapper wrapper = new ModuleLocationWrapper(loc, mod, false,
+						Collections.singletonList(path));
+				this.locationNames.put(mod, wrapper);
+				this.locationPaths.put(path, wrapper);
+			}
+		}
+	}
+
+	class LocationWrapper implements Location {
+
+		Location loc;
+		boolean output;
+		List<? extends Path> paths;
+		LocationWrapper() {
+		}
+		public LocationWrapper(Location loc, boolean output, Iterable<? extends Path> paths) {
+			this.loc = loc;
+			this.output = output;
+			setPaths(paths);
+		}
+
+		@Override
+		public String getName() {
+			return this.loc.getName();
+		}
+
+		@Override
+		public boolean isOutputLocation() {
+			return this.output;
+		}
+
+		Iterable<? extends Path> getPaths() {
+			return this.paths;
+		}
+
+		void setPaths(Iterable<? extends Path> paths) {
+			if (paths == null) {
+				this.paths = null;
+			} else {
+				List<Path> newPaths = new ArrayList<>();
+				for (Path file : paths) {
+					newPaths.add(file);
+				}
+				this.paths = Collections.unmodifiableList(newPaths);
+			}
+		}
+
+		@Override
+		public String toString() {
+			return this.loc.toString() + "[]"; //$NON-NLS-1$
+		}
+	}
+
+	class ModuleLocationWrapper extends LocationWrapper {
+		String modName;
+
+		public ModuleLocationWrapper(Location loc, String mod, boolean output, Iterable<? extends Path> paths) {
+			super(loc, output, paths);
+			this.modName = mod;
+		}
+
+		@Override
+		public String getName() {
+			return this.loc.getName() + "[" + this.modName + "]"; //$NON-NLS-1$//$NON-NLS-2$
+		}
+
+		@Override
+		public boolean isOutputLocation() {
+			return this.output;
+		}
+
+		@Override
+		Iterable<? extends Path> getPaths() {
+			return this.paths;
+		}
+
+		@Override
+		public String toString() {
+			return this.loc.toString() + "[" + this.modName + "]"; //$NON-NLS-1$//$NON-NLS-2$
+		}
+	}
+	public void close() {
+		Collection<LocationContainer> values = this.containers.values();
+		for (LocationContainer locationContainer : values) {
+			locationContainer.clear();
+		}
+	}
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/Options.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/Options.java
new file mode 100644
index 0000000..09d84cf
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/util/Options.java
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2015 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.apt.util;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * Class used to handle options in the EclipseFileManager and the EclipseCompiler
+ */
+public final class Options {
+	private static final Set<String> ZERO_ARGUMENT_OPTIONS;
+	private static final Set<String> ONE_ARGUMENT_OPTIONS;
+	private static final Set<String> FILE_MANAGER_OPTIONS;
+	static {
+		ZERO_ARGUMENT_OPTIONS = new HashSet<>();
+		Options.ZERO_ARGUMENT_OPTIONS.add("-progress");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-proceedOnError");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-proceedOnError:Fatal");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-time");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-v");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-version");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-showversion");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-deprecation");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-help");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-?");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-help:warn");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-?:warn");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-noExit");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-verbose");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-referenceInfo");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-inlineJSR");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-g");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-g:none");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-warn:none");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-preserveAllLocals");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-enableJavadoc");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-Xemacs");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-X");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-O");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-1.3");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-1.4");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-1.5");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-5");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-5.0");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-1.6");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-6");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-6.0");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-1.7");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-7");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-7.0");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-1.8");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-8");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-8.0");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-proc:only");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-proc:none");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-XprintProcessorInfo");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-XprintRounds");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-parameters");//$NON-NLS-1$
+		Options.ZERO_ARGUMENT_OPTIONS.add("-genericsignature");//$NON-NLS-1$
+
+		FILE_MANAGER_OPTIONS = new HashSet<>();
+		Options.FILE_MANAGER_OPTIONS.add("-bootclasspath");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-encoding");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-d");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-classpath");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-cp");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-sourcepath");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-extdirs");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-endorseddirs");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-s");//$NON-NLS-1$
+		Options.FILE_MANAGER_OPTIONS.add("-processorpath");//$NON-NLS-1$
+
+		ONE_ARGUMENT_OPTIONS = new HashSet<>();
+		Options.ONE_ARGUMENT_OPTIONS.addAll(Options.FILE_MANAGER_OPTIONS);
+		Options.ONE_ARGUMENT_OPTIONS.add("-log");//$NON-NLS-1$
+		Options.ONE_ARGUMENT_OPTIONS.add("-repeat");//$NON-NLS-1$
+		Options.ONE_ARGUMENT_OPTIONS.add("-maxProblems");//$NON-NLS-1$
+		Options.ONE_ARGUMENT_OPTIONS.add("-source");//$NON-NLS-1$
+		Options.ONE_ARGUMENT_OPTIONS.add("-target");//$NON-NLS-1$
+		Options.ONE_ARGUMENT_OPTIONS.add("-processor");//$NON-NLS-1$
+		Options.ONE_ARGUMENT_OPTIONS.add("-classNames");//$NON-NLS-1$
+		Options.ONE_ARGUMENT_OPTIONS.add("-properties");//$NON-NLS-1$
+
+	}
+	public static int processOptionsFileManager(String option) {
+		if (option == null) return -1;
+		if (Options.FILE_MANAGER_OPTIONS.contains(option)) {
+			return 1;
+		}
+		return -1;
+	}
+
+	public static int processOptions(String option) {
+		if (option == null) return -1;
+		if (Options.ZERO_ARGUMENT_OPTIONS.contains(option)) {
+			return 0;
+		}
+		if (Options.ONE_ARGUMENT_OPTIONS.contains(option)) {
+			return 1;
+		}
+		if (option.startsWith("-g")) { //$NON-NLS-1$
+			int length = option.length();
+			if (length > 3) {
+				StringTokenizer tokenizer =
+					new StringTokenizer(option.substring(3, option.length()), ",");//$NON-NLS-1$
+				while (tokenizer.hasMoreTokens()) {
+					String token = tokenizer.nextToken();
+					if ("vars".equals(token) || "lines".equals(token) || "source".equals(token)) {//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+						continue;
+					}
+					return -1;
+				}
+				return 0;
+			}
+			return -1;
+		}
+		if (option.startsWith("-warn")) {//$NON-NLS-1$
+			int length = option.length();
+			if (length <= 6) {
+				return -1;
+			}
+			int warnTokenStart;
+			switch (option.charAt(6)) {
+				case '+' :
+					warnTokenStart = 7;
+					break;
+				case '-' :
+					warnTokenStart = 7;
+					break;
+				default:
+					warnTokenStart = 6;
+			}
+
+			StringTokenizer tokenizer =
+				new StringTokenizer(option.substring(warnTokenStart, option.length()), ","); //$NON-NLS-1$
+			int tokenCounter = 0;
+
+			while (tokenizer.hasMoreTokens()) {
+				String token = tokenizer.nextToken();
+				tokenCounter++;
+				if (token.equals("allDeadCode")//$NON-NLS-1$
+						|| token.equals("allDeprecation")//$NON-NLS-1$
+						|| token.equals("allJav