udpate jdt.core to I20220414-1800
- incl. freshly generated block in PublicScanner
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
index 42e5eac..b224014 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2021 IBM Corporation and others.
+ * 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
@@ -312,6 +312,8 @@
 			return JavaCore.VERSION_16;
 		} else if(rawVersion.startsWith("17")) {
 			return JavaCore.VERSION_17;
+		} else if(rawVersion.startsWith("18")) {
+			return JavaCore.VERSION_18;
 		} else {
 			throw new RuntimeException("unknown javac version: " + rawVersion);
 		}
@@ -509,6 +511,20 @@
 				return 0200;
 			}
 		}
+		if (version == JavaCore.VERSION_18) {
+			if ("18-ea".equals(rawVersion)) {
+				return 0000;
+			}
+			if ("18".equals(rawVersion)) {
+				return 0000;
+			}
+			if ("18.0.1".equals(rawVersion)) {
+				return 0100;
+			}
+			if ("18.0.2".equals(rawVersion)) {
+				return 0200;
+			}
+		}
 		throw new RuntimeException("unknown raw javac version: " + rawVersion);
 	}
 	// returns 0L if everything went fine; else the lower word contains the
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
index 2f90961..8bfd1e7 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
@@ -34,6 +34,7 @@
  *     Jesper Steen Moller - Contributions for
  *								bug 404146 - [1.7][compiler] nested try-catch-finally-blocks leads to unrunnable Java byte code
  *								bug 407297 - [1.8][compiler] Control generation of parameter names by option
+ *                              bug 413873 - Warning "Method can be static" on method referencing a non-static inner class
  *******************************************************************************/
 package org.eclipse.jdt.core.tests.compiler.regression;
 
@@ -13384,4 +13385,28 @@
 	template = template.replace("15", CompilerOptions.getLatestVersion());
 	assertEquals("configure.source is not updated", template, output);
 }
+//https://bugs.eclipse.org/bugs/show_bug.cgi?id=413873
+public void test413873() {
+	this.runConformTest(
+		new String[] {
+			"OuterClass.java",
+			"public class OuterClass<T> {\n" + 
+			"	private final class InnerClass {\n" + 
+			"	}\n" + 
+			"\n" + 
+			"	private InnerClass foo(final InnerClass object) {\n" + 
+			"		return object;\n" + 
+			"	}\n" + 
+			"	\n" + 
+			"	public void doStuff() {\n" + 
+			"		foo(new InnerClass());\n" + 
+			"	}\n" + 
+			"}"
+			},
+			"\"" + OUTPUT_DIR +  File.separator + "OuterClass.java\""
+			+ " -1.6 -warn:all-static-method -proc:none -d none",
+			"",
+			"",
+			true);
+}
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
index bcda3c9..59aeb0c 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016, 2021 IBM Corporation and others.
+ * Copyright (c) 2016, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -5634,4 +5634,257 @@
 			"2 problems (2 errors)\n",
 			false);
 	}
+	/*
+	 * Test that reference to a binary package that is exported in a module
+	 * but doesn't have a corresponding resource or .class files is reported.
+	 */
+	public void testBug522472a() {
+		File outputDirectory = new File(OUTPUT_DIR);
+		Util.flushDirectoryContent(outputDirectory);
+		String out = "bin";
+		String directory = OUTPUT_DIR + File.separator + "src";
+		File srcDir = new File(directory);
+		String moduleLoc = directory + File.separator + "mod.one";
+		List<String> files = new ArrayList<>();
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.one { \n" +
+				"	exports x.y.z;\n" +
+				"	exports a.b.c;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "x" + File.separator + "y" + File.separator + "z",
+				"X.java",
+				"package x.y.z;\n");
+		writeFileCollecting(files, moduleLoc + File.separator + "a" + File.separator + "b" + File.separator + "c",
+				"A.java",
+				"package a.b.c;\n" +
+				"public class A {}");
+		StringBuffer buffer = new StringBuffer();
+		buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+			.append(" -9 ")
+			.append(" -classpath \"")
+			.append(Util.getJavaClassLibsAsString())
+			.append("\" ")
+			.append(" --module-source-path " + "\"" + directory + "\" ");
+
+		runConformModuleTest(files,
+				buffer,
+				"",
+				"",
+				false);
+
+		Util.flushDirectoryContent(srcDir);
+		files.clear();
+		moduleLoc = directory + File.separator + "mod.two";
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.two { \n" +
+					"	requires mod.one;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "p" + File.separator + "q" + File.separator + "r",
+				"Main.java",
+				"package p.q.r;\n" +
+				"import a.b.c.*;\n" +
+				"import x.y.z.*;\n" +
+				"public class Main {"
+				+ "}");
+		buffer = new StringBuffer();
+		buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+			.append(" -9 ")
+			.append(" -classpath \"")
+			.append(Util.getJavaClassLibsAsString())
+			.append("\" ")
+			.append(" --module-path " + "\"" + OUTPUT_DIR + File.separator + out + "\" ")
+			.append(" --module-source-path " + "\"" + directory + "\" ");
+		runConformModuleTest(files,
+				buffer,
+				"",
+				"----------\n"
+				+ "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/p/q/r/Main.java (at line 2)\n"
+				+ "	import a.b.c.*;\n"
+				+ "	       ^^^^^\n"
+				+ "The import a.b.c is never used\n"
+				+ "----------\n"
+				+ "2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/p/q/r/Main.java (at line 3)\n"
+				+ "	import x.y.z.*;\n"
+				+ "	       ^^^^^\n"
+				+ "The import x.y.z is never used\n"
+				+ "----------\n"
+				+ "2 problems (2 warnings)\n",
+				false,
+				"package conflict");
+	}
+	/*
+	 * Same as above test case, but two binary modules export the package, without any .class files
+	 */
+	public void testBug522472b() {
+		File outputDirectory = new File(OUTPUT_DIR);
+		Util.flushDirectoryContent(outputDirectory);
+		String out = "bin";
+		String directory = OUTPUT_DIR + File.separator + "src";
+		File srcDir = new File(directory);
+		String moduleLoc = directory + File.separator + "mod.one";
+		List<String> files = new ArrayList<>();
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.one { \n" +
+				"	exports x.y.z;\n" +
+				"	exports a.b.c;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "x" + File.separator + "y" + File.separator + "z",
+				"X.java",
+				"package x.y.z;\n");
+		writeFileCollecting(files, moduleLoc + File.separator + "a" + File.separator + "b" + File.separator + "c",
+				"A.java",
+				"package a.b.c;\n" +
+				"public class A {}");
+
+		moduleLoc = directory + File.separator + "mod.one.a";
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.one.a { \n" +
+				"	exports x.y.z;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "x" + File.separator + "y" + File.separator + "z",
+				"X.java",
+				"package x.y.z;\n");
+		StringBuffer buffer = new StringBuffer();
+		buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+			.append(" -9 ")
+			.append(" -classpath \"")
+			.append(Util.getJavaClassLibsAsString())
+			.append("\" ")
+			.append(" --module-source-path " + "\"" + directory + "\" ");
+
+		runConformModuleTest(files,
+				buffer,
+				"",
+				"",
+				false);
+
+		Util.flushDirectoryContent(srcDir);
+		files.clear();
+		moduleLoc = directory + File.separator + "mod.two";
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.two { \n" +
+					"	requires mod.one;\n" +
+					"	requires mod.one.a;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "p" + File.separator + "q" + File.separator + "r",
+				"Main.java",
+				"package p.q.r;\n" +
+				"import a.b.c.*;\n" +
+				"import x.y.z.*;\n" +
+				"public class Main {"
+				+ "}");
+		buffer = new StringBuffer();
+		buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+			.append(" -9 ")
+			.append(" -classpath \"")
+			.append(Util.getJavaClassLibsAsString())
+			.append("\" ")
+			.append(" --module-path " + "\"" + OUTPUT_DIR + File.separator + out + "\" ")
+			.append(" --module-source-path " + "\"" + directory + "\" ");
+		runNegativeModuleTest(files,
+				buffer,
+				"",
+				"----------\n" +
+				"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/module-info.java (at line 2)\n" +
+				"	requires mod.one;\n" +
+				"	^^^^^^^^^^^^^^^^\n" +
+				"The package x.y.z is accessible from more than one module: mod.one, mod.one.a\n" +
+				"----------\n" +
+				"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/module-info.java (at line 3)\n" +
+				"	requires mod.one.a;\n" +
+				"	^^^^^^^^^^^^^^^^^^\n" +
+				"The package x.y.z is accessible from more than one module: mod.one, mod.one.a\n" +
+				"----------\n" +
+				"----------\n" +
+				"3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/p/q/r/Main.java (at line 3)\n" +
+				"	import x.y.z.*;\n" +
+				"	       ^^^^^\n" +
+				"The import x.y.z cannot be resolved\n" +
+				"----------\n" +
+				"3 problems (3 errors)\n",
+				false,
+				"package conflict");
+	}
+	public void testBug522472d() {
+		File outputDirectory = new File(OUTPUT_DIR);
+		Util.flushDirectoryContent(outputDirectory);
+		String out = "bin";
+		String directory = OUTPUT_DIR + File.separator + "src";
+		String moduleLoc = directory + File.separator + "mod.one";
+		List<String> files = new ArrayList<>();
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.one { \n" +
+				"	exports x.y.z;\n" +
+				"	exports a.b.c;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "x" + File.separator + "y" + File.separator + "z",
+				"X.java",
+				"package x.y.z;\n");
+		writeFileCollecting(files, moduleLoc + File.separator + "a" + File.separator + "b" + File.separator + "c",
+				"A.java",
+				"package a.b.c;\n" +
+				"public class A {}");
+
+		moduleLoc = directory + File.separator + "mod.one.a";
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.one.a { \n" +
+				"	exports x.y.z;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "x" + File.separator + "y" + File.separator + "z",
+				"X.java",
+				"package x.y.z;\n");
+		StringBuffer buffer = new StringBuffer();
+		buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+			.append(" -9 ")
+			.append(" -classpath \"")
+			.append(Util.getJavaClassLibsAsString())
+			.append("\" ")
+			.append(" --module-source-path " + "\"" + directory + "\" ");
+
+		moduleLoc = directory + File.separator + "mod.two";
+		writeFileCollecting(files, moduleLoc,
+				"module-info.java",
+				"module mod.two { \n" +
+					"	requires mod.one;\n" +
+					"	requires mod.one.a;\n" +
+				"}");
+		writeFileCollecting(files, moduleLoc + File.separator + "p" + File.separator + "q" + File.separator + "r",
+				"Main.java",
+				"package p.q.r;\n" +
+				"import a.b.c.*;\n" +
+				"import x.y.z.*;\n" +
+				"public class Main {"
+				+ "}");
+
+		runNegativeModuleTest(files,
+				buffer,
+				"",
+				"----------\n" +
+				"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/module-info.java (at line 2)\n" +
+				"	requires mod.one;\n" +
+				"	^^^^^^^^^^^^^^^^\n" +
+				"The package x.y.z is accessible from more than one module: mod.one, mod.one.a\n" +
+				"----------\n" +
+				"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/module-info.java (at line 3)\n" +
+				"	requires mod.one.a;\n" +
+				"	^^^^^^^^^^^^^^^^^^\n" +
+				"The package x.y.z is accessible from more than one module: mod.one, mod.one.a\n" +
+				"----------\n" +
+				"----------\n" +
+				"3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/p/q/r/Main.java (at line 3)\n" +
+				"	import x.y.z.*;\n" +
+				"	       ^^^^^\n" +
+				"The package x.y.z is accessible from more than one module: mod.one, mod.one.a\n" +
+				"----------\n" +
+				"3 problems (3 errors)\n",
+				false,
+				"conflict");
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
index 8dc580b..593becf 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
@@ -6585,10 +6585,10 @@
 				"}\n"
 			},
 			"----------\n" +
-			"1. ERROR in X.java (at line 10)\n" +
-			"	this(Z::new);\n" +
-			"	     ^^^^^^\n" +
-			"No enclosing instance of the type X.Y is accessible in scope\n" +
+			"1. ERROR in X.java (at line 1)\n" +
+			"	interface I {\n" +
+			"	^\n" +
+			"No enclosing instance of type X.Y is available due to some intermediate constructor invocation\n" +
 			"----------\n");
 }
 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=406586, [1.8][compiler] Missing error about unavailable enclosing instance
@@ -6608,10 +6608,10 @@
 				"}\n"
 			},
 			"----------\n" +
-			"1. ERROR in X.java (at line 8)\n" +
-			"	I i = Y::new;\n" +
-			"	      ^^^^^^\n" +
-			"No enclosing instance of the type X is accessible in scope\n" +
+			"1. ERROR in X.java (at line 1)\n" +
+			"	interface I {\n" +
+			"	^\n" +
+			"No enclosing instance of type X is accessible. Must qualify the allocation with an enclosing instance of type X (e.g. x.new A() where x is an instance of X).\n" +
 			"----------\n");
 }
 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=401989, [1.8][compiler] hook lambda expressions into "can be static" analysis
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PreviewFeatureTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PreviewFeatureTest.java
index be0f9c1..d7ace63 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PreviewFeatureTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PreviewFeatureTest.java
@@ -12,11 +12,15 @@
  *******************************************************************************/
 package org.eclipse.jdt.core.tests.compiler.regression;
 
+import java.io.File;
+import java.io.IOException;
 import java.util.Map;
 
+import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 import org.eclipse.jdt.internal.compiler.impl.JavaFeature;
+import org.eclipse.jdt.core.tests.util.Util;
 
 import junit.framework.Test;
 
@@ -45,6 +49,54 @@
 		}
 		return options;
 	}
+	private String[] getClasspathWithPreviewAPI() {
+		File libDir = new File(LIB_DIR);
+		Util.delete(libDir); // make sure we recycle the libs
+ 		libDir.mkdirs();
+		String jarPath = LIB_DIR + "/lib1.jar";
+		try {
+			Util.createJar(
+				new String[] {
+						"jdk/internal/javac/PreviewFeature.java",
+						"package jdk.internal.javac;\n"
+						+ "import java.lang.annotation.*;\n"
+						+ "@Target({ElementType.METHOD,\n"
+						+ "         ElementType.CONSTRUCTOR,\n"
+						+ "         ElementType.FIELD,\n"
+						+ "         ElementType.PACKAGE,\n"
+						+ "         ElementType.MODULE,\n"
+						+ "         ElementType.TYPE})\n"
+						+ "@Retention(RetentionPolicy.CLASS)\n"
+						+ "public @interface PreviewFeature {\n"
+						+ "    public Feature feature();\n"
+						+ "    public enum Feature {\n"
+						+ "        /**\n"
+						+ "         * A key for testing.\n"
+						+ "         */\n"
+						+ "        TEST;\n"
+						+ "    }\n"
+						+ "}",
+						"p/ABC.java",
+						"package p;\n"
+						+ "import jdk.internal.javac.PreviewFeature;\n"
+						+ "@PreviewFeature(feature=PreviewFeature.Feature.TEST)\n"
+						+ "public class ABC {\n"
+						+ "  @PreviewFeature(feature=PreviewFeature.Feature.TEST)\n"
+						+ "  public void doSomething() {}\n"
+						+ "}"
+				},
+				jarPath,
+				JavaCore.VERSION_9);
+		} catch (IOException e) {
+			// ignore
+		}
+		String [] javaClassLibs = Util.getJavaClassLibs();
+		int javaClassLibsLength;
+		String [] xClassLibs = new String[(javaClassLibsLength = javaClassLibs.length) + 1];
+		System.arraycopy(javaClassLibs, 0, xClassLibs, 0, javaClassLibsLength);
+		xClassLibs[javaClassLibsLength] = jarPath;
+		return xClassLibs;
+	}
 	/*
 	 * Preview API, --enable-preview=false, SuppressWarning=No
 	 */
@@ -52,6 +104,7 @@
 		if (this.complianceLevel >= ClassFileConstants.JDK17) {
 			return;
 		}
+		String[] classLibs = getClasspathWithPreviewAPI();
 		Map<String, String> options = getCompilerOptions();
 		String old = options.get(CompilerOptions.OPTION_EnablePreviews);
 		options.put(CompilerOptions.OPTION_EnablePreviews, CompilerOptions.DISABLED);
@@ -59,16 +112,14 @@
 			runNegativeTest(
 					new String[] {
 							"X.java",
-							"import javax.lang.model.element.Modifier;\n"+
+							"import p.*;\n"+
 							"public class X {\n"+
-									"    Zork z = null;\n" +
-									"public Modifier getModifier() {\n"+
-									"		return Modifier.SEALED;\n"+
-									"	}\n"+
-									"	public Class<?>[] getPermittedClasses() {\n"+
-									"		return this.getClass().getPermittedSubclasses();\n"+
-									"	}\n"+
-									"}\n",
+							"    Zork z = null;\n" +
+							"    ABC abc = null;\n" +
+							"   public void foo () {\n"+
+							"      (new ABC()).doSomething();\n"+
+							"   }\n"+
+							"}\n",
 					},
 					"----------\n" +
 					"1. ERROR in X.java (at line 3)\n" +
@@ -76,17 +127,12 @@
 					"	^^^^\n" +
 					"Zork cannot be resolved to a type\n" +
 					"----------\n" +
-					"2. WARNING in X.java (at line 5)\n" +
-					"	return Modifier.SEALED;\n" +
-					"	       ^^^^^^^^^^^^^^^\n" +
-					"You are using an API that is part of a preview feature and may be removed in future\n" +
-					"----------\n" +
-					"3. WARNING in X.java (at line 8)\n" +
-					"	return this.getClass().getPermittedSubclasses();\n" +
-					"	       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+					"2. WARNING in X.java (at line 6)\n" +
+					"	(new ABC()).doSomething();\n" +
+					"	^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
 					"You are using an API that is part of a preview feature and may be removed in future\n" +
 					"----------\n",
-					null,
+					classLibs,
 					true,
 					options);
 		} finally {
@@ -103,20 +149,19 @@
 		Map<String, String> options = getCompilerOptions();
 		String old = options.get(CompilerOptions.OPTION_EnablePreviews);
 		options.put(CompilerOptions.OPTION_EnablePreviews, CompilerOptions.DISABLED);
+		String[] classLibs = getClasspathWithPreviewAPI();
 		try {
 			runNegativeTest(
 					new String[] {
 							"X.java",
-							"import javax.lang.model.element.Modifier;\n"+
+							"import p.*;\n"+
 							"@SuppressWarnings(\"preview\")\n"+
 							"public class X {\n"+
 									"    Zork z = null;\n" +
-									"public Modifier getModifier() {\n"+
-									"		return Modifier.SEALED;\n"+
-									"	}\n"+
-									"	public Class<?>[] getPermittedClasses() {\n"+
-									"		return this.getClass().getPermittedSubclasses();\n"+
-									"	}\n"+
+									"    ABC abc = null;\n" +
+									"   public void foo () {\n"+
+									"      (new ABC()).doSomething();\n"+
+									"   }\n"+
 									"}\n",
 					},
 					"----------\n" +
@@ -125,7 +170,7 @@
 					"	^^^^\n" +
 					"Zork cannot be resolved to a type\n" +
 					"----------\n",
-					null,
+					classLibs,
 					true,
 					options);
 		} finally {
@@ -141,19 +186,18 @@
 		Map<String, String> options = getCompilerOptions();
 		String old = options.get(CompilerOptions.OPTION_EnablePreviews);
 		options.put(CompilerOptions.OPTION_EnablePreviews, CompilerOptions.ENABLED);
+		String[] classLibs = getClasspathWithPreviewAPI();
 		try {
 			runNegativeTest(
 					new String[] {
 							"X.java",
-							"import javax.lang.model.element.Modifier;\n"+
+							"import p.*;\n"+
 							"public class X {\n"+
 									"    Zork z = null;\n" +
-									"public Modifier getModifier() {\n"+
-									"		return Modifier.SEALED;\n"+
-									"	}\n"+
-									"	public Class<?>[] getPermittedClasses() {\n"+
-									"		return this.getClass().getPermittedSubclasses();\n"+
-									"	}\n"+
+									"    ABC abc = null;\n" +
+									"   public void foo () {\n"+
+									"      (new ABC()).doSomething();\n"+
+									"   }\n"+
 									"}\n",
 					},
 					"----------\n" +
@@ -162,7 +206,7 @@
 					"	^^^^\n" +
 					"Zork cannot be resolved to a type\n" +
 					"----------\n",
-					null,
+					classLibs,
 					true,
 					options);
 		} finally {
@@ -178,20 +222,19 @@
 		Map<String, String> options = getCompilerOptions();
 		String old = options.get(CompilerOptions.OPTION_EnablePreviews);
 		options.put(CompilerOptions.OPTION_EnablePreviews, CompilerOptions.ENABLED);
+		String[] classLibs = getClasspathWithPreviewAPI();
 		try {
 			runNegativeTest(
 					new String[] {
 							"X.java",
-							"import javax.lang.model.element.Modifier;\n"+
+							"import p.*;\n"+
 							"@SuppressWarnings(\"preview\")\n"+
 							"public class X {\n"+
 									"    Zork z = null;\n" +
-									"public Modifier getModifier() {\n"+
-									"		return Modifier.SEALED;\n"+
-									"	}\n"+
-									"	public Class<?>[] getPermittedClasses() {\n"+
-									"		return this.getClass().getPermittedSubclasses();\n"+
-									"	}\n"+
+									"    ABC abc = null;\n" +
+									"   public void foo () {\n"+
+									"      (new ABC()).doSomething();\n"+
+									"   }\n"+
 									"}\n",
 					},
 					"----------\n" +
@@ -200,7 +243,7 @@
 					"	^^^^\n" +
 					"Zork cannot be resolved to a type\n" +
 					"----------\n",
-					null,
+					classLibs,
 					true,
 					options);
 		} finally {
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PublicScannerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PublicScannerTest.java
new file mode 100644
index 0000000..7f41f28
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PublicScannerTest.java
@@ -0,0 +1,334 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Andrey Loskutov 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:
+ *     Andrey Loskutov - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.compiler.regression;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.core.compiler.ITerminalSymbols;
+import org.eclipse.jdt.core.compiler.InvalidInputException;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.parser.Scanner;
+import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
+import org.eclipse.jdt.internal.core.util.PublicScanner;
+
+import junit.framework.Test;
+
+/**
+ * Test that validates {@link Scanner} and {@link PublicScanner} use of tokens.
+ */
+@SuppressWarnings({ "rawtypes" })
+public class PublicScannerTest extends AbstractRegressionTest {
+
+	private Map<Integer, String> ttValueToName;
+	private Map<String, Integer> ttNameToValue;
+	private Field[] ttFields;
+	private Map<Integer, String> tsValueToName;
+	private Map<String, Integer> tsNameToValue;
+	private Field[] tsFields;
+	private MyPublicScanner ps;
+
+	/**
+	 * Replacement map for tokens that shouldn't be exposed to clients
+	 * Key is the token, value is the replacement token
+	 */
+	static final Map<Integer, Integer> SYNTHETIC_REPLACE_TOKENS;
+
+	/**
+	 * Replacement list for tokens that shouldn't be exposed to clients and scanner should
+	 * skip to next token
+	 */
+	static final List<Integer> SYNTHETIC_SKIP_TOKENS;
+
+	/**
+	 * List of tokens that shouldn't be exposed to clients because they can never be
+	 * produced without declaring the source to be "module-info.java".
+	 */
+	static final List<Integer> MODULE_TOKENS;
+
+	static {
+		Map<Integer, Integer> map = new HashMap<>();
+		map.put(TerminalTokens.TokenNameAT308DOTDOTDOT, TerminalTokens.TokenNameAT);
+		map.put(TerminalTokens.TokenNameAT308, TerminalTokens.TokenNameAT);
+//{ObjectTeams:
+		map.put(TerminalTokens.TokenNameATOT, TerminalTokens.TokenNameAT);
+// SH}
+		SYNTHETIC_REPLACE_TOKENS = Collections.unmodifiableMap(map);
+
+		List<Integer> list = new ArrayList<>();
+		list.add(TerminalTokens.TokenNameBeginCaseElement);
+		list.add(TerminalTokens.TokenNameBeginCaseExpr);
+		list.add(TerminalTokens.TokenNameBeginIntersectionCast);
+		list.add(TerminalTokens.TokenNameBeginLambda);
+		list.add(TerminalTokens.TokenNameBeginTypeArguments);
+		list.add(TerminalTokens.TokenNameElidedSemicolonAndRightBrace);
+		SYNTHETIC_SKIP_TOKENS = Collections.unmodifiableList(list);
+
+		list = new ArrayList<>();
+		list.add(TerminalTokens.TokenNamemodule);
+		list.add(TerminalTokens.TokenNamerequires);
+		list.add(TerminalTokens.TokenNameexports);
+		list.add(TerminalTokens.TokenNameto);
+		list.add(TerminalTokens.TokenNameopen);
+		list.add(TerminalTokens.TokenNameopens);
+		list.add(TerminalTokens.TokenNameprovides);
+		list.add(TerminalTokens.TokenNamewith);
+		list.add(TerminalTokens.TokenNametransitive);
+		list.add(TerminalTokens.TokenNameuses);
+		MODULE_TOKENS = Collections.unmodifiableList(list);
+	}
+
+	public PublicScannerTest(String name) {
+		super(name);
+	}
+
+	public static Test suite() {
+		return buildAllCompliancesTestSuite(testClass());
+	}
+
+	public static Class testClass() {
+		return PublicScannerTest.class;
+	}
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.ttValueToName = new TreeMap<>();
+		this.ttNameToValue = new TreeMap<>();
+		this.ttFields = TerminalTokens.class.getFields();
+		for (Field field : this.ttFields) {
+			this.ttValueToName.put(field.getInt(null), field.getName());
+			this.ttNameToValue.put(field.getName(), field.getInt(null));
+		}
+		if(this.ttValueToName.size() != this.ttNameToValue.size()) {
+			this.ttNameToValue.keySet().removeAll(this.ttValueToName.values());
+			fail("TerminalTokens constants use already defined values: " + this.ttNameToValue.keySet());
+		}
+		this.tsValueToName = new TreeMap<>();
+		this.tsNameToValue = new TreeMap<>();
+		this.tsFields = ITerminalSymbols.class.getFields();
+		for (Field field : this.tsFields) {
+			this.tsValueToName.put(field.getInt(null), field.getName());
+			this.tsNameToValue.put(field.getName(), field.getInt(null));
+		}
+		if(this.tsValueToName.size() != this.tsNameToValue.size()) {
+			this.tsNameToValue.keySet().removeAll(this.tsValueToName.values());
+			fail("ITerminalSymbols constants use already defined values: " + this.tsNameToValue.keySet());
+		}
+		this.ps = new MyPublicScanner();
+	}
+
+	/**
+	 * Tests that all constants defined in @link {@link TerminalTokens} are properly handled by {@link PublicScanner#getNextToken()}
+	 */
+	public void testGetNextToken() throws Exception {
+		Set<Entry<String, Integer>> entrySet = this.ttNameToValue.entrySet();
+		for (Entry<String, Integer> entry : entrySet) {
+			this.ps.reset();
+			String fieldName = entry.getKey();
+			Integer fieldValue = entry.getValue();
+			if (MODULE_TOKENS.contains(fieldValue)) {
+				continue;
+			}
+			this.ps.setNextToken(fieldValue);
+			int nextToken = -1;
+			try {
+				nextToken = this.ps.getNextToken();
+			} catch (InvalidInputException e) {
+				fail("Scanner.getNextToken() returns token unknown by PublicScanner: " + fieldName);
+			}
+
+			if(this.tsNameToValue.containsKey(fieldName)) {
+				int actual = ITerminalSymbols.class.getField(fieldName).getInt(null);
+				assertEquals("getNextToken() returns value not specified in ITerminalSymbols for token " + fieldName, actual, nextToken);
+				assertEquals(1, this.ps.nextTokenCalls);
+			} else {
+				Integer value = TerminalTokens.class.getField(fieldName).getInt(null);
+				if(SYNTHETIC_SKIP_TOKENS.contains(value)){
+					assertEquals(2, this.ps.nextTokenCalls);
+				} else {
+					assertEquals(1, this.ps.nextTokenCalls);
+					Integer target = SYNTHETIC_REPLACE_TOKENS.get(value);
+					if(target == null) {
+						fail("TerminalTokens." + fieldName + " should be added to ITerminalSymbols or SYNTHETIC_*_TOKENS in PublicScannerTest*!");
+					} else {
+						String replaceName = this.ttValueToName.get(target);
+						Integer replaceValue = this.tsNameToValue.get(replaceName);
+						assertEquals("getNextToken() returns unexpected value for " + fieldName, nextToken, replaceValue.intValue());
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests that all constants defined in {@link TerminalTokens} are either defined in {@link ITerminalSymbols}
+	 * or defined in {@link #SYNTHETIC_REPLACE_TOKENS}, or {@link #SYNTHETIC_SKIP_TOKENS}, or {@link #MODULE_TOKENS}
+	 * and no constants defined in {@link ITerminalSymbols} are missing in {@link TerminalTokens}
+	 */
+	public void testTokensAndSymbolsSync() throws Exception {
+		Set<Entry<String, Integer>> entrySet = this.tsNameToValue.entrySet();
+		for (Entry<String, Integer> entry : entrySet) {
+			String fieldName = entry.getKey();
+			if (!this.ttNameToValue.containsKey(fieldName)) {
+				fail("ITerminalSymbols." + fieldName + " does not exist in TerminalTokens");
+			}
+		}
+		entrySet = this.ttNameToValue.entrySet();
+		for (Entry<String, Integer> entry : entrySet) {
+			String fieldName = entry.getKey();
+			if(this.tsNameToValue.containsKey(fieldName)) {
+				// OK, constant present
+			} else {
+				Integer value = TerminalTokens.class.getField(fieldName).getInt(null);
+				if(SYNTHETIC_SKIP_TOKENS.contains(value) || MODULE_TOKENS.contains(value)){
+					// OK, constant present
+				} else {
+					Integer target = SYNTHETIC_REPLACE_TOKENS.get(value);
+					if(target != null) {
+						// OK, constant present
+					} else {
+						fail("TerminalTokens." + fieldName + " should be added to ITerminalSymbols or SYNTHETIC_*_TOKENS in PublicScannerTest*!");
+					}
+				}
+			}
+		}
+	}
+
+	class MyPublicScanner extends PublicScanner {
+		MyScanner delegate;
+		int nextTokenCalls;
+		boolean inNextCall;
+
+		public MyPublicScanner() {
+			super(false /* comment */,
+					false /* whitespace */,
+					false /* nls */,
+					ClassFileConstants.JDK17 /* sourceLevel */,
+					ClassFileConstants.JDK17 /* complianceLevel */,
+					null/* taskTag */,
+					null/* taskPriorities */,
+					true /* taskCaseSensitive */,
+					true,
+					true);
+		}
+
+		@Override
+		protected Scanner createScanner(boolean tokenizeComments, boolean tokenizeWhiteSpace,
+				boolean checkNonExternalizedStringLiterals, long sourceLevel, long complianceLevel1, char[][] taskTags,
+				char[][] taskPriorities, boolean isTaskCaseSensitive, boolean isPreviewEnabled) {
+			MyScanner myScanner = new MyScanner(tokenizeComments, tokenizeWhiteSpace, checkNonExternalizedStringLiterals, sourceLevel,
+							complianceLevel1, taskTags, taskPriorities, isTaskCaseSensitive, isPreviewEnabled);
+			this.delegate = myScanner;
+			return myScanner;
+		}
+
+		void setNextToken(int next) {
+			this.delegate.next = next;
+		}
+
+		void reset() {
+			this.delegate.next = -1;
+			this.nextTokenCalls = 0;
+		}
+
+		@Override
+		public int getNextToken() throws InvalidInputException {
+			this.nextTokenCalls ++;
+			if (this.inNextCall) {
+				return this.delegate.next;
+			} else {
+				this.inNextCall = true;
+			}
+			try {
+				return super.getNextToken();
+			} finally {
+				this.inNextCall = false;
+			}
+		}
+	}
+
+
+	class MyScanner extends Scanner {
+		int next;
+		public MyScanner(boolean tokenizeComments, boolean tokenizeWhiteSpace,
+				boolean checkNonExternalizedStringLiterals, long sourceLevel, long complianceLevel, char[][] taskTags,
+				char[][] taskPriorities, boolean isTaskCaseSensitive, boolean isPreviewEnabled) {
+			super(tokenizeComments, tokenizeWhiteSpace, checkNonExternalizedStringLiterals, sourceLevel,
+					complianceLevel, taskTags, taskPriorities, isTaskCaseSensitive, isPreviewEnabled);
+			this.next = -1;
+		}
+
+		@Override
+		public int getNextToken() throws InvalidInputException {
+			return this.next;
+		}
+	}
+
+	/**
+	 * Run this if {@link TerminalTokens} is updated and the test fails - that generates the body of the
+	 * switch in the {@link PublicScanner#getNextToken()}
+	 */
+	public static void main(String[] args) throws Exception {
+		printGeneratedSwitchForPublicScanner();
+	}
+
+	private static void printGeneratedSwitchForPublicScanner() throws Exception {
+		Map<Integer, String> valueToName = new TreeMap<>();
+		Map<String, Integer> nameToValue = new TreeMap<>();
+		Field[] ttFields = TerminalTokens.class.getFields();
+		for (Field field : ttFields) {
+			valueToName.put(field.getInt(null), field.getName());
+			nameToValue.put(field.getName(), field.getInt(null));
+		}
+
+		Field[] tsFields = ITerminalSymbols.class.getFields();
+		Set<String> ttNames = nameToValue.keySet();
+		Set<String> tsSet = Arrays.asList(tsFields).stream().map(x -> x.getName()).collect(Collectors.toSet());
+		StringBuilder sb = new StringBuilder();
+		String ident = "\t\t\t";
+		for (String ttName : ttNames) {
+			if(tsSet.contains(ttName)) {
+				sb.append(ident + "case TerminalTokens." + ttName + " : nextToken = ITerminalSymbols." + ttName + "; break;\n");
+			} else {
+				Integer value = TerminalTokens.class.getField(ttName).getInt(null);
+				if (MODULE_TOKENS.contains(value)) {
+					continue;
+				}
+				if(SYNTHETIC_SKIP_TOKENS.contains(value)){
+					sb.append(ident + "case TerminalTokens." + ttName + " : nextToken = getNextToken(); break;\n");
+				} else {
+					Integer target = SYNTHETIC_REPLACE_TOKENS.get(value);
+					if(target == null) {
+						sb.append("// TODO: add constant " + ttName + " to ITerminalSymbols or update SYNTHETIC_*_TOKENS in PublicScannerTest!\n");
+						sb.append("// case TerminalTokens." + ttName + " : nextToken = ITerminalSymbols." + ttName + "; break;\n");
+					} else {
+						String replaceName = valueToName.get(target);
+						sb.append(ident + "case TerminalTokens." + ttName + " : nextToken = ITerminalSymbols." + replaceName + "; break;\n");
+					}
+				}
+			}
+		}
+		System.out.println(sb);
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ScannerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ScannerTest.java
index 4403508..bb953ea 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ScannerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ScannerTest.java
@@ -13,8 +13,6 @@
  *******************************************************************************/
 package org.eclipse.jdt.core.tests.compiler.regression;
 
-import junit.framework.Test;
-
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.ToolFactory;
 import org.eclipse.jdt.core.compiler.IScanner;
@@ -23,7 +21,8 @@
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
 import org.eclipse.jdt.internal.compiler.parser.Scanner;
 import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
-import org.eclipse.jdt.internal.core.util.PublicScanner;
+
+import junit.framework.Test;
 
 @SuppressWarnings({ "rawtypes" })
 public class ScannerTest extends AbstractRegressionTest {
@@ -198,7 +197,7 @@
 		try {
 			scanner.getNextToken();
 		} catch (InvalidInputException e) {
-			assertEquals("Wrong message", PublicScanner.ILLEGAL_HEXA_LITERAL, e.getMessage());
+			assertEquals("Wrong message", Scanner.ILLEGAL_HEXA_LITERAL, e.getMessage());
 		}
 	}
 
@@ -885,7 +884,7 @@
 		} catch (InvalidInputException e) {
 			buffer.append(scanner.getRawTokenSource());
 			assertEquals("Unexpected contents", "\"a\\u000D\"", String.valueOf(buffer));
-			assertEquals("Wrong exception", PublicScanner.INVALID_CHAR_IN_STRING, e.getMessage());
+			assertEquals("Wrong exception", Scanner.INVALID_CHAR_IN_STRING, e.getMessage());
 		}
 	}
 
@@ -914,7 +913,7 @@
 		} catch (InvalidInputException e) {
 			buffer.append(scanner.getRawTokenSource());
 			assertEquals("Unexpected contents", "\"\\u004Ca\\u000D\"", String.valueOf(buffer));
-			assertEquals("Wrong exception", PublicScanner.INVALID_CHAR_IN_STRING, e.getMessage());
+			assertEquals("Wrong exception", Scanner.INVALID_CHAR_IN_STRING, e.getMessage());
 		}
 	}
 	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=112223
@@ -942,7 +941,7 @@
 		} catch (InvalidInputException e) {
 			buffer.append(scanner.getRawTokenSource());
 			assertEquals("Unexpected contents", "\"\\u004Ca\\u000D\\u0022", String.valueOf(buffer));
-			assertEquals("Wrong exception", PublicScanner.INVALID_CHAR_IN_STRING, e.getMessage());
+			assertEquals("Wrong exception", Scanner.INVALID_CHAR_IN_STRING, e.getMessage());
 		}
 	}
 
@@ -1582,4 +1581,213 @@
 			fail("Should have accepted \\s");
 		}
 	}
+
+	public void testSealed() {
+		char[] source = ("sealed class X { }").toCharArray();
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "17", "17", false);
+		scanner.setSource(source);
+		scanner.resetTo(0, source.length - 1);
+		try {
+			int token = scanner.getNextToken();
+			assertEquals("Wrong token", ITerminalSymbols.TokenNameRestrictedIdentifiersealed, token);
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+	@SuppressWarnings("deprecation")
+	public void testPermits() {
+		char[] source = ("sealed class X permits Y { }").toCharArray();
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "17", "17", false);
+		scanner.setSource(source);
+		scanner.resetTo(0, source.length - 1);
+		try {
+			int token;
+			while ((token = scanner.getNextToken()) != ITerminalSymbols.TokenNameEOF) {
+				switch (token) {
+					case ITerminalSymbols.TokenNameclass:
+					case ITerminalSymbols.TokenNameWHITESPACE:
+					case ITerminalSymbols.TokenNameIdentifier:
+					case ITerminalSymbols.TokenNameRestrictedIdentifiersealed:
+						break;
+					case ITerminalSymbols.TokenNameRestrictedIdentifierpermits:
+						return; // success
+					default:
+						fail("Unexpected token "+token);
+				}
+			}
+			fail("TokenNameRestrictedIdentifierpermits was not detected");
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+	public void testNonSealed() {
+		char[] source = ("non-sealed class X { }").toCharArray();
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "17", "17", false);
+		scanner.setSource(source);
+		scanner.resetTo(0, source.length - 1);
+		try {
+			int token = scanner.getNextToken();
+			assertEquals("Wrong token", ITerminalSymbols.TokenNamenon_sealed, token);
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+	@SuppressWarnings("deprecation")
+	public void testNonSealedNOK() { // insufficient compliance level
+		char[] source = ("non-sealed class X { }").toCharArray();
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "15", "15", false);
+		scanner.setSource(source);
+		scanner.resetTo(0, source.length - 1);
+		try {
+			int token = scanner.getNextToken();
+			assertEquals("Wrong token", ITerminalSymbols.TokenNameIdentifier, token);
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+	@SuppressWarnings("deprecation")
+	public void testRestrictedIdentifierYield() {
+		char[] source = ("class X {\n" +
+				"	int m(int i) {\n" +
+				"		return switch (i) { case 0 -> { yield 13; } default -> 0 }\n" +
+				"	}\n" +
+				"}\n").toCharArray();
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "17", "17", false);
+		scanner.setSource(source);
+		scanner.resetTo(0, source.length - 1);
+		try {
+			int token;
+			while ((token = scanner.getNextToken()) != ITerminalSymbols.TokenNameEOF) {
+				switch (token) {
+					case ITerminalSymbols.TokenNameclass:
+					case ITerminalSymbols.TokenNameWHITESPACE:
+					case ITerminalSymbols.TokenNameLBRACE:
+					case ITerminalSymbols.TokenNameint:
+					case ITerminalSymbols.TokenNameIdentifier:
+					case ITerminalSymbols.TokenNameLPAREN:
+					case ITerminalSymbols.TokenNameRPAREN:
+					case ITerminalSymbols.TokenNamereturn:
+					case ITerminalSymbols.TokenNameswitch:
+					case ITerminalSymbols.TokenNamecase:
+					case ITerminalSymbols.TokenNameIntegerLiteral:
+					case ITerminalSymbols.TokenNameARROW:
+						break;
+					case ITerminalSymbols.TokenNameRestrictedIdentifierYield:
+						return; // success
+					default:
+						fail("Unexpected token "+token);
+				}
+			}
+			fail("TokenNameRestrictedIdentifierYield was not detected");
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+	@SuppressWarnings("deprecation")
+	public void testYieldNOK() { // insufficient context
+		String source = "class X {\n" +
+				"	int m(int i) {\n" +
+				"		return switch (i) { case 0 -> { yield 13; } default -> 0 }\n" +
+				"	}\n" +
+				"}\n";
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "15", "15", false);
+		scanner.setSource(source.toCharArray());
+		scanner.resetTo(source.indexOf("yield")-1, source.length() - 1); // start directly at "yield"
+		try {
+			int token;
+			while ((token = scanner.getNextToken()) != ITerminalSymbols.TokenNameEOF) {
+				switch (token) {
+					case ITerminalSymbols.TokenNameIdentifier:
+					case ITerminalSymbols.TokenNameWHITESPACE:
+					case ITerminalSymbols.TokenNameIntegerLiteral:
+						break;
+					case ITerminalSymbols.TokenNameSEMICOLON: // past the word 'yield'
+						return; // success
+					default:
+						fail("Unexpected token "+token);
+				}
+			}
+			fail("TokenNameSEMICOLON was not detected");
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+	public void testRecord() {
+		char[] source = ("record Point {int x, int y}").toCharArray();
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "16", "16", false);
+		scanner.setSource(source);
+		scanner.resetTo(0, source.length - 1);
+		try {
+			int token = scanner.getNextToken();
+			assertEquals("Wrong token", ITerminalSymbols.TokenNameRestrictedIdentifierrecord, token);
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+	// here the internal scanner could theoretically produce TokenNameAt308, which, however, doesn't happen without an active parser
+	@SuppressWarnings("deprecation")
+	public void testAt308() {
+		char[] source = ("class X<@Marker T> { }").toCharArray();
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "17", "17", false);
+		scanner.setSource(source);
+		scanner.resetTo(0, source.length - 1);
+		try {
+			int token;
+			while ((token = scanner.getNextToken()) != ITerminalSymbols.TokenNameEOF) {
+				switch (token) {
+					case ITerminalSymbols.TokenNameclass:
+					case ITerminalSymbols.TokenNameWHITESPACE:
+					case ITerminalSymbols.TokenNameIdentifier:
+					case ITerminalSymbols.TokenNameLESS:
+						break;
+					case ITerminalSymbols.TokenNameAT:
+						return; // success
+					default:
+						fail("Unexpected token "+token);
+				}
+			}
+			fail("TokenNameAT was not detected");
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
+
+
+	@SuppressWarnings("deprecation")
+	public void testModule() { // insufficient context, all module words are identifiers
+		String source =
+				"open module m1 {\n" +
+				"	requires p1.m2;\n" +
+				"	requires transitive p1.m3;\n" +
+				"	exports p2;\n" +
+				"}\n";
+		IScanner scanner = ToolFactory.createScanner(false, true, false, "9", "9", false);
+		scanner.setSource(source.toCharArray());
+		scanner.resetTo(0, source.length() - 1);
+		try {
+			int token;
+			while ((token = scanner.getNextToken()) != ITerminalSymbols.TokenNameEOF) {
+				switch (token) {
+					case ITerminalSymbols.TokenNameIdentifier:
+					case ITerminalSymbols.TokenNameWHITESPACE:
+					case ITerminalSymbols.TokenNameLBRACE:
+					case ITerminalSymbols.TokenNameRBRACE:
+					case ITerminalSymbols.TokenNameDOT:
+					case ITerminalSymbols.TokenNameSEMICOLON:
+						break;
+					default:
+						fail("Unexpected token "+token);
+				}
+			}
+		} catch (InvalidInputException e) {
+			assertTrue(false);
+		}
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java
index 71464a2..0d72471 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java
@@ -2275,6 +2275,37 @@
 		null,true,
 		new String[]{"-Ddummy"});
 	}
+	public void testbug566155() {
+		// method reference must be compiled as an implicit lambda expression
+		// else it cannot be serialized correctly
+		this.runConformTest(new String[] {
+				"OuterClass.java",
+				"import java.io.*;\n"+
+				"import java.util.function.Supplier;\n"+
+				"\n"+
+				"public class OuterClass implements Serializable {\n"+
+				"\n"+
+				"    private static final long serialVersionUID = 5390565572939096897L;\n"+
+				"    public Supplier<OuterClass.InnerClass> supplier;\n"+
+				"\n"+
+				"    @SuppressWarnings(\"unchecked\")\n"+
+				"    public OuterClass() {\n"+
+				"        this.supplier = (Supplier<OuterClass.InnerClass> & Serializable) InnerClass::new;\n"+
+				"    }\n"+
+				"\n"+
+				"    public class InnerClass implements Serializable {\n"+
+				"        private static final long serialVersionUID = 2478179807896338433L;\n"+
+				" 	   public InnerClass() {\n"+
+				"        }\n"+
+				"    }\n"+
+				"\n"+
+				"}\n"
+		}, "");
+		String expectedOutput =
+			"lambda$1()LOuterClass$InnerClass;\n";
+		String data = printLambdaMethods(OUTPUT_DIR + File.separator + "OuterClass.class");
+		checkExpected(expectedOutput,data);
+	}
 	// ---
 
 	private void checkExpected(String expected, String actual) {
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java
index ff99a82..ed8db86 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java
@@ -67,6 +67,7 @@
 	standardTests.add(ProblemConstructorTest.class);
 	standardTests.add(ProblemTypeAndMethodTest.class);
 	standardTests.add(ScannerTest.class);
+	standardTests.add(PublicScannerTest.class);
 	standardTests.add(SwitchTest.class);
 	standardTests.add(TryStatementTest.class);
 	standardTests.add(UtilTest.class);
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java
index b9f9454..c682c68 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java
@@ -1073,6 +1073,7 @@
     return !file.exists() && getParentChildFile(file) == null;
 }
 public static boolean isMacOS() {
+	// Alternative: "macosx".equals(System.getProperty("osgi.os"))
     return System.getProperty("os.name").indexOf("Mac") != -1;
 }
 /**
diff --git a/org.eclipse.jdt.core.tests.model/pom.xml b/org.eclipse.jdt.core.tests.model/pom.xml
index b9047b8..02e79e0 100644
--- a/org.eclipse.jdt.core.tests.model/pom.xml
+++ b/org.eclipse.jdt.core.tests.model/pom.xml
@@ -34,7 +34,7 @@
       	<artifactId>tycho-surefire-plugin</artifactId>
       	<version>${tycho.version}</version>
       	<configuration>
-      	  <argLine>-Djdt.default.test.compliance=1.8 ${tycho.surefire.argLine}</argLine>
+      	  <argLine>-Xmx1G -Djdt.default.test.compliance=1.8 ${tycho.surefire.argLine}</argLine>
       	  <includes>
       	  	<include>org/eclipse/jdt/core/tests/model/AllJavaModelTests.class</include>
       	  	<include>org/eclipse/jdt/core/tests/dom/RunAllTests.class</include>
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
index 38c524d..7978d04 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
@@ -25,7 +25,11 @@
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
 import java.util.stream.Collectors;
 
 import org.eclipse.core.internal.resources.CharsetDeltaJob;
@@ -525,6 +529,8 @@
 	protected ILogListener logListener;
 	protected ILog log;
 
+	protected static boolean systemConfigReported;
+
 
 	public AbstractJavaModelTests(String name) {
 		super(name);
@@ -3648,7 +3654,14 @@
 			description.setAutoBuilding(false);
 			getWorkspace().setDescription(description);
 		}
+
+		if (!systemConfigReported) {
+			printSystemEnv();
+			systemConfigReported = true;
+		}
+		printMemoryUse();
 	}
+
 	@Override
 	protected void setUp () throws Exception {
 		super.setUp();
@@ -3659,6 +3672,56 @@
 		}
 		logInfo("SETUP " + getName());
 	}
+
+    private static void printSystemEnv() {
+        Set<Entry<String, String>> set = new TreeMap<>(System.getenv()).entrySet();
+        StringBuilder sb = new StringBuilder("\n###################### System environment ######################\n");
+        for (Entry<String, String> entry : set) {
+            sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue()).append("\n");
+        }
+
+        sb.append("\n###################### System properties ######################\n");
+        Set<Entry<String, String>> props = getPropertiesSafe();
+        for (Entry<String, String> entry : props) {
+            sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue()).append("\n");
+        }
+        String env = sb.toString();
+        System.out.println(env);
+        logInfo(env);
+    }
+
+    private static void printMemoryUse() {
+    	System.gc();
+    	System.runFinalization();
+    	System.gc();
+    	System.runFinalization();
+    	long nax = Runtime.getRuntime().maxMemory();
+    	long total = Runtime.getRuntime().totalMemory();
+		long free = Runtime.getRuntime().freeMemory();
+		long used = total - free;
+		System.out.print("\n########### Memory usage reported by JVM ########");
+		System.out.printf(Locale.GERMAN, "%n%,16d bytes max heap", nax);
+		System.out.printf(Locale.GERMAN, "%n%,16d bytes heap allocated", total);
+		System.out.printf(Locale.GERMAN, "%n%,16d bytes free heap", free);
+    	System.out.printf(Locale.GERMAN, "%n%,16d bytes used heap", used);
+    	System.out.println("\n#################################################\n");
+    }
+
+    /**
+     * Retrieves properties safely. In case if someone tries to change the properties set
+     * while iterating over the collection, we repeat the procedure till this
+     * works without an error.
+     */
+    private static Set<Entry<String, String>> getPropertiesSafe() {
+        try {
+            return new TreeMap<>(System.getProperties().entrySet().stream()
+                    .collect(Collectors.toMap(e -> String.valueOf(e.getKey()),
+                            e -> String.valueOf(e.getValue())))).entrySet();
+        } catch (Exception e) {
+            return getPropertiesSafe();
+        }
+    }
+
 	protected void sortElements(IJavaElement[] elements) {
 		Util.Comparer comparer = new Util.Comparer() {
 			public int compare(Object a, Object b) {
@@ -3929,6 +3992,9 @@
 			ILog log = plugin.getLog();
 			Status status = new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, errorMessage, e);
 			log.log(status);
+		} else {
+			System.out.println(errorMessage);
+			e.printStackTrace(System.out);
 		}
 	}
 
@@ -3936,6 +4002,8 @@
 		Plugin plugin = JavaCore.getPlugin();
 		if (plugin != null) {
 			plugin.getLog().log(new Status(IStatus.INFO, JavaCore.PLUGIN_ID, message));
+		} else {
+			System.out.println(message);
 		}
 	}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
index 47aa1e2..3787075 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
@@ -3624,7 +3624,7 @@
 			JavaCore.VERSION_1_4);
 		createFile("/P/lib2.jar", "");
 		startDeltas();
-		if ("macosx".equals(System.getProperty("osgi.os"))) {
+		if (Util.isMacOS()) {
 			// necessary for filesystems with timestamps only upto seconds (eg. Mac)
 			// "lib1.jar" was created above and is modified below.
 			Thread.sleep(2000);
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java
index b0e1172..d61ce7b 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2020 IBM Corporation and others.
+ * 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
@@ -26447,4 +26447,27 @@
 			"A[TYPE_REF]{A, , LA;, null, null, 56}",
 			requestor.getResults());
 }
+/**
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=578815
+ * @throws IOException
+ */
+public void testCompletionConstructorsForSubTypes() throws JavaModelException, IOException {
+	ICompilationUnit cu= getCompilationUnit("Completion", "src", "", "CompletionConstructorsForSubTypes.java");
+	String str = cu.getSource();
+	String completeBehind = "new ";
+	int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+
+	CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, false, false, true, true);
+	requestor.allowAllRequiredProposals();
+	cu.codeComplete(cursorLocation, requestor, new NullProgressMonitor());
+
+	assertEquals(
+			"CompletionConstructorsForSubTypes[CONSTRUCTOR_INVOCATION]{(), LCompletionConstructorsForSubTypes;, ()V, CompletionConstructorsForSubTypes, null, 55}\n" +
+			"   CompletionConstructorsForSubTypes[TYPE_REF]{CompletionConstructorsForSubTypes, , LCompletionConstructorsForSubTypes;, null, null, 55}\n" +
+			"CompletionConstructorsForSubTypes2[CONSTRUCTOR_INVOCATION]{(), LCompletionConstructorsForSubTypes2;, ()V, CompletionConstructorsForSubTypes2, null, 75}\n" +
+			"   CompletionConstructorsForSubTypes2[TYPE_REF]{CompletionConstructorsForSubTypes2, , LCompletionConstructorsForSubTypes2;, null, null, 75}\n" +
+			"CompletionConstructorsForSubTypes1[ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION]{(), LCompletionConstructorsForSubTypes1;, ()V, CompletionConstructorsForSubTypes1, null, 82}\n" +
+			"   CompletionConstructorsForSubTypes1[TYPE_REF]{CompletionConstructorsForSubTypes1, , LCompletionConstructorsForSubTypes1;, null, null, 82}",
+			requestor.getResults());
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
index d1d56c5..d4143d5 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
@@ -1159,7 +1159,7 @@
 	String completeBehind = "p.get";
 	int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
-	assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [186, 189], " + (R_DEFAULT + R_EXPECTED_TYPE + 30) + "}\n" +
+	assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [186, 189], " + (R_DEFAULT + R_PACKAGE_EXPECTED_TYPE + 30) + "}\n" +
                   "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [186, 189], " + (R_DEFAULT + R_EXACT_EXPECTED_TYPE + 30) + "}", requestor.getResults());
 }
 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735,  [1.8][assist] Missing completion proposals inside lambda body expression - other than first token
@@ -1913,7 +1913,7 @@
 	String completeBehind = "so.tr";
 	int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
-	assertResults("trim[METHOD_REF]{trim(), Ljava.lang.String;, ()Ljava.lang.String;, null, null, trim, null, [237, 239], " + (R_DEFAULT + R_EXPECTED_TYPE + 30) + "}", requestor.getResults());
+	assertResults("trim[METHOD_REF]{trim(), Ljava.lang.String;, ()Ljava.lang.String;, null, null, trim, null, [237, 239], " + (R_DEFAULT + R_PACKAGE_EXPECTED_TYPE + 30) + "}", requestor.getResults());
 }
 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435682, [1.8] content assist not working inside lambda expression
 public void test435682a() throws JavaModelException {
@@ -6262,9 +6262,9 @@
 			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 55}\n"
 			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 55}\n"
 			+ "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 80}\n"
-			+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 80}\n"
 			+ "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 80}\n"
-			+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 80}\n"
+			+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 85}\n"
+			+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 85}\n"
 			+ "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 90}",
 			result);
 }
@@ -6297,11 +6297,11 @@
 			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 55}\n"
 			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 55}\n"
 			+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 55}\n"
-			+ "boo[METHOD_REF]{boo(), LBug577883$Int;, ()Ljava.lang.Integer;, boo, null, 80}\n"
 			+ "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 80}\n"
-			+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 80}\n"
 			+ "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 80}\n"
-			+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 80}\n"
+			+ "boo[METHOD_REF]{boo(), LBug577883$Int;, ()Ljava.lang.Integer;, boo, null, 85}\n"
+			+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 85}\n"
+			+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 85}\n"
 			+ "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 90}",
 			result);
 }
@@ -6483,11 +6483,11 @@
 	int cursorLocation = str.indexOf(completeBehind) + completeBehind.length();
 	this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, new NullProgressMonitor());
 	String result = requestor.getResults();
-	assertResults("LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, ()V, LinkedHashMap, null, 76}\n"
-			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (I)V, LinkedHashMap, (arg0), 76}\n"
-			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (IF)V, LinkedHashMap, (arg0, arg1), 76}\n"
-			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (IFZ)V, LinkedHashMap, (arg0, arg1, arg2), 76}\n"
-			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (Ljava.util.Map<+TK;+TV;>;)V, LinkedHashMap, (arg0), 76}", result);
+	assertResults("LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, ()V, LinkedHashMap, null, 81}\n"
+			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (I)V, LinkedHashMap, (arg0), 81}\n"
+			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (IF)V, LinkedHashMap, (arg0, arg1), 81}\n"
+			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (IFZ)V, LinkedHashMap, (arg0, arg1, arg2), 81}\n"
+			+ "LinkedHashMap[CONSTRUCTOR_INVOCATION]{(), Ljava.util.LinkedHashMap;, (Ljava.util.Map<+TK;+TV;>;)V, LinkedHashMap, (arg0), 81}", result);
 
 	requestor = new CompletionTestsRequestor2(true);
 	requestor.setAllowsRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF, true);
@@ -6499,4 +6499,29 @@
 	result = requestor.getResults();
 	assertResults("", result);
 }
+	public void testBug564875() throws JavaModelException {
+		this.workingCopies = new ICompilationUnit[1];
+		this.workingCopies[0] = getWorkingCopy(
+				"/Completion/src/X.java",
+				"import java.util.List;\n" +
+				"class Person {\n" +
+				"   String getLastName() { return null; }\n" +
+				"   Person getLastPerson() { return null; }\n" +
+				"}\n" +
+				"public class X {\n" +
+				"	void test1 (List<Person> people) {\n" +
+				"		people.stream().forEach(p -> System.out.println(p.get)); \n" +
+				"	}\n" +
+				"}\n");
+
+		CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+		requestor.allowAllRequiredProposals();
+		String str = this.workingCopies[0].getSource();
+		String completeBehind = "p.get";
+		int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+		this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+		assertResults("getLastPerson[METHOD_REF]{getLastPerson(), LPerson;, ()LPerson;, null, null, getLastPerson, null, [229, 232], "+ (R_DEFAULT+R_EXPECTED_TYPE+30)+"}\n"
+				+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [229, 232], "+(R_DEFAULT+R_PACKAGE_EXPECTED_TYPE+30)+"}\n"
+				+ "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [229, 232], "+(R_DEFAULT+R_EXACT_EXPECTED_TYPE+30)+"}", requestor.getResults());
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java
index 44b9319..0609941 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java
@@ -7617,7 +7617,7 @@
 				result.proposals);
     } else {
     	assertResults(
-				"Test<ZT>[TYPE_REF]{Test, test0232, Ltest0232.Test<TZT;>;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + R_EXPECTED_TYPE) + "}",
+				"Test<ZT>[TYPE_REF]{Test, test0232, Ltest0232.Test<TZT;>;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + R_PACKAGE_EXPECTED_TYPE) + "}",
 				result.proposals);
     }
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
index be6e238..c20f98b 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
@@ -19,6 +19,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -60,6 +61,7 @@
 import org.eclipse.jdt.core.dom.NodeFinder;
 import org.eclipse.jdt.core.dom.SimpleName;
 import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.tests.model.ContainerInitializer.ITestInitializer;
 import org.eclipse.jdt.core.tests.util.AbstractCompilerTest;
 import org.eclipse.jdt.core.tests.util.Util;
 import org.eclipse.jdt.core.util.ExternalAnnotationUtil;
@@ -119,6 +121,76 @@
 		}
 	}
 
+	/**
+	 * Initializer for a container that may provide a mix of entries, some of which are "self-annotating",
+	 * i.e., contain .eea files for their own classes.
+	 */
+	static class TestCustomContainerInitializer implements ContainerInitializer.ITestInitializer {
+
+		/** Use this container name in test projects. */
+		private static final String CONTAINER_NAME = "org.eclipse.jdt.core.tests.model.TEST_CONTAINER";
+
+		List<String> allEntries;
+		Map<String,String> elementAnnotationPaths;
+
+		/**
+		 * @param elementsAndAnnotationPaths each pair of entries in this array defines one classpath entry:
+		 * <ul>
+		 * 	<li>1st string specifies the path,
+		 *  <li>if 2nd string is "self" than the entry is "self-annotating".
+		 *  	 {@code null} is a legal value to signal "not self-annotating"
+		 *  </ul>
+		 */
+		public TestCustomContainerInitializer(String... elementsAndAnnotationPaths) {
+			this.allEntries = new ArrayList<>();
+			this.elementAnnotationPaths = new HashMap<>();
+			for (int i = 0; i < elementsAndAnnotationPaths.length; i+=2) {
+				String entryPath = elementsAndAnnotationPaths[i];
+				this.allEntries.add(entryPath);
+				String annotsPath = elementsAndAnnotationPaths[i+1];
+				if ("self".equals(annotsPath))
+					this.elementAnnotationPaths.put(entryPath, entryPath);
+			}
+		}
+
+		static class TestContainer implements IClasspathContainer {
+			IPath path;
+			IClasspathEntry[] entries;
+			TestContainer(IPath path, IClasspathEntry[] entries){
+				this.path = path;
+				this.entries = entries;
+			}
+			public IPath getPath() { return this.path; }
+			public IClasspathEntry[] getClasspathEntries() { return this.entries;	}
+			public String getDescription() { return this.path.toString(); 	}
+			public int getKind() { return 0; }
+		}
+
+		@Override
+		public void initialize(IPath containerPath, IJavaProject project) throws CoreException {
+			List<IClasspathEntry> entries = new ArrayList<>();
+			for (String entryPath : this.allEntries) {
+				IClasspathAttribute[] extraAttributes;
+				String elementAnnotationPath = this.elementAnnotationPaths.get(entryPath);
+				if (elementAnnotationPath != null)
+					extraAttributes = externalAnnotationExtraAttributes(elementAnnotationPath);
+				else
+					extraAttributes = ClasspathEntry.NO_EXTRA_ATTRIBUTES;
+				entries.add(JavaCore.newLibraryEntry(new Path(entryPath), null, null,
+						ClasspathEntry.NO_ACCESS_RULES, extraAttributes, false/*not exported*/));
+			}
+			JavaCore.setClasspathContainer(
+					new Path(CONTAINER_NAME),
+					new IJavaProject[]{ project },
+					new IClasspathContainer[] { new TestContainer(new Path(CONTAINER_NAME), entries.toArray(IClasspathEntry[]::new)) },
+					null);
+		}
+		@Override
+		public boolean allowFailureContainer() {
+			return false;
+		}
+	}
+
 	static class LogListener implements ILogListener {
     	List<IStatus> loggedStatus = new ArrayList<>();
         public void logging(IStatus status, String plugin) {
@@ -3108,4 +3180,133 @@
 				7, 11
 			});
 	}
+
+	public void testMixedElementAndContainerAnnotation() throws Exception {
+		myCreateJavaProject("PrjTest");
+		String projectLoc = this.project.getProject().getLocation().toString();
+		ITestInitializer prev = ContainerInitializer.initializer;
+		ContainerInitializer.setInitializer(new TestCustomContainerInitializer(projectLoc+"/lib1.jar", "self", projectLoc+"/lib2.jar", null));
+		try {
+
+			// jar with external annotations for its own class
+			Util.createJar(new String[] {
+					"lib/pgen/CGen.java",
+					"package lib.pgen;\n" +
+					"public class CGen {\n" +
+					"	public String get(String in) { return in; }\n" +
+					"}\n"
+				},
+				new String[] {
+					"lib/pgen/CGen.eea",
+					"class lib/pgen/CGen\n" +
+					"\n" +
+					"get\n" +
+					" (Ljava/lang/String;)Ljava/lang/String;\n" +
+					" (L1java/lang/String;)L1java/lang/String;\n",
+				},
+				projectLoc+"/lib1.jar",
+				"1.8");
+			Util.createJar(new String[] {
+					"lib2/C2.java",
+					"package lib2;\n" +
+					"public class C2 {\n" +
+					"	public String get2(Exception in) { return in.toString(); }\n" +
+					"}\n"
+				},
+				projectLoc+"/lib2.jar",
+				"1.8");
+			IClasspathEntry containerEntry = JavaCore.newContainerEntry(
+					new Path("org.eclipse.jdt.core.tests.model.TEST_CONTAINER"),
+					null,
+					new IClasspathAttribute[] {
+							JavaCore.newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, "/PrjTest/annots")
+					},
+					false);
+			addClasspathEntry(this.project, containerEntry);
+
+			createFileInProject("annots/lib2", "C2.eea",
+					"class lib2/C2\n" +
+					"\n" +
+					"get2\n" +
+					" (Ljava/lang/Exception;)Ljava/lang/String;\n" +
+					" (L1java/lang/Exception;)L1java/lang/String;\n");
+
+			createFileInProject("src/p", "Use.java",
+					"package p;\n" +
+					"import lib.pgen.CGen;\n" +
+					"import lib2.C2;\n" +
+					"import org.eclipse.jdt.annotation.NonNull;\n" +
+					"public class Use {\n" +
+					"	public @NonNull String test(CGen c) {\n" +
+					"		String s = c.get(null);\n" + // problem here (7)
+					"		return s;\n" + // no problem here
+					"	}\n" +
+					"	public @NonNull String test2(C2 c) {\n" +
+					"		String s = c.get2(null);\n" + // problem here (11)
+					"		return s;\n" + // no problem here
+					"	}\n" +
+					"}\n");
+
+
+			this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
+			IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
+			sortMarkers(markers);
+			assertMarkers("Unexpected markers",
+					"Null type mismatch: required '@NonNull Exception' but the provided value is null\n" +
+					"Null type mismatch: required '@NonNull String' but the provided value is null",
+					markers);
+
+			ICompilationUnit unit = JavaCore.createCompilationUnitFrom(this.project.getProject().getFile("src/p/Use.java")).getWorkingCopy(null);
+			CompilationUnit reconciled = unit.reconcile(getJLS8(), true, null, new NullProgressMonitor());
+			IProblem[] problems = reconciled.getProblems();
+			assertProblems(problems,
+				new String[] {
+					"Pb(910) Null type mismatch: required '@NonNull String' but the provided value is null",
+					"Pb(910) Null type mismatch: required '@NonNull Exception' but the provided value is null"
+				},
+				new int[] {
+					7, 11
+				});
+		} finally {
+			ContainerInitializer.setInitializer(prev);
+		}
+	}
+	public void testAnnotatedSourceSharesOutputFolder() throws CoreException {
+		IJavaProject prj1 = null;
+		myCreateJavaProject("Prj1");
+		addSourceFolderWithExternalAnnotations(this.project, "/Prj1/src-gen", null, "/Prj1/annot-gen");
+		prj1 = this.project;
+		try {
+			createFileInProject("annot-gen/pgen", "CGen.eea",
+					"class pgen/CGen\n" +
+					"\n" +
+					"get\n" +
+					" (Ljava/lang/String;)Ljava/lang/String;\n" +
+					" (L1java/lang/String;)L1java/lang/String;\n");
+
+			createFileInProject("src-gen/pgen", "CGen.java",
+					"package pgen;\n" +
+					"public class CGen {\n" +
+					"	public String get(String in) { return in; }\n" +
+					"}\n");
+			this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
+
+			createFileInProject("src/pgen", "CImpl.java",
+					"package pgen;\n" +
+					"import org.eclipse.jdt.annotation.*;\n" +
+					"public class CImpl extends CGen {\n" +
+					"	public @NonNull String get(@NonNull String in) { return in; }\n" +
+					"}\n");
+			ICompilationUnit unit = JavaCore.createCompilationUnitFrom(this.project.getProject().getFile("src/pgen/CImpl.java")).getWorkingCopy(null);
+			CompilationUnit reconciled = unit.reconcile(AST.getJLSLatest(), true, null, new NullProgressMonitor());
+			IProblem[] problems = reconciled.getProblems();
+			assertNoProblems(problems);
+			this.project.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null);
+			IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
+			assertNoMarkers(markers);
+		} finally {
+			if (prj1 != null)
+				prj1.getProject().delete(true, true , null);
+		}
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests_1_5.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests_1_5.java
index b2b8f99..f814807 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests_1_5.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests_1_5.java
@@ -3002,4 +3002,53 @@
 		deleteProject("P0125");
 	}
 }
+
+public void testBrokenSwitch0() throws JavaModelException {
+	ICompilationUnit cu = getWorkingCopy("/Resolve/src/Test.java",
+			"interface ILog {\n" +
+			"	void log(String status);\n" +
+			"}\n" +
+			"public class Test {\n" +
+			"    private static ILog test() {\n" +
+			"        return new ILog() {\n" +
+			"            @Override\n" +
+			"            public void log(String status) {\n" +
+			"                switch (status.length()) { // here\n" +
+			"                case\n" +
+			"                }\n" +
+			"            }\n" +
+			"        };\n" +
+			"    }\n" +
+			"}\n");
+	IJavaElement[] elements = codeSelect(cu, "length()", "length");
+	assertElementsEqual(
+				"Unexpected elements",
+				"length() [in String [in String.class [in java.lang [in "+ getExternalJCLPathString("1.5") + "]]]]",
+				elements);
+}
+
+public void testBrokenSwitch1() throws JavaModelException {
+	ICompilationUnit cu = getWorkingCopy("/Resolve/src/Test.java",
+			"interface ILog {\n" +
+			"	void log(String status);\n" +
+			"}\n" +
+			"public class Test {\n" +
+			"    private static ILog test() {\n" +
+			"        return new ILog() {\n" +
+			"            @Override\n" +
+			"            public void log(String status) {\n" +
+			"                Boolean severity = Boolean.FALSE;" +
+			"                switch (status.length()) { // here\n" +
+			"                case\n" +
+			"                }\n" +
+			"            }\n" +
+			"        };\n" +
+			"    }\n" +
+			"}\n");
+	IJavaElement[] elements = codeSelect(cu, "length()", "length");
+	assertElementsEqual(
+				"Unexpected elements",
+				"length() [in String [in String.class [in java.lang [in "+ getExternalJCLPathString("1.5") + "]]]]",
+				elements);
+}
 }
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes.java b/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes.java
new file mode 100644
index 0000000..7389c9d
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes.java
@@ -0,0 +1,5 @@
+public class CompletionConstructorsForSubTypes {
+	public void test() {
+		CompletionConstructorsForSubTypes1 i = new 
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes1.java b/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes1.java
new file mode 100644
index 0000000..51e5fa4
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes1.java
@@ -0,0 +1,2 @@
+public interface CompletionConstructorsForSubTypes1 {
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes2.java b/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes2.java
new file mode 100644
index 0000000..6dc4d9b
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/workspace/Completion/src/CompletionConstructorsForSubTypes2.java
@@ -0,0 +1,2 @@
+public class CompletionConstructorsForSubTypes2 implements CompletionConstructorsForSubTypes1 {
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/.settings/.api_filters b/org.eclipse.jdt.core/.settings/.api_filters
index 905fdaa..00fbc54 100644
--- a/org.eclipse.jdt.core/.settings/.api_filters
+++ b/org.eclipse.jdt.core/.settings/.api_filters
@@ -8,8 +8,6 @@
                 <message_argument value="16"/>
             </message_arguments>
         </filter>
-    </resource>
-    <resource path="dom/org/eclipse/jdt/core/dom/AST.java" type="org.eclipse.jdt.core.dom.AST">
         <filter comment="For new java version" id="388194388">
             <message_arguments>
                 <message_argument value="org.eclipse.jdt.core.dom.AST"/>
@@ -18,20 +16,6 @@
             </message_arguments>
         </filter>
     </resource>
-    <resource path="dom/org/eclipse/jdt/core/dom/AbstractTagElement.java" type="org.eclipse.jdt.core.dom.AbstractTagElement">
-        <filter id="576725006">
-            <message_arguments>
-                <message_argument value="IDocElement"/>
-                <message_argument value="TagProperty"/>
-            </message_arguments>
-        </filter>
-        <filter id="576778288">
-            <message_arguments>
-                <message_argument value="ASTNode"/>
-                <message_argument value="TagProperty"/>
-            </message_arguments>
-        </filter>
-    </resource>
     <resource path="dom/org/eclipse/jdt/core/dom/ASTNode.java" type="org.eclipse.jdt.core.dom.ASTNode">
         <filter comment="adjust constants after additions in JDT" id="388194388">
             <message_arguments>
@@ -146,6 +130,20 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="dom/org/eclipse/jdt/core/dom/AbstractTagElement.java" type="org.eclipse.jdt.core.dom.AbstractTagElement">
+        <filter id="576725006">
+            <message_arguments>
+                <message_argument value="IDocElement"/>
+                <message_argument value="TagProperty"/>
+            </message_arguments>
+        </filter>
+        <filter id="576778288">
+            <message_arguments>
+                <message_argument value="ASTNode"/>
+                <message_argument value="TagProperty"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="formatter/org/eclipse/jdt/core/formatter/CodeFormatter.java" type="org.eclipse.jdt.core.formatter.CodeFormatter">
         <filter comment="adjust constants after additions in JDT" id="388194388">
             <message_arguments>
@@ -155,4 +153,18 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java" type="org.eclipse.jdt.core.compiler.ITerminalSymbols">
+        <filter comment="token 'readonly' has long been deprecated as it is not supported" id="405864542">
+            <message_arguments>
+                <message_argument value="org.eclipse.jdt.core.compiler.ITerminalSymbols"/>
+                <message_argument value="TokenNamereadonly"/>
+            </message_arguments>
+        </filter>
+        <filter comment="result was never detected as a token by the scanner" id="405864542">
+            <message_arguments>
+                <message_argument value="org.eclipse.jdt.core.compiler.ITerminalSymbols"/>
+                <message_argument value="TokenNameresult"/>
+            </message_arguments>
+        </filter>
+    </resource>
 </component>
diff --git a/org.eclipse.jdt.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.core/META-INF/MANIFEST.MF
index 7b46414..f03eb03 100644
--- a/org.eclipse.jdt.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.core/META-INF/MANIFEST.MF
@@ -78,7 +78,7 @@
  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.internal.core.util;x-internal:=true
-Require-Bundle: org.eclipse.core.resources;bundle-version="[3.14.0,4.0.0)",
+Require-Bundle: org.eclipse.core.resources;bundle-version="[3.17.0,4.0.0)",
  org.eclipse.core.runtime;bundle-version="[3.13.0,4.0.0)",
  org.eclipse.core.filesystem;bundle-version="[1.7.0,2.0.0)",
  org.eclipse.text;bundle-version="[3.6.0,4.0.0)",
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
index b44aeb5..590ea04 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
@@ -19,6 +19,7 @@
  *     Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
  *     Microsoft Corporation - Contribution for bug 575562 - improve completion search performance
  *     							Bug 578817 - skip the ignored types before creating CompletionProposals
+ *     Microsoft Corporation - Contribution for bug 578815 - more candidates when completion after new keyword for an interface
  *******************************************************************************/
 package org.eclipse.jdt.internal.codeassist;
 
@@ -5097,12 +5098,16 @@
 				if((this.expectedTypesFilter & SUBTYPE) != 0
 						&& (proposalType.erasure().isCompatibleWith(this.expectedTypes[i].erasure()))) {
 
-					if(CharOperation.equals(this.expectedTypes[i].qualifiedPackageName(), proposalType.qualifiedPackageName()) &&
-							CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), proposalType.qualifiedSourceName())) {
-						return R_EXACT_EXPECTED_TYPE;
+					if(CharOperation.equals(this.expectedTypes[i].qualifiedPackageName(), proposalType.qualifiedPackageName()) ) {
+						if(CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), proposalType.qualifiedSourceName()))
+								return R_EXACT_EXPECTED_TYPE;
+						if(!CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), proposalType.qualifiedSourceName()) &&
+								proposalType.qualifiedPackageName().length !=0)
+							return R_PACKAGE_EXPECTED_TYPE;
 					}
 
 					relevance = R_EXPECTED_TYPE;
+
 				}
 				if((this.expectedTypesFilter & SUPERTYPE) != 0
 						&& this.expectedTypes[i].isCompatibleWith(proposalType)) {
@@ -12474,6 +12479,8 @@
 		if (isEmptyPrefix && !this.assistNodeIsAnnotation) {
 			if (!proposeConstructor) {
 				findTypesFromExpectedTypes(token, scope, typesFound, proposeType, proposeConstructor);
+			} else {
+				findConstructorsFromSubTypes(scope, typesFound);
 			}
 		} else {
 			if(!isEmptyPrefix && !this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
@@ -12556,6 +12563,144 @@
 		}
 	}
 
+	/**
+	 * Find all the constructors of the expected types' sub types.
+	 */
+	private void findConstructorsFromSubTypes(Scope scope, ObjectVector typesFound) {
+		SearchPattern pattern = null;
+		for (TypeBinding typeBinding : this.expectedTypes) {
+			if (typeBinding == null || !typeBinding.isInterface()) {
+				continue;
+			}
+			if (pattern == null) {
+				pattern = SearchPattern.createPattern(String.valueOf(typeBinding.qualifiedPackageName()) +
+						"." + String.valueOf(typeBinding.qualifiedSourceName()), //$NON-NLS-1$
+						IJavaSearchConstants.CLASS, IJavaSearchConstants.IMPLEMENTORS,
+						SearchPattern.R_EXACT_MATCH);
+			} else {
+				pattern = SearchPattern.createOrPattern(pattern, SearchPattern.createPattern(String.valueOf(
+						typeBinding.qualifiedPackageName()) + "." + String.valueOf(typeBinding.qualifiedSourceName()), //$NON-NLS-1$
+						IJavaSearchConstants.CLASS, IJavaSearchConstants.IMPLEMENTORS,
+						SearchPattern.R_EXACT_MATCH));
+			}
+		}
+		if (pattern == null) {
+			return;
+		}
+
+		List<IType> types = new ArrayList<>();
+		IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(new IJavaElement[] {this.javaProject});
+		SearchRequestor searchRequstor = new SearchRequestor() {
+			@Override
+			public void acceptSearchMatch(SearchMatch match) {
+				Object element = match.getElement();
+				if (!(element instanceof IType)) {
+					return;
+				}
+				types.add((IType) element);
+			}
+		};
+		try {
+			new SearchEngine().search(pattern,
+					new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
+					searchScope,
+					searchRequstor,
+					null
+			);
+		} catch (CoreException e) {
+			if(DEBUG) {
+				System.out.println("Exception caught by CompletionEngine:"); //$NON-NLS-1$
+				e.printStackTrace(System.out);
+			}
+		}
+
+		checkCancel();
+
+		for (IType type : types) {
+			ReferenceBinding typeBinding = this.lookupEnvironment.getType(CharOperation.splitOn('.', type.getFullyQualifiedName().toCharArray()));
+			if (typeBinding == null) {
+				continue;
+			}
+
+			int accessibility = IAccessRule.K_ACCESSIBLE;
+			if(typeBinding.hasRestrictedAccess()) {
+				AccessRestriction accessRestriction = this.lookupEnvironment.getAccessRestriction(typeBinding);
+				if(accessRestriction != null) {
+					switch (accessRestriction.getProblemId()) {
+						case IProblem.ForbiddenReference:
+							if (this.options.checkForbiddenReference) {
+								continue;
+							}
+							accessibility = IAccessRule.K_NON_ACCESSIBLE;
+							break;
+						case IProblem.DiscouragedReference:
+							if (this.options.checkDiscouragedReference) {
+								continue;
+							}
+							accessibility = IAccessRule.K_DISCOURAGED;
+							break;
+					}
+				}
+			}
+
+			if (typesFound.contains(typeBinding)) continue;
+
+			typesFound.add(typeBinding);
+
+			if (this.assistNodeIsExtendedType && typeBinding.isFinal()) continue;
+			if (this.assistNodeIsInterfaceExcludingAnnotation && typeBinding.isAnnotationType()) continue;
+			if(this.assistNodeIsClass) {
+				if(!typeBinding.isClass()) continue;
+			} else if(this.assistNodeIsInterface) {
+				if(!typeBinding.isInterface() && !typeBinding.isAnnotationType()) continue;
+			} else if (this.assistNodeIsAnnotation) {
+				if(!typeBinding.isAnnotationType()) continue;
+			}
+
+			int relevance = computeBaseRelevance();
+			relevance += computeRelevanceForResolution();
+			relevance += computeRelevanceForInterestingProposal(typeBinding);
+			relevance += R_EXACT_EXPECTED_TYPE;	// all found types are sub-types
+			relevance += computeRelevanceForQualification(false);
+			relevance += computeRelevanceForRestrictions(accessibility);
+
+			if (typeBinding.isAnnotationType()) {
+				relevance += computeRelevanceForAnnotation();
+				relevance += computeRelevanceForAnnotationTarget(typeBinding);
+			} else if (typeBinding.isInterface()) {
+				relevance += computeRelevanceForInterface();
+			} else if(typeBinding.isClass()){
+				relevance += computeRelevanceForClass();
+				relevance += computeRelevanceForException(typeBinding.sourceName);
+			}
+
+			if (!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
+				boolean isQualified = false;
+				if (!this.insideQualifiedReference && !typeBinding.isMemberType()) {
+					char[] packageName = typeBinding.qualifiedPackageName();
+					char[] typeName = typeBinding.sourceName();
+					if (mustQualifyType(packageName, typeName, null, typeBinding.modifiers)) {
+						isQualified = true;
+					}
+				}
+				findConstructors(
+					typeBinding,
+					null,
+					scope,
+					FakeInvocationSite,
+					false,
+					null,
+					null,
+					null,
+					false,
+					false,
+					isQualified,
+					relevance
+				);
+			}
+		}
+	}
+
 	private int getTypesMatchRule() {
 		int matchRule = SearchPattern.R_PREFIX_MATCH;
 		if (this.options.camelCaseMatch)
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java
index 6e43a84..9ef6f05 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java
@@ -31,6 +31,7 @@
 	int R_EXACT_NAME = 4;
 	int R_VOID = -5;
 	int R_EXPECTED_TYPE = 20;
+	int R_PACKAGE_EXPECTED_TYPE = 25;
 	int R_EXACT_EXPECTED_TYPE = 30;
 	int R_INTERFACE = 20;
 	int R_CLASS = 20;
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
index c809b6c..4cce843 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
@@ -173,14 +173,14 @@
 		int info = topKnownElementInfo(SELECTION_OR_ASSIST_PARSER);
 		nextElement : switch (kind) {
 			case K_BETWEEN_CASE_AND_COLONORARROW :
-				if(this.expressionPtr > 0) {
+				if(this.expressionPtr >= info && info > -1) {
 					SwitchStatement switchStatement = new SwitchStatement();
-					switchStatement.expression = this.expressionStack[this.expressionPtr - 1];
+					switchStatement.expression = this.expressionStack[info]; // info is pointer to top expr when encountering 'case'
 					if(this.astLengthPtr > -1 && this.astPtr > -1) {
 						int length = this.astLengthStack[this.astLengthPtr];
 						int newAstPtr = this.astPtr - length;
 						ASTNode firstNode = this.astStack[newAstPtr + 1];
-						if(length != 0 && firstNode.sourceStart > switchStatement.expression.sourceEnd) {
+						if(length != 0 && firstNode instanceof Statement && firstNode.sourceStart > switchStatement.expression.sourceEnd) {
 							switchStatement.statements = new Statement[length + 1];
 							System.arraycopy(
 								this.astStack,
@@ -1361,7 +1361,7 @@
 	if (isInsideMethod() || isInsideFieldInitialization()) {
 		switch (token) {
 			case TokenNamecase :
-				pushOnElementStack(K_BETWEEN_CASE_AND_COLONORARROW);
+				pushOnElementStack(K_BETWEEN_CASE_AND_COLONORARROW, this.expressionPtr);
 				break;
 			case TokenNameCOMMA :
 				switch (topKnownElementKind(SELECTION_OR_ASSIST_PARSER)) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
index f355226..2a75295 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
@@ -27,6 +27,7 @@
  *								Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param
  *     Jesper S Moller <jesper@selskabet.org> - Contributions for
  *								bug 378674 - "The method can be declared as static" is wrong
+ *                              bug 413873 - Warning "Method can be static" on method referencing a non-static inner class
  *******************************************************************************/
 package org.eclipse.jdt.internal.compiler.ast;
 
@@ -48,6 +49,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
 import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
@@ -163,7 +165,17 @@
 			// nullity and mark as assigned
 			analyseArguments(classScope.environment(), flowInfo, this.arguments, this.binding);
 
-			if (this.binding.declaringClass instanceof MemberTypeBinding && !this.binding.declaringClass.isStatic()) {
+			boolean hasMemberTypeParameter = false;
+			if (this.binding.parameters != null && this.arguments != null) {
+				int length = Math.min(this.binding.parameters.length, this.arguments.length);
+				for (int i = 0; i < length; i++) {
+					if (this.binding.parameters[i] instanceof ParameterizedTypeBinding) {
+						hasMemberTypeParameter = true;
+						break;
+					}
+				}
+			}
+			if (this.binding.declaringClass instanceof MemberTypeBinding && !this.binding.declaringClass.isStatic() || hasMemberTypeParameter) {
 				// method of a non-static member type can't be static.
 				this.bits &= ~ASTNode.CanBeStatic;
 			}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index 1dee15b..aaee28e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -277,7 +277,7 @@
 	private boolean shouldGenerateImplicitLambda(BlockScope currentScope) {
 		// these cases are either too complicated, impossible to handle or result in significant code duplication
 		return (this.binding.isVarargs() ||
-				(isConstructorReference() && this.receiverType.syntheticOuterLocalVariables() != null && this.shouldCaptureInstance) ||
+				(isConstructorReference() && (this.receiverType.syntheticOuterLocalVariables() != null || this.shouldCaptureInstance)) ||
 				this.requiresBridges() || // bridges.
 				!isDirectCodeGenPossible());
 		// To fix: We should opt for direct code generation wherever possible.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java
index 6651738..5a2082e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java
@@ -160,11 +160,11 @@
 }
 
 public int forwardReferenceCount() {
-	if (this.delegate != null) this.delegate.forwardReferenceCount();
+	if (this.delegate != null) return this.delegate.forwardReferenceCount();
 	return this.forwardReferenceCount;
 }
 public int[] forwardReferences() {
-	if (this.delegate != null) this.delegate.forwardReferences();
+	if (this.delegate != null) return this.delegate.forwardReferences();
 	return this.forwardReferences;
 }
 public void initialize(CodeStream stream) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
index 6ffd4e8..c6e0349 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
@@ -1618,6 +1618,11 @@
 		updateScanContext(token);
 	}
 	if (this.activeParser == null) { // anybody interested in the grammatical structure of the program should have registered.
+		if (token != TokenNameWHITESPACE) {
+			this.lookBack[0] = this.lookBack[1];
+			this.lookBack[1] = token;
+			this.multiCaseLabelComma = false;
+		}
 		return token;
 	}
 	if (token == TokenNameLPAREN || token == TokenNameLESS || token == TokenNameAT || token == TokenNameARROW) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java
index 1e573c7..78e8b37 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java
@@ -449,8 +449,9 @@
 				complianceLevelValue,
 				null/*taskTags*/,
 				null/*taskPriorities*/,
-				true/*taskCaseSensitive*/);
-		scanner.recordLineSeparator = recordLineSeparator;
+				true/*taskCaseSensitive*/,
+				true/*isPreviewEnabled*/,
+				recordLineSeparator);
 		return scanner;
 	}
 
@@ -506,8 +507,9 @@
 				complianceLevelValue,
 				null/*taskTags*/,
 				null/*taskPriorities*/,
-				true/*taskCaseSensitive*/);
-		scanner.recordLineSeparator = recordLineSeparator;
+				true/*taskCaseSensitive*/,
+				true/*isPreviewEnabled*/,
+				recordLineSeparator);
 		return scanner;
 	}
 
@@ -591,8 +593,10 @@
 		if (sourceLevelValue == 0) sourceLevelValue = ClassFileConstants.JDK1_3; // fault-tolerance
 		long complianceLevelValue = CompilerOptions.versionToJdkLevel(complianceLevel);
 		if (complianceLevelValue == 0) complianceLevelValue = ClassFileConstants.JDK1_4; // fault-tolerance
-		scanner = new PublicScanner(tokenizeComments, tokenizeWhiteSpace, false/*nls*/,sourceLevelValue /*sourceLevel*/, complianceLevelValue, null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/, enablePreview);
-		scanner.recordLineSeparator = recordLineSeparator;
+		scanner = new PublicScanner(tokenizeComments, tokenizeWhiteSpace,
+				false/*nls*/,sourceLevelValue /*sourceLevel*/, complianceLevelValue,
+				null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/,
+				enablePreview, recordLineSeparator);
 		return scanner;
 	}
 }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java
index 60b8a6d..b2b913a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java
@@ -192,6 +192,27 @@
 	 */
 	int TokenNameCOLON_COLON = 406;
 
+	/** @since 3.30 */
+	int TokenNameNotAToken = 408;
+
+	/** @since 3.30 */
+	int TokenNameRestrictedIdentifierYield = 409;
+
+	/** @since 3.30 */
+	int TokenNameRestrictedIdentifierpermits = 410;
+
+	/** @since 3.30 */
+	int TokenNameRestrictedIdentifierrecord = 411;
+
+	/** @since 3.30 */
+	int TokenNameRestrictedIdentifiersealed = 412;
+
+	/** @since 3.30 */
+	int TokenNameSingleQuoteStringLiteral = 413;
+
+	/** @since 3.30 */
+	int TokenNamenon_sealed = 414;
+
 //{ObjectTeams: mirrored from auto-generated TerminalTokens.java. Note: the values here must be constant across Eclipse-versions, so do not simply copy the identifiers!
 	// Take values from 5000 upwards
     int
@@ -203,7 +224,6 @@
     TokenNamewith = 5005,
     TokenNameteam = 5006,
     TokenNameas = 5007,
-    TokenNameresult = 5008,
     TokenNamereplace = 5009,
     TokenNameafter = 5010,
     TokenNamebefore = 5011,
@@ -214,10 +234,5 @@
 	TokenNameset = 5017,
 	TokenNamewhen = 5018,
     TokenNameprecedence = 5019;
-    /**
-     * @deprecated Modifier 'readonly' is no longer recognized. It was never truly supported.
-     */
-    @Deprecated
-	int TokenNamereadonly = 5015;
 // carp}
 }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java
index bca0333..89a3930 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * 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
@@ -15,12 +15,14 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.core;
 
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Objects;
 
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IResource;
@@ -560,7 +562,11 @@
 								if (oldurl == null && newurl == null) {
 									pathHasChanged = false;
 								} else if (oldurl != null && newurl != null) {
-									pathHasChanged = !(newurl.equals(oldurl));
+									try {
+										pathHasChanged = !Objects.equals(newurl.toURI(),oldurl.toURI());
+									} catch (URISyntaxException e) {
+										// ignore
+									}
 								} else if (oldurl != null) {
 									indexManager.removeIndex(newPath);
 								}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java
index 4a443f0..2bc5c30 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java
@@ -361,7 +361,7 @@
 			int lenRefer = referringExtraAttributes.length;
 			if (lenRefer > 0) {
 				int lenEntry = combinedAttributes.length;
-				if (referringEntry.path.isPrefixOf(this.path)) {
+				if (referringEntry.path.isPrefixOf(this.path) || referringEntry.getEntryKind() == CPE_CONTAINER) {
 					// consider prefix location as less specific, put to back (e.g.: referring to a library via a project):
 					System.arraycopy(combinedAttributes, 0, combinedAttributes=new IClasspathAttribute[lenEntry+lenRefer], 0, lenEntry);
 					System.arraycopy(referringExtraAttributes, 0, combinedAttributes, lenEntry, lenRefer);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java
index d3a43f1..ca9a7c2 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Enumeration;
@@ -52,11 +53,13 @@
 final boolean isOnModulePath;
 
 static class PackageCacheEntry {
+	WeakReference<ZipFile> zipFile;
 	long lastModified;
 	long fileSize;
 	SimpleSet packageSet;
 
-	PackageCacheEntry(long lastModified, long fileSize, SimpleSet packageSet) {
+	PackageCacheEntry(ZipFile zipFile, long lastModified, long fileSize, SimpleSet packageSet) {
+		this.zipFile = new WeakReference<>(zipFile);
 		this.lastModified = lastModified;
 		this.fileSize = fileSize;
 		this.packageSet = packageSet;
@@ -84,15 +87,19 @@
 protected SimpleSet findPackageSet() {
 	String zipFileName = this.zipFilename;
 	PackageCacheEntry cacheEntry = (PackageCacheEntry) PackageCache.get(zipFileName);
+	if(cacheEntry != null && cacheEntry.zipFile.get() == this.zipFile) {
+		return cacheEntry.packageSet;
+	}
 	long timestamp = this.lastModified();
 	long fileSize = new File(zipFileName).length();
 	if (cacheEntry != null && cacheEntry.lastModified == timestamp && cacheEntry.fileSize == fileSize) {
+		cacheEntry.zipFile = new WeakReference<ZipFile>(this.zipFile);
 		return cacheEntry.packageSet;
 	}
 	final SimpleSet packageSet = new SimpleSet(41);
 	packageSet.add(""); //$NON-NLS-1$
 	readJarContent(packageSet);
-	PackageCache.put(zipFileName, new PackageCacheEntry(timestamp, fileSize, packageSet));
+	PackageCache.put(zipFileName, new PackageCacheEntry(this.zipFile, timestamp, fileSize, packageSet));
 	return packageSet;
 }
 protected String readJarContent(final SimpleSet packageSet) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedReader.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedReader.java
index cd56d74..811477a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedReader.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedReader.java
@@ -100,13 +100,17 @@
 	 * @see CompressedWriter#writeIntInRange(int, int)
 	 **/
 	public int readIntInRange(int range) throws IOException {
-		if (range < 0 || range > 0xFFFF) {
+		if (range < 0 || range > 0xFFFFFF) {
 			return this.in.readInt();
 		} else {
 			if (range <= 0xFF) {
 				return Byte.toUnsignedInt(this.in.readByte());
-			} else {
+			} else if (range <= 0xFFFF) {
 				return Short.toUnsignedInt(this.in.readShort());
+			} else {
+				byte b = this.in.readByte();
+				short s = this.in.readShort();
+				return ((s << 8 | (b & 0xff))) & 0xffffff;
 			}
 		}
 	}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedWriter.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedWriter.java
index 1b298bf..8bd6fb7 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedWriter.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/CompressedWriter.java
@@ -105,7 +105,7 @@
 	 **/
 	public void writeIntInRange(int v, int range) throws IOException {
 		// => typically(0<range<256) only one byte per int
-		if (range < 0 || range > 0xFFFF) {
+		if (range < 0 || range > 0xFFFFFF) {
 			this.out.writeInt(v);
 		} else {
 			if (v >= range) {
@@ -116,8 +116,11 @@
 			}
 			if (range <= 0xFF) {
 				this.out.writeByte(v);
-			} else {
+			} else if (range <= 0xFFFF) {
 				this.out.writeShort(v);
+			} else {
+				this.out.writeByte(v);
+				this.out.writeShort(v >>> 8);
 			}
 		}
 	}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
index ebe1678..03a261d 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
@@ -381,13 +381,28 @@
 				// do nothing, probably a non module project
 			}
 		}
-		// collect the output folders, skipping duplicates
+		// collect the output folders, skipping, or merging duplicates
 		next : for (int i = 0, l = this.sourceLocations.length; i < l; i++) {
 			ClasspathMultiDirectory md = this.sourceLocations[i];
 			IPath outputPath = md.binaryFolder.getFullPath();
+			String eeaPath = md.externalAnnotationPath;
 			for (int j = 0; j < i; j++) { // compare against previously walked source folders
-				if (outputPath.equals(this.sourceLocations[j].binaryFolder.getFullPath())) {
-					md.hasIndependentOutputFolder = this.sourceLocations[j].hasIndependentOutputFolder;
+				ClasspathMultiDirectory previousMd = this.sourceLocations[j];
+				if (outputPath.equals(previousMd.binaryFolder.getFullPath())) {
+					if ((eeaPath == null) != (previousMd.externalAnnotationPath == null) // one has eeaPath other doesn't ?
+							&& md.isOnModulePath == previousMd.isOnModulePath
+							&& md.accessRuleSet == previousMd.accessRuleSet)
+					{
+						// only relevant difference between md and previousMd is a non-conflicting eea-path, so unify into one entry *with* eea-path:
+						if (eeaPath == null)
+							eeaPath = previousMd.externalAnnotationPath;
+						int prev = outputFolders.indexOf(previousMd);
+						if (prev != -1) {
+							outputFolders.set(prev, new ClasspathDirectory(md.binaryFolder, true, md.accessRuleSet, new Path(eeaPath), md.isOnModulePath));
+							continue next;
+						}
+					}
+					md.hasIndependentOutputFolder = previousMd.hasIndependentOutputFolder;
 					continue next;
 				}
 			}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
index 96441aa..013c2cb 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
@@ -80,7 +80,7 @@
 private StringSet structurallyChangedTypes;
 public static int MaxStructurallyChangedTypes = 100; // keep track of ? structurally changed types, otherwise consider all to be changed
 
-public static final byte VERSION = 0x0025;
+public static final byte VERSION = 0x0026;
 
 static final byte SOURCE_FOLDER = 1;
 static final byte BINARY_FOLDER = 2;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
index 5ea6358..aae3012 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
@@ -10,4433 +10,320 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
- *     Fraunhofer FIRST - extended API and implementation
- *     Technical University Berlin - extended API and implementation
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.util;
 
-import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.compiler.IScanner;
 import org.eclipse.jdt.core.compiler.ITerminalSymbols;
 import org.eclipse.jdt.core.compiler.InvalidInputException;
-import org.eclipse.jdt.internal.compiler.CompilationResult;
-import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
-import org.eclipse.jdt.internal.compiler.parser.NLSTag;
-import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
+import org.eclipse.jdt.internal.compiler.parser.Scanner;
 import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
-import org.eclipse.jdt.internal.compiler.util.CharDeduplication;
-import org.eclipse.jdt.internal.compiler.util.Util;
 
+/**
+ * This class acts as a facade to the internal Scanner implementation and delegates
+ * all the work to the internal Scanner instance.
+ *
+ * <p>
+ * <b>Note for maintainers</b>
+ * No additional logic should be added here except adopting NON-API constants
+ * returned by {@link #getNextToken()} from {@link TerminalTokens} to {@link ITerminalSymbols}.
+ */
 public class PublicScanner implements IScanner, ITerminalSymbols {
 
-	@SuppressWarnings("deprecation")
-	private static final int InternalTokenNameIdentifier = TokenNameIdentifier;
+	private final Scanner delegate;
 
-	//public int newIdentCount = 0;
+	public PublicScanner(
+			boolean tokenizeComments,
+			boolean tokenizeWhiteSpace,
+			boolean checkNonExternalizedStringLiterals,
+			long sourceLevel,
+			long complianceLevel,
+			char[][] taskTags,
+			char[][] taskPriorities,
+			boolean isTaskCaseSensitive,
+			boolean isPreviewEnabled,
+			boolean recordLineSeparator) {
 
-	/* APIs ares
-	 - getNextToken() which return the current type of the token
-	   (this value is not memorized by the scanner)
-	 - getCurrentTokenSource() which provides with the token "REAL" source
-	   (aka all unicode have been transformed into a correct char)
-	 - sourceStart gives the position into the stream
-	 - currentPosition-1 gives the sourceEnd position into the stream
-	*/
-	public long sourceLevel;
-	public long complianceLevel;
-	public boolean previewEnabled;
+			this.delegate = createScanner(tokenizeComments, tokenizeWhiteSpace, checkNonExternalizedStringLiterals, sourceLevel,
+					complianceLevel, taskTags, taskPriorities, isTaskCaseSensitive, isPreviewEnabled);
+			this.delegate.recordLineSeparator = recordLineSeparator;
+	}
 
-	// 1.4 feature
-	public boolean useAssertAsAnIndentifier = false;
-	//flag indicating if processed source contains occurrences of keyword assert
-	public boolean containsAssertKeyword = false;
+	protected Scanner createScanner(boolean tokenizeComments, boolean tokenizeWhiteSpace,
+			boolean checkNonExternalizedStringLiterals, long sourceLevel, long complianceLevel, char[][] taskTags,
+			char[][] taskPriorities, boolean isTaskCaseSensitive, boolean isPreviewEnabled) {
+		return new Scanner(tokenizeComments, tokenizeWhiteSpace, checkNonExternalizedStringLiterals,
+				sourceLevel, complianceLevel, taskTags, taskPriorities, isTaskCaseSensitive, isPreviewEnabled);
+	}
 
-	// 1.5 feature
-	public boolean useEnumAsAnIndentifier = false;
+	@Override
+	public int getCurrentTokenEndPosition(){
+		return this.delegate.getCurrentTokenEndPosition();
+	}
 
-//{ObjectTeams: the scanner is now quite stateful:
-	// a master switch to turn off all OT keywords:
-	public boolean parsePureJavaOnly = false;
-	// experimental switch, set to true if you encounter problems with the scanner:
-	public boolean parseOTJonly = true;
+	@Override
+	public char[] getCurrentTokenSource() {
+		return this.delegate.getCurrentTokenSource();
+	}
 
-	// general switch for std-Java vs. OT/J
-	protected boolean _isOTSource = this.parseOTJonly; // initialized also in setSource().
-	/** Fully enable OT/J keywords. */
-    public void enterOTSource() {
-    	this._isOTSource = true;
-    	this._teamKeywordSeen = false; // not needed any more
-    }
+	@Override
+	public final char[] getRawTokenSource() {
+		return this.delegate.getRawTokenSource();
+	}
 
-    //  within the body of a base predicate "base" is not a keyword
-    protected boolean _forceBaseIsIdentifier = false;
-    // seen 'team' but no 'class'/'interface' yet -> 'base' could still be an imported package name.
-    protected boolean _teamKeywordSeen = false;
-    // directly after 'import' 'base' is always a keyword/modifier:
-    protected boolean _atStartOfImport = false;
-    public void forceBaseIsIdentifier() {
-    	this._forceBaseIsIdentifier = true;
-    }
-    public void restoreBaseKeyword() {
-    	this._forceBaseIsIdentifier = false;
-    }
-    protected boolean baseIsKeyword(boolean atStartOfImport) {
-    	if (this.parsePureJavaOnly)
-    		return false;
-    	// "base" keyword may appear before 'class' header of containing
-    	// team/role has been consumed.
-    	if (this.currentCharacter == '.')
-    		atStartOfImport = false;
-    	return (this._isOTSource || atStartOfImport) && !this._forceBaseIsIdentifier;
-    }
+	@Override
+	public int getCurrentTokenStartPosition(){
+		return this.delegate.startPosition;
+	}
 
-    // remember if the previous token was a callin/callout symbol ...
-    protected boolean _calloutSeen = false;
-    protected boolean _callinSeen = false;
-    // ... or the "precedence" keyword
-	protected boolean _precedenceSeen = false;
+	/*
+	 * Search the source position corresponding to the end of a given line number
+	 *
+	 * Line numbers are 1-based, and relative to the scanner initialPosition.
+	 * Character positions are 0-based.
+	 *
+	 * In case the given line number is inconsistent, answers -1.
+	 */
+	@Override
+	public final int getLineEnd(int lineNumber) {
+		return this.delegate.getLineEnd(lineNumber);
+	}
 
-    // after a '.' even 'team' can be an identifier:
-    private int _dotSeen = 0; // 0: no, 1: previos, 2: this token
+	@Override
+	public final int[] getLineEnds() {
+		return this.delegate.getLineEnds();
+	}
 
-    public void resetOTFlags() {
-    	this._isOTSource = this.parseOTJonly;
-    	this._teamKeywordSeen = false;
-    	this._forceBaseIsIdentifier = false;
-    	this._callinSeen = false;
-    	this._calloutSeen = false;
-    }
+	/**
+	 * Search the source position corresponding to the beginning of a given line number
+	 *
+	 * Line numbers are 1-based, and relative to the scanner initialPosition.
+	 * Character positions are 0-based.
+	 *
+	 * e.g.	getLineStart(1) --> 0	indicates that the first line starts at character 0.
+	 *
+	 * In case the given line number is inconsistent, answers -1.
+	 *
+	 * @param lineNumber int
+	 * @return int
+	 */
+	@Override
+	public final int getLineStart(int lineNumber) {
+		return this.delegate.getLineStart(lineNumber);
+	}
 
-    /**
-     * Check whether a given terminal token is currently enabled.
-     * Some keywords can be disabled:
-     *    (base) as callin playedBy precedence tsuper when with
-     * Only within a join point query:
-     *    # .. elementof
-     * Some keyworkds can only be disabled by parsePureJavaOnly:
-     *    team within
-     */
-    public boolean isTokenEnabled(int sym) {
-    	switch (sym) {
-    	case TokenNameteam:
-    	case TokenNamewithin:
-    		return !this.parsePureJavaOnly;
+	@Override
+	public int getNextToken() throws InvalidInputException {
+		int nextToken = this.delegate.getNextToken();
+		int symbol  = translateTokenToTerminalSymbol(nextToken);
+		return symbol;
+	}
 
-    	case TokenNamebase:
-    		return baseIsKeyword(false);
-    	case TokenNameas:
-    	case TokenNameBINDIN:
-    	case TokenNameBINDOUT:
-    	case TokenNamecallin:
-    	case TokenNameplayedBy:
-    	case TokenNameprecedence:
-    	case TokenNametsuper:
-    	case TokenNamewhen:
-    	case TokenNamewith:
-    		return this._isOTSource;
-    	}
-    	return true;
-    }
-// SH}
-
-	public boolean recordLineSeparator = false;
-	public char currentCharacter;
-	public int startPosition;
-	public int currentPosition;
-	public int initialPosition, eofPosition;
-	// after this position eof are generated instead of real token from the source
-
-	public boolean skipComments = false;
-	public boolean tokenizeComments = false;
-	public boolean tokenizeWhiteSpace = false;
-
-	//source should be viewed as a window (aka a part)
-	//of a entire very large stream
-	public char source[];
-
-	//unicode support
-	public char[] withoutUnicodeBuffer;
-	public int withoutUnicodePtr; //when == 0 ==> no unicode in the current token
-	public boolean unicodeAsBackSlash = false;
-
-	public boolean scanningFloatLiteral = false;
-
-	//support for /** comments
-	public final static int COMMENT_ARRAYS_SIZE = 30;
-	public int[] commentStops = new int[COMMENT_ARRAYS_SIZE];
-	public int[] commentStarts = new int[COMMENT_ARRAYS_SIZE];
-	public int[] commentTagStarts = new int[COMMENT_ARRAYS_SIZE];
-	public int commentPtr = -1; // no comment test with commentPtr value -1
-	protected int lastCommentLinePosition = -1;
-
-	// task tag support
-	public char[][] foundTaskTags = null;
-	public char[][] foundTaskMessages;
-	public char[][] foundTaskPriorities = null;
-	public int[][] foundTaskPositions;
-	public int foundTaskCount = 0;
-	public char[][] taskTags = null;
-	public char[][] taskPriorities = null;
-	public boolean isTaskCaseSensitive = true;
-
-	//diet parsing support - jump over some method body when requested
-	public boolean diet = false;
-
-	//support for the  poor-line-debuggers ....
-	//remember the position of the cr/lf
-	public int[] lineEnds = new int[250];
-	public int linePtr = -1;
-	public boolean wasAcr = false;
-
-	public static final String END_OF_SOURCE = "End_Of_Source"; //$NON-NLS-1$
-
-	public static final String INVALID_HEXA = "Invalid_Hexa_Literal"; //$NON-NLS-1$
-	public static final String INVALID_OCTAL = "Invalid_Octal_Literal"; //$NON-NLS-1$
-	public static final String INVALID_CHARACTER_CONSTANT = "Invalid_Character_Constant";  //$NON-NLS-1$
-	public static final String INVALID_ESCAPE = "Invalid_Escape"; //$NON-NLS-1$
-	public static final String INVALID_INPUT = "Invalid_Input"; //$NON-NLS-1$
-	public static final String INVALID_TEXTBLOCK = "Invalid_Textblock"; //$NON-NLS-1$
-	public static final String INVALID_UNICODE_ESCAPE = "Invalid_Unicode_Escape"; //$NON-NLS-1$
-	public static final String INVALID_FLOAT = "Invalid_Float_Literal"; //$NON-NLS-1$
-	public static final String INVALID_LOW_SURROGATE = "Invalid_Low_Surrogate"; //$NON-NLS-1$
-	public static final String INVALID_HIGH_SURROGATE = "Invalid_High_Surrogate"; //$NON-NLS-1$
-
-	public static final String NULL_SOURCE_STRING = "Null_Source_String"; //$NON-NLS-1$
-	public static final String UNTERMINATED_STRING = "Unterminated_String"; //$NON-NLS-1$
-	public static final String UNTERMINATED_TEXT_BLOCK = "Unterminated_Text_Block"; //$NON-NLS-1$
-	public static final String UNTERMINATED_COMMENT = "Unterminated_Comment"; //$NON-NLS-1$
-	public static final String INVALID_CHAR_IN_STRING = "Invalid_Char_In_String"; //$NON-NLS-1$
-	public static final String INVALID_DIGIT = "Invalid_Digit"; //$NON-NLS-1$
-	private static final int[] EMPTY_LINE_ENDS = Util.EMPTY_INT_ARRAY;
-
-	public static final String INVALID_BINARY = "Invalid_Binary_Literal"; //$NON-NLS-1$
-	public static final String BINARY_LITERAL_NOT_BELOW_17 = "Binary_Literal_Not_Below_17"; //$NON-NLS-1$
-	public static final String ILLEGAL_HEXA_LITERAL = "Illegal_Hexa_Literal"; //$NON-NLS-1$
-	public static final String INVALID_UNDERSCORE = "Invalid_Underscore"; //$NON-NLS-1$
-	public static final String UNDERSCORES_IN_LITERALS_NOT_BELOW_17 = "Underscores_In_Literals_Not_Below_17"; //$NON-NLS-1$`
-
-	// support for detecting non-externalized string literals
-	public static final char[] TAG_PREFIX= "//$NON-NLS-".toCharArray(); //$NON-NLS-1$
-	public static final int TAG_PREFIX_LENGTH= TAG_PREFIX.length;
-	public static final char TAG_POSTFIX= '$';
-	public static final int TAG_POSTFIX_LENGTH= 1;
-	private NLSTag[] nlsTags = null;
-	protected int nlsTagsPtr;
-	public boolean checkNonExternalizedStringLiterals;
-
-	protected int lastPosition;
-
-	// generic support
-	public boolean returnOnlyGreater = false;
-
-	public boolean insideRecovery = false;
-
-	public static final int RoundBracket = 0;
-	public static final int SquareBracket = 1;
-	public static final int CurlyBracket = 2;
-	public static final int BracketKinds = 3;
-
-	// extended unicode support
-	public static final int LOW_SURROGATE_MIN_VALUE = 0xDC00;
-	public static final int HIGH_SURROGATE_MIN_VALUE = 0xD800;
-	public static final int HIGH_SURROGATE_MAX_VALUE = 0xDBFF;
-	public static final int LOW_SURROGATE_MAX_VALUE = 0xDFFF;
-
-	// text block support - 13
-	/* package */ int rawStart = -1;
-
-	private CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();
-
-public PublicScanner() {
-	this(false /*comment*/, false /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true /*taskCaseSensitive*/);
-}
-
-public PublicScanner(
-		boolean tokenizeComments,
-		boolean tokenizeWhiteSpace,
-		boolean checkNonExternalizedStringLiterals,
-		long sourceLevel,
-		long complianceLevel,
-		char[][] taskTags,
-		char[][] taskPriorities,
-		boolean isTaskCaseSensitive) {
-			this(tokenizeComments,
-				tokenizeWhiteSpace,
-				checkNonExternalizedStringLiterals,
-				sourceLevel,
-				complianceLevel,
-				taskTags,
-				taskPriorities,
-				isTaskCaseSensitive,
-				true);
-}
-public PublicScanner(
-		boolean tokenizeComments,
-		boolean tokenizeWhiteSpace,
-		boolean checkNonExternalizedStringLiterals,
-		long sourceLevel,
-		long complianceLevel,
-		char[][] taskTags,
-		char[][] taskPriorities,
-		boolean isTaskCaseSensitive,
-		boolean previewEnabled) {
-
-	this.eofPosition = Integer.MAX_VALUE;
-	this.tokenizeComments = tokenizeComments;
-	this.tokenizeWhiteSpace = tokenizeWhiteSpace;
-	this.sourceLevel = sourceLevel;
-	this.complianceLevel = complianceLevel;
-	this.previewEnabled = previewEnabled;
-	this.checkNonExternalizedStringLiterals = checkNonExternalizedStringLiterals;
-	if (taskTags != null) {
-		int taskTagsLength = taskTags.length;
-		int length = taskTagsLength;
-		if (taskPriorities != null) {
-			int taskPrioritiesLength = taskPriorities.length;
-			if (taskPrioritiesLength != taskTagsLength) {
-				if (taskPrioritiesLength > taskTagsLength) {
-					System.arraycopy(taskPriorities, 0, (taskPriorities = new char[taskTagsLength][]), 0, taskTagsLength);
-				} else {
-					System.arraycopy(taskTags, 0, (taskTags = new char[taskPrioritiesLength][]), 0, taskPrioritiesLength);
-					length = taskPrioritiesLength;
-				}
-			}
-			int[] initialIndexes = new int[length];
-			for (int i = 0; i < length; i++) {
-				initialIndexes[i] = i;
-			}
-			Util.reverseQuickSort(taskTags, 0, length - 1, initialIndexes);
-			char[][] temp = new char[length][];
-			for (int i = 0; i < length; i++) {
-				temp[i] = taskPriorities[initialIndexes[i]];
-			}
-			this.taskPriorities = temp;
-		} else {
-			Util.reverseQuickSort(taskTags, 0, length - 1);
+	/**
+	 * Translates internal generated constants from {@link TerminalTokens} to public constants defined in
+	 * {@link ITerminalSymbols}.
+	 * <p>
+	 * There is PublicScannerTest that validates the translation and can generate switch body for this method
+	 * on changes in Scanner / TerminalTokens.
+	 * <p>
+	 * Note for maintainers: new tokens from {@link TerminalTokens} should be added to {@link ITerminalSymbols} with
+	 * <b>adopted</b> numerical values (they differ in each interface).
+	 */
+	@Deprecated // uses deprecated TerminalTokensIdentifier
+	private int translateTokenToTerminalSymbol(int nextToken) throws InvalidInputException {
+		switch (nextToken) {
+			case TerminalTokens.TokenNameAND : nextToken = ITerminalSymbols.TokenNameAND; break;
+			case TerminalTokens.TokenNameAND_AND : nextToken = ITerminalSymbols.TokenNameAND_AND; break;
+			case TerminalTokens.TokenNameAND_EQUAL : nextToken = ITerminalSymbols.TokenNameAND_EQUAL; break;
+			case TerminalTokens.TokenNameARROW : nextToken = ITerminalSymbols.TokenNameARROW; break;
+			case TerminalTokens.TokenNameAT : nextToken = ITerminalSymbols.TokenNameAT; break;
+			case TerminalTokens.TokenNameAT308 : nextToken = ITerminalSymbols.TokenNameAT; break;
+			case TerminalTokens.TokenNameAT308DOTDOTDOT : nextToken = ITerminalSymbols.TokenNameAT; break;
+			case TerminalTokens.TokenNameATOT : nextToken = ITerminalSymbols.TokenNameAT; break;
+			case TerminalTokens.TokenNameBINDIN : nextToken = ITerminalSymbols.TokenNameBINDIN; break;
+			case TerminalTokens.TokenNameBINDOUT : nextToken = ITerminalSymbols.TokenNameBINDOUT; break;
+			case TerminalTokens.TokenNameBeginCaseElement : nextToken = getNextToken(); break;
+			case TerminalTokens.TokenNameBeginCaseExpr : nextToken = getNextToken(); break;
+			case TerminalTokens.TokenNameBeginIntersectionCast : nextToken = getNextToken(); break;
+			case TerminalTokens.TokenNameBeginLambda : nextToken = getNextToken(); break;
+			case TerminalTokens.TokenNameBeginTypeArguments : nextToken = getNextToken(); break;
+			case TerminalTokens.TokenNameCALLOUT_OVERRIDE : nextToken = ITerminalSymbols.TokenNameCALLOUT_OVERRIDE; break;
+			case TerminalTokens.TokenNameCOLON : nextToken = ITerminalSymbols.TokenNameCOLON; break;
+			case TerminalTokens.TokenNameCOLON_COLON : nextToken = ITerminalSymbols.TokenNameCOLON_COLON; break;
+			case TerminalTokens.TokenNameCOMMA : nextToken = ITerminalSymbols.TokenNameCOMMA; break;
+			case TerminalTokens.TokenNameCOMMENT_BLOCK : nextToken = ITerminalSymbols.TokenNameCOMMENT_BLOCK; break;
+			case TerminalTokens.TokenNameCOMMENT_JAVADOC : nextToken = ITerminalSymbols.TokenNameCOMMENT_JAVADOC; break;
+			case TerminalTokens.TokenNameCOMMENT_LINE : nextToken = ITerminalSymbols.TokenNameCOMMENT_LINE; break;
+			case TerminalTokens.TokenNameCharacterLiteral : nextToken = ITerminalSymbols.TokenNameCharacterLiteral; break;
+			case TerminalTokens.TokenNameDIVIDE : nextToken = ITerminalSymbols.TokenNameDIVIDE; break;
+			case TerminalTokens.TokenNameDIVIDE_EQUAL : nextToken = ITerminalSymbols.TokenNameDIVIDE_EQUAL; break;
+			case TerminalTokens.TokenNameDOT : nextToken = ITerminalSymbols.TokenNameDOT; break;
+			case TerminalTokens.TokenNameDoubleLiteral : nextToken = ITerminalSymbols.TokenNameDoubleLiteral; break;
+			case TerminalTokens.TokenNameELLIPSIS : nextToken = ITerminalSymbols.TokenNameELLIPSIS; break;
+			case TerminalTokens.TokenNameEOF : nextToken = ITerminalSymbols.TokenNameEOF; break;
+			case TerminalTokens.TokenNameEQUAL : nextToken = ITerminalSymbols.TokenNameEQUAL; break;
+			case TerminalTokens.TokenNameEQUAL_EQUAL : nextToken = ITerminalSymbols.TokenNameEQUAL_EQUAL; break;
+			case TerminalTokens.TokenNameERROR : nextToken = ITerminalSymbols.TokenNameERROR; break;
+			case TerminalTokens.TokenNameElidedSemicolonAndRightBrace : nextToken = getNextToken(); break;
+			case TerminalTokens.TokenNameFloatingPointLiteral : nextToken = ITerminalSymbols.TokenNameFloatingPointLiteral; break;
+			case TerminalTokens.TokenNameGREATER : nextToken = ITerminalSymbols.TokenNameGREATER; break;
+			case TerminalTokens.TokenNameGREATER_EQUAL : nextToken = ITerminalSymbols.TokenNameGREATER_EQUAL; break;
+			case TerminalTokens.TokenNameIdentifier : nextToken = ITerminalSymbols.TokenNameIdentifier; break;
+			case TerminalTokens.TokenNameIntegerLiteral : nextToken = ITerminalSymbols.TokenNameIntegerLiteral; break;
+			case TerminalTokens.TokenNameLBRACE : nextToken = ITerminalSymbols.TokenNameLBRACE; break;
+			case TerminalTokens.TokenNameLBRACKET : nextToken = ITerminalSymbols.TokenNameLBRACKET; break;
+			case TerminalTokens.TokenNameLEFT_SHIFT : nextToken = ITerminalSymbols.TokenNameLEFT_SHIFT; break;
+			case TerminalTokens.TokenNameLEFT_SHIFT_EQUAL : nextToken = ITerminalSymbols.TokenNameLEFT_SHIFT_EQUAL; break;
+			case TerminalTokens.TokenNameLESS : nextToken = ITerminalSymbols.TokenNameLESS; break;
+			case TerminalTokens.TokenNameLESS_EQUAL : nextToken = ITerminalSymbols.TokenNameLESS_EQUAL; break;
+			case TerminalTokens.TokenNameLPAREN : nextToken = ITerminalSymbols.TokenNameLPAREN; break;
+			case TerminalTokens.TokenNameLongLiteral : nextToken = ITerminalSymbols.TokenNameLongLiteral; break;
+			case TerminalTokens.TokenNameMINUS : nextToken = ITerminalSymbols.TokenNameMINUS; break;
+			case TerminalTokens.TokenNameMINUS_EQUAL : nextToken = ITerminalSymbols.TokenNameMINUS_EQUAL; break;
+			case TerminalTokens.TokenNameMINUS_MINUS : nextToken = ITerminalSymbols.TokenNameMINUS_MINUS; break;
+			case TerminalTokens.TokenNameMULTIPLY : nextToken = ITerminalSymbols.TokenNameMULTIPLY; break;
+			case TerminalTokens.TokenNameMULTIPLY_EQUAL : nextToken = ITerminalSymbols.TokenNameMULTIPLY_EQUAL; break;
+			case TerminalTokens.TokenNameNOT : nextToken = ITerminalSymbols.TokenNameNOT; break;
+			case TerminalTokens.TokenNameNOT_EQUAL : nextToken = ITerminalSymbols.TokenNameNOT_EQUAL; break;
+			case TerminalTokens.TokenNameNotAToken : nextToken = ITerminalSymbols.TokenNameNotAToken; break;
+			case TerminalTokens.TokenNameOR : nextToken = ITerminalSymbols.TokenNameOR; break;
+			case TerminalTokens.TokenNameOR_EQUAL : nextToken = ITerminalSymbols.TokenNameOR_EQUAL; break;
+			case TerminalTokens.TokenNameOR_OR : nextToken = ITerminalSymbols.TokenNameOR_OR; break;
+			case TerminalTokens.TokenNamePLUS : nextToken = ITerminalSymbols.TokenNamePLUS; break;
+			case TerminalTokens.TokenNamePLUS_EQUAL : nextToken = ITerminalSymbols.TokenNamePLUS_EQUAL; break;
+			case TerminalTokens.TokenNamePLUS_PLUS : nextToken = ITerminalSymbols.TokenNamePLUS_PLUS; break;
+			case TerminalTokens.TokenNameQUESTION : nextToken = ITerminalSymbols.TokenNameQUESTION; break;
+			case TerminalTokens.TokenNameRBRACE : nextToken = ITerminalSymbols.TokenNameRBRACE; break;
+			case TerminalTokens.TokenNameRBRACKET : nextToken = ITerminalSymbols.TokenNameRBRACKET; break;
+			case TerminalTokens.TokenNameREMAINDER : nextToken = ITerminalSymbols.TokenNameREMAINDER; break;
+			case TerminalTokens.TokenNameREMAINDER_EQUAL : nextToken = ITerminalSymbols.TokenNameREMAINDER_EQUAL; break;
+			case TerminalTokens.TokenNameRIGHT_SHIFT : nextToken = ITerminalSymbols.TokenNameRIGHT_SHIFT; break;
+			case TerminalTokens.TokenNameRIGHT_SHIFT_EQUAL : nextToken = ITerminalSymbols.TokenNameRIGHT_SHIFT_EQUAL; break;
+			case TerminalTokens.TokenNameRPAREN : nextToken = ITerminalSymbols.TokenNameRPAREN; break;
+			case TerminalTokens.TokenNameRestrictedIdentifierYield : nextToken = ITerminalSymbols.TokenNameRestrictedIdentifierYield; break;
+			case TerminalTokens.TokenNameRestrictedIdentifierpermits : nextToken = ITerminalSymbols.TokenNameRestrictedIdentifierpermits; break;
+			case TerminalTokens.TokenNameRestrictedIdentifierrecord : nextToken = ITerminalSymbols.TokenNameRestrictedIdentifierrecord; break;
+			case TerminalTokens.TokenNameRestrictedIdentifiersealed : nextToken = ITerminalSymbols.TokenNameRestrictedIdentifiersealed; break;
+			case TerminalTokens.TokenNameSEMICOLON : nextToken = ITerminalSymbols.TokenNameSEMICOLON; break;
+			case TerminalTokens.TokenNameSingleQuoteStringLiteral : nextToken = ITerminalSymbols.TokenNameSingleQuoteStringLiteral; break;
+			case TerminalTokens.TokenNameStringLiteral : nextToken = ITerminalSymbols.TokenNameStringLiteral; break;
+			case TerminalTokens.TokenNameTWIDDLE : nextToken = ITerminalSymbols.TokenNameTWIDDLE; break;
+			case TerminalTokens.TokenNameTextBlock : nextToken = ITerminalSymbols.TokenNameTextBlock; break;
+			case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT : nextToken = ITerminalSymbols.TokenNameUNSIGNED_RIGHT_SHIFT; break;
+			case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : nextToken = ITerminalSymbols.TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL; break;
+			case TerminalTokens.TokenNameWHITESPACE : nextToken = ITerminalSymbols.TokenNameWHITESPACE; break;
+			case TerminalTokens.TokenNameXOR : nextToken = ITerminalSymbols.TokenNameXOR; break;
+			case TerminalTokens.TokenNameXOR_EQUAL : nextToken = ITerminalSymbols.TokenNameXOR_EQUAL; break;
+			case TerminalTokens.TokenNameabstract : nextToken = ITerminalSymbols.TokenNameabstract; break;
+			case TerminalTokens.TokenNameafter : nextToken = ITerminalSymbols.TokenNameafter; break;
+			case TerminalTokens.TokenNameas : nextToken = ITerminalSymbols.TokenNameas; break;
+			case TerminalTokens.TokenNameassert : nextToken = ITerminalSymbols.TokenNameassert; break;
+			case TerminalTokens.TokenNamebase : nextToken = ITerminalSymbols.TokenNamebase; break;
+			case TerminalTokens.TokenNamebefore : nextToken = ITerminalSymbols.TokenNamebefore; break;
+			case TerminalTokens.TokenNameboolean : nextToken = ITerminalSymbols.TokenNameboolean; break;
+			case TerminalTokens.TokenNamebreak : nextToken = ITerminalSymbols.TokenNamebreak; break;
+			case TerminalTokens.TokenNamebyte : nextToken = ITerminalSymbols.TokenNamebyte; break;
+			case TerminalTokens.TokenNamecallin : nextToken = ITerminalSymbols.TokenNamecallin; break;
+			case TerminalTokens.TokenNamecase : nextToken = ITerminalSymbols.TokenNamecase; break;
+			case TerminalTokens.TokenNamecatch : nextToken = ITerminalSymbols.TokenNamecatch; break;
+			case TerminalTokens.TokenNamechar : nextToken = ITerminalSymbols.TokenNamechar; break;
+			case TerminalTokens.TokenNameclass : nextToken = ITerminalSymbols.TokenNameclass; break;
+			case TerminalTokens.TokenNameconst : nextToken = ITerminalSymbols.TokenNameconst; break;
+			case TerminalTokens.TokenNamecontinue : nextToken = ITerminalSymbols.TokenNamecontinue; break;
+			case TerminalTokens.TokenNamedefault : nextToken = ITerminalSymbols.TokenNamedefault; break;
+			case TerminalTokens.TokenNamedo : nextToken = ITerminalSymbols.TokenNamedo; break;
+			case TerminalTokens.TokenNamedouble : nextToken = ITerminalSymbols.TokenNamedouble; break;
+			case TerminalTokens.TokenNameelse : nextToken = ITerminalSymbols.TokenNameelse; break;
+			case TerminalTokens.TokenNameenum : nextToken = ITerminalSymbols.TokenNameenum; break;
+			case TerminalTokens.TokenNameextends : nextToken = ITerminalSymbols.TokenNameextends; break;
+			case TerminalTokens.TokenNamefalse : nextToken = ITerminalSymbols.TokenNamefalse; break;
+			case TerminalTokens.TokenNamefinal : nextToken = ITerminalSymbols.TokenNamefinal; break;
+			case TerminalTokens.TokenNamefinally : nextToken = ITerminalSymbols.TokenNamefinally; break;
+			case TerminalTokens.TokenNamefloat : nextToken = ITerminalSymbols.TokenNamefloat; break;
+			case TerminalTokens.TokenNamefor : nextToken = ITerminalSymbols.TokenNamefor; break;
+			case TerminalTokens.TokenNameget : nextToken = ITerminalSymbols.TokenNameget; break;
+			case TerminalTokens.TokenNamegoto : nextToken = ITerminalSymbols.TokenNamegoto; break;
+			case TerminalTokens.TokenNameif : nextToken = ITerminalSymbols.TokenNameif; break;
+			case TerminalTokens.TokenNameimplements : nextToken = ITerminalSymbols.TokenNameimplements; break;
+			case TerminalTokens.TokenNameimport : nextToken = ITerminalSymbols.TokenNameimport; break;
+			case TerminalTokens.TokenNameinstanceof : nextToken = ITerminalSymbols.TokenNameinstanceof; break;
+			case TerminalTokens.TokenNameint : nextToken = ITerminalSymbols.TokenNameint; break;
+			case TerminalTokens.TokenNameinterface : nextToken = ITerminalSymbols.TokenNameinterface; break;
+			case TerminalTokens.TokenNamelong : nextToken = ITerminalSymbols.TokenNamelong; break;
+			case TerminalTokens.TokenNamenative : nextToken = ITerminalSymbols.TokenNamenative; break;
+			case TerminalTokens.TokenNamenew : nextToken = ITerminalSymbols.TokenNamenew; break;
+			case TerminalTokens.TokenNamenon_sealed : nextToken = ITerminalSymbols.TokenNamenon_sealed; break;
+			case TerminalTokens.TokenNamenull : nextToken = ITerminalSymbols.TokenNamenull; break;
+			case TerminalTokens.TokenNamepackage : nextToken = ITerminalSymbols.TokenNamepackage; break;
+			case TerminalTokens.TokenNameplayedBy : nextToken = ITerminalSymbols.TokenNameplayedBy; break;
+			case TerminalTokens.TokenNameprecedence : nextToken = ITerminalSymbols.TokenNameprecedence; break;
+			case TerminalTokens.TokenNameprivate : nextToken = ITerminalSymbols.TokenNameprivate; break;
+			case TerminalTokens.TokenNameprotected : nextToken = ITerminalSymbols.TokenNameprotected; break;
+			case TerminalTokens.TokenNamepublic : nextToken = ITerminalSymbols.TokenNamepublic; break;
+			case TerminalTokens.TokenNamereplace : nextToken = ITerminalSymbols.TokenNamereplace; break;
+			case TerminalTokens.TokenNamereturn : nextToken = ITerminalSymbols.TokenNamereturn; break;
+			case TerminalTokens.TokenNameset : nextToken = ITerminalSymbols.TokenNameset; break;
+			case TerminalTokens.TokenNameshort : nextToken = ITerminalSymbols.TokenNameshort; break;
+			case TerminalTokens.TokenNamestatic : nextToken = ITerminalSymbols.TokenNamestatic; break;
+			case TerminalTokens.TokenNamestrictfp : nextToken = ITerminalSymbols.TokenNamestrictfp; break;
+			case TerminalTokens.TokenNamesuper : nextToken = ITerminalSymbols.TokenNamesuper; break;
+			case TerminalTokens.TokenNameswitch : nextToken = ITerminalSymbols.TokenNameswitch; break;
+			case TerminalTokens.TokenNamesynchronized : nextToken = ITerminalSymbols.TokenNamesynchronized; break;
+			case TerminalTokens.TokenNameteam : nextToken = ITerminalSymbols.TokenNameteam; break;
+			case TerminalTokens.TokenNamethis : nextToken = ITerminalSymbols.TokenNamethis; break;
+			case TerminalTokens.TokenNamethrow : nextToken = ITerminalSymbols.TokenNamethrow; break;
+			case TerminalTokens.TokenNamethrows : nextToken = ITerminalSymbols.TokenNamethrows; break;
+			case TerminalTokens.TokenNametransient : nextToken = ITerminalSymbols.TokenNametransient; break;
+			case TerminalTokens.TokenNametrue : nextToken = ITerminalSymbols.TokenNametrue; break;
+			case TerminalTokens.TokenNametry : nextToken = ITerminalSymbols.TokenNametry; break;
+			case TerminalTokens.TokenNametsuper : nextToken = ITerminalSymbols.TokenNametsuper; break;
+			case TerminalTokens.TokenNamevoid : nextToken = ITerminalSymbols.TokenNamevoid; break;
+			case TerminalTokens.TokenNamevolatile : nextToken = ITerminalSymbols.TokenNamevolatile; break;
+			case TerminalTokens.TokenNamewhen : nextToken = ITerminalSymbols.TokenNamewhen; break;
+			case TerminalTokens.TokenNamewhile : nextToken = ITerminalSymbols.TokenNamewhile; break;
+			case TerminalTokens.TokenNamewith : nextToken = ITerminalSymbols.TokenNamewith; break;
+			case TerminalTokens.TokenNamewithin : nextToken = ITerminalSymbols.TokenNamewithin; break;
+			default:
+				throw new InvalidInputException("Unknown token (check Scanner/TerminalTokens): " + nextToken); //$NON-NLS-1$
 		}
-		this.taskTags = taskTags;
-		this.isTaskCaseSensitive = isTaskCaseSensitive;
+		return nextToken;
 	}
-}
 
-public PublicScanner(
-		boolean tokenizeComments,
-		boolean tokenizeWhiteSpace,
-		boolean checkNonExternalizedStringLiterals,
-		long sourceLevel,
-		char[][] taskTags,
-		char[][] taskPriorities,
-		boolean isTaskCaseSensitive) {
-
-	this(
-		tokenizeComments,
-		tokenizeWhiteSpace,
-		checkNonExternalizedStringLiterals,
-		sourceLevel,
-		sourceLevel,
-		taskTags,
-		taskPriorities,
-		isTaskCaseSensitive);
-}
-
-public final boolean atEnd() {
-	// This code is not relevant if source is
-	// Only a part of the real stream input
-
-	return this.eofPosition <= this.currentPosition;
-}
-
-// chech presence of task: tags
-// TODO (frederic) see if we need to take unicode characters into account...
-public void checkTaskTag(int commentStart, int commentEnd) throws InvalidInputException {
-	char[] src = this.source;
-
-	// only look for newer task: tags
-	if (this.foundTaskCount > 0
-		&& this.foundTaskPositions[this.foundTaskCount - 1][0] >= commentStart) {
-		return;
+	@Override
+	public char[] getSource(){
+		return this.delegate.getSource();
 	}
-	int foundTaskIndex = this.foundTaskCount;
-	char previous = src[commentStart+1]; // should be '*' or '/'
-	for (
-		int i = commentStart + 2; i < commentEnd && i < this.eofPosition; i++) {
-		char[] tag = null;
-		char[] priority = null;
-		// check for tag occurrence only if not ambiguous with javadoc tag
-		if (previous != '@') {
-			nextTag : for (int itag = 0; itag < this.taskTags.length; itag++) {
-				tag = this.taskTags[itag];
-				int tagLength = tag.length;
-				if (tagLength == 0) continue nextTag;
 
-				// ensure tag is not leaded with letter if tag starts with a letter
-				if (ScannerHelper.isJavaIdentifierStart(this.complianceLevel, tag[0])) {
-					if (ScannerHelper.isJavaIdentifierPart(this.complianceLevel, previous)) {
-						continue nextTag;
-					}
-				}
-
-				for (int t = 0; t < tagLength; t++) {
-					char sc, tc;
-					int x = i+t;
-					if (x >= this.eofPosition || x >= commentEnd) continue nextTag;
-					// case sensitive check
-					if ((sc = src[i + t]) != (tc = tag[t])) {
-						// case insensitive check
-						if (this.isTaskCaseSensitive || (ScannerHelper.toLowerCase(sc) != ScannerHelper.toLowerCase(tc))) {
-							continue nextTag;
-						}
-					}
-				}
-				// ensure tag is not followed with letter if tag finishes with a letter
-				if (i+tagLength < commentEnd && ScannerHelper.isJavaIdentifierPart(this.complianceLevel, src[i+tagLength-1])) {
-					if (ScannerHelper.isJavaIdentifierPart(this.complianceLevel, src[i + tagLength]))
-						continue nextTag;
-				}
-				if (this.foundTaskTags == null) {
-					this.foundTaskTags = new char[5][];
-					this.foundTaskMessages = new char[5][];
-					this.foundTaskPriorities = new char[5][];
-					this.foundTaskPositions = new int[5][];
-				} else if (this.foundTaskCount == this.foundTaskTags.length) {
-					System.arraycopy(this.foundTaskTags, 0, this.foundTaskTags = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount);
-					System.arraycopy(this.foundTaskMessages, 0, this.foundTaskMessages = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount);
-					System.arraycopy(this.foundTaskPriorities, 0, this.foundTaskPriorities = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount);
-					System.arraycopy(this.foundTaskPositions, 0, this.foundTaskPositions = new int[this.foundTaskCount * 2][], 0, this.foundTaskCount);
-				}
-
-				priority = this.taskPriorities != null && itag < this.taskPriorities.length
-							? this.taskPriorities[itag]
-							: null;
-
-				this.foundTaskTags[this.foundTaskCount] = tag;
-				this.foundTaskPriorities[this.foundTaskCount] = priority;
-				this.foundTaskPositions[this.foundTaskCount] = new int[] { i, i + tagLength - 1 };
-				this.foundTaskMessages[this.foundTaskCount] = CharOperation.NO_CHAR;
-				this.foundTaskCount++;
-				i += tagLength - 1; // will be incremented when looping
-				break nextTag;
-			}
-		}
-		previous = src[i];
-	}
-	boolean containsEmptyTask = false;
-	for (int i = foundTaskIndex; i < this.foundTaskCount; i++) {
-		// retrieve message start and end positions
-		int msgStart = this.foundTaskPositions[i][0] + this.foundTaskTags[i].length;
-		int max_value = i + 1 < this.foundTaskCount
-				? this.foundTaskPositions[i + 1][0] - 1
-				: commentEnd - 1;
-		// at most beginning of next task
-		if (max_value < msgStart) {
-			max_value = msgStart; // would only occur if tag is before EOF.
-		}
-		int end = -1;
-		char c;
-		for (int j = msgStart; j < max_value; j++) {
-			if ((c = src[j]) == '\n' || c == '\r') {
-				end = j - 1;
-				break;
-			}
-		}
-		if (end == -1) {
-			for (int j = max_value; j > msgStart; j--) {
-				if ((c = src[j]) == '*') {
-					end = j - 1;
-					break;
-				}
-			}
-			if (end == -1)
-				end = max_value;
-		}
-		if (msgStart == end) {
-			// if the description is empty, we might want to see if two tags are not sharing the same message
-			// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=110797
-			containsEmptyTask = true;
-			continue;
-		}
-		// trim the message
-		// we don't trim the beginning of the message to be able to show it after the task tag
-		while (CharOperation.isWhitespace(src[end]) && msgStart <= end)
-			end--;
-		// update the end position of the task
-		this.foundTaskPositions[i][1] = end;
-		// get the message source
-		final int messageLength = end - msgStart + 1;
-		char[] message = new char[messageLength];
-		System.arraycopy(src, msgStart, message, 0, messageLength);
-		this.foundTaskMessages[i] = message;
-	}
-	if (containsEmptyTask) {
-		for (int i = foundTaskIndex, max = this.foundTaskCount; i < max; i++) {
-			if (this.foundTaskMessages[i].length == 0) {
-				loop: for (int j = i + 1; j < max; j++) {
-					if (this.foundTaskMessages[j].length != 0) {
-						this.foundTaskMessages[i] = this.foundTaskMessages[j];
-						this.foundTaskPositions[i][1] = this.foundTaskPositions[j][1];
-						break loop;
-					}
-				}
-			}
-		}
+	/**
+	 * Reposition the scanner on some portion of the original source. The given endPosition is the last valid position.
+	 * Beyond this position, the scanner will answer EOF tokens (<code>ITerminalSymbols.TokenNameEOF</code>).
+	 *
+	 * @param begin the given start position
+	 * @param end the given end position
+	 */
+	@Override
+	public void resetTo(int begin, int end) {
+		this.delegate.resetTo(begin, end);
 	}
-}
 
-public char[] getCurrentIdentifierSource() {
-	//return the token REAL source (aka unicodes are precomputed)
-	if (this.withoutUnicodePtr != 0) {
-		//0 is used as a fast test flag so the real first char is in position 1
-		char[] result = new char[this.withoutUnicodePtr];
-		System.arraycopy(
-			this.withoutUnicodeBuffer,
-			1,
-			result,
-			0,
-			this.withoutUnicodePtr);
-		return result;
+	/**
+	 * Search the line number corresponding to a specific position
+	 * @param position int
+	 * @return int
+	 */
+	@Override
+	public final int getLineNumber(int position) {
+		return this.delegate.getLineNumber(position);
 	}
-	int length = this.currentPosition - this.startPosition;
-	if (length == this.eofPosition) return this.source;
-	return this.deduplication.sharedCopyOfRange(this.source, this.startPosition, this.currentPosition);
-}
 
-@Override
-public int getCurrentTokenEndPosition(){
-	return this.currentPosition - 1;
-}
-@Override
-public char[] getCurrentTokenSource() {
-	// Return the token REAL source (aka unicodes are precomputed)
-
-	char[] result;
-	if (this.withoutUnicodePtr != 0)
-		// 0 is used as a fast test flag so the real first char is in position 1
-		System.arraycopy(
-			this.withoutUnicodeBuffer,
-			1,
-			result = new char[this.withoutUnicodePtr],
-			0,
-			this.withoutUnicodePtr);
-	else {
-		int length;
-		System.arraycopy(
-			this.source,
-			this.startPosition,
-			result = new char[length = this.currentPosition - this.startPosition],
-			0,
-			length);
+	@Override
+	public final void setSource(char[] sourceString){
+		this.delegate.setSource(sourceString);
 	}
-	return result;
-}
-public final String getCurrentTokenString() {
-	// Return current token as a string
 
-	if (this.withoutUnicodePtr != 0) {
-		// 0 is used as a fast test flag so the real first char is in position 1
-		return new String(
-			this.withoutUnicodeBuffer,
-			1,
-			this.withoutUnicodePtr);
-	}
-	return new String(
-		this.source,
-		this.startPosition,
-		this.currentPosition - this.startPosition);
-}
-public char[] getCurrentTokenSourceString() {
-	//return the token REAL source (aka unicodes are precomputed).
-	//REMOVE the two " that are at the beginning and the end.
-
-	char[] result;
-	if (this.withoutUnicodePtr != 0)
-		//0 is used as a fast test flag so the real first char is in position 1
-		System.arraycopy(this.withoutUnicodeBuffer, 2,
-		//2 is 1 (real start) + 1 (to jump over the ")
-		result = new char[this.withoutUnicodePtr - 2], 0, this.withoutUnicodePtr - 2);
-	else {
-		int length;
-		System.arraycopy(
-			this.source,
-			this.startPosition + 1,
-			result = new char[length = this.currentPosition - this.startPosition - 2],
-			0,
-			length);
-	}
-	return result;
-}
-protected final boolean scanForTextBlockBeginning() {
-	if (this.complianceLevel < ClassFileConstants.JDK13 || !this.previewEnabled) {
-		return false;
-	}
-	try {
-		// Don't change the position and current character unless we are certain
-		// to be dealing with a text block. For producing all errors like before
-		// in case of a valid """ but missing \r or \n, just return false and not
-		// throw any error.
-		int temp = this.currentPosition;
-		if ((this.source[temp++] == '\"' && this.source[temp++] == '\"')) {
-			char c = this.source[temp++];
-			while (ScannerHelper.isWhitespace(c)) {
-				switch (c) {
-					case 10 : /* \ u000a: LINE FEED               */
-					case 13 : /* \ u000d: CARRIAGE RETURN         */
-						this.currentCharacter = c;
-						this.currentPosition = temp;
-						return true;
-					default:
-						break;
-				}
-				c = this.source[temp++];
-			}
-		}
-	} catch(IndexOutOfBoundsException e) {
-		//let it return false;
-	}
-	return false;
-}
-protected final boolean scanForTextBlockClose() throws InvalidInputException {
-	try {
-		if (this.source[this.currentPosition] == '\"' && this.source[this.currentPosition + 1] == '\"') {
-			return true;
-		}
-	} catch(IndexOutOfBoundsException e) {
-		//let it return false;
-	}
-	return false;
-}
-public final String getCurrentStringLiteral() {
-	//return the token REAL source (aka unicodes are precomputed).
-	//REMOVE the two " that are at the beginning and the end.
-
-	if (this.withoutUnicodePtr != 0)
-		//0 is used as a fast test flag so the real first char is in position 1
-		//2 is 1 (real start) + 1 (to jump over the ")
-		return new String(this.withoutUnicodeBuffer, 2, this.withoutUnicodePtr - 2);
-	else {
-		return new String(this.source, this.startPosition + 1, this.currentPosition - this.startPosition - 2);
-	}
-}
-@Override
-public final char[] getRawTokenSource() {
-	int length = this.currentPosition - this.startPosition;
-	char[] tokenSource = new char[length];
-	System.arraycopy(this.source, this.startPosition, tokenSource, 0, length);
-	return tokenSource;
-}
-
-public final char[] getRawTokenSourceEnd() {
-	int length = this.eofPosition - this.currentPosition - 1;
-	char[] sourceEnd = new char[length];
-	System.arraycopy(this.source, this.currentPosition, sourceEnd, 0, length);
-	return sourceEnd;
-}
-
-@Override
-public int getCurrentTokenStartPosition(){
-	return this.startPosition;
-}
-/*
- * Search the source position corresponding to the end of a given line number
- *
- * Line numbers are 1-based, and relative to the scanner initialPosition.
- * Character positions are 0-based.
- *
- * In case the given line number is inconsistent, answers -1.
- */
-@Override
-public final int getLineEnd(int lineNumber) {
-
-	if (this.lineEnds == null || this.linePtr == -1)
-		return -1;
-	if (lineNumber > this.lineEnds.length+1)
-		return -1;
-	if (lineNumber <= 0)
-		return -1;
-	if (lineNumber == this.lineEnds.length + 1)
-		return this.eofPosition;
-	return this.lineEnds[lineNumber-1]; // next line start one character behind the lineEnd of the previous line
-}
-
-@Override
-public final int[] getLineEnds() {
-	//return a bounded copy of this.lineEnds
-	if (this.linePtr == -1) {
-		return EMPTY_LINE_ENDS;
-	}
-	int[] copy;
-	System.arraycopy(this.lineEnds, 0, copy = new int[this.linePtr + 1], 0, this.linePtr + 1);
-	return copy;
-}
-
-/**
- * Search the source position corresponding to the beginning of a given line number
- *
- * Line numbers are 1-based, and relative to the scanner initialPosition.
- * Character positions are 0-based.
- *
- * e.g.	getLineStart(1) --> 0	indicates that the first line starts at character 0.
- *
- * In case the given line number is inconsistent, answers -1.
- *
- * @param lineNumber int
- * @return int
- */
-@Override
-public final int getLineStart(int lineNumber) {
-
-	if (this.lineEnds == null || this.linePtr == -1)
-		return -1;
-	if (lineNumber > this.lineEnds.length + 1)
-		return -1;
-	if (lineNumber <= 0)
-		return -1;
-
-	if (lineNumber == 1)
-		return this.initialPosition;
-	return this.lineEnds[lineNumber-2]+1; // next line start one character behind the lineEnd of the previous line
-}
-public final int getNextChar() {
-	try {
-		if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-			&& (this.source[this.currentPosition] == 'u')) {
-				getNextUnicodeChar();
-		} else {
-			this.unicodeAsBackSlash = false;
-			if (this.withoutUnicodePtr != 0) {
-			    unicodeStore();
-			}
-		}
-		return this.currentCharacter;
-	} catch(IndexOutOfBoundsException | InvalidInputException e) {
-		return -1;
-	}
-}
-public final int getNextCharWithBoundChecks() {
-	if (this.currentPosition >= this.eofPosition) {
-		return -1;
-	}
-	this.currentCharacter = this.source[this.currentPosition++];
-	if (this.currentPosition >= this.eofPosition) {
-		this.unicodeAsBackSlash = false;
-		if (this.withoutUnicodePtr != 0) {
-		    unicodeStore();
-		}
-		return this.currentCharacter;
-	}
-	if (this.currentCharacter == '\\' && this.source[this.currentPosition] == 'u') {
-		try {
-			getNextUnicodeChar();
-		} catch (InvalidInputException e) {
-			return -1;
-		}
-	} else {
-		this.unicodeAsBackSlash = false;
-		if (this.withoutUnicodePtr != 0) {
-		    unicodeStore();
-		}
-	}
-	return this.currentCharacter;
-}
-public final boolean getNextChar(char testedChar) {
-	//BOOLEAN
-	//handle the case of unicode.
-	//when a unicode appears then we must use a buffer that holds char internal values
-	//At the end of this method currentCharacter holds the new visited char
-	//and currentPosition points right next after it
-	//Both previous lines are true if the currentCharacter is == to the testedChar
-	//On false, no side effect has occured.
-
-	//ALL getNextChar.... ARE OPTIMIZED COPIES
-
-	if (this.currentPosition >= this.eofPosition) { // handle the obvious case upfront
-		this.unicodeAsBackSlash = false;
-		return false;
-	}
-
-	int temp = this.currentPosition;
-	try {
-		if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-			&& (this.source[this.currentPosition] == 'u')) {
-			getNextUnicodeChar();
-			if (this.currentCharacter != testedChar) {
-				this.currentPosition = temp;
-				this.withoutUnicodePtr--;
-				return false;
-			}
-			return true;
-		} //-------------end unicode traitement--------------
-		else {
-			if (this.currentCharacter != testedChar) {
-				this.currentPosition = temp;
-				return false;
-			}
-			this.unicodeAsBackSlash = false;
-			if (this.withoutUnicodePtr != 0)
-				unicodeStore();
-			return true;
-		}
-	} catch(IndexOutOfBoundsException | InvalidInputException e) {
-		this.unicodeAsBackSlash = false;
-		this.currentPosition = temp;
-		return false;
-	}
-}
-public final int getNextChar(char testedChar1, char testedChar2) {
-	//INT 0 : testChar1 \\\\///\\\\ 1 : testedChar2 \\\\///\\\\ -1 : others
-	//test can be done with (x==0) for the first and (x>0) for the second
-	//handle the case of unicode.
-	//when a unicode appears then we must use a buffer that holds char internal values
-	//At the end of this method currentCharacter holds the new visited char
-	//and currentPosition points right next after it
-	//Both previous lines are true if the currentCharacter is == to the testedChar1/2
-	//On false, no side effect has occured.
-
-	//ALL getNextChar.... ARE OPTIMIZED COPIES
-	if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront
-		return -1;
-
-	int temp = this.currentPosition;
-	try {
-		int result;
-		if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-			&& (this.source[this.currentPosition] == 'u')) {
-			getNextUnicodeChar();
-			if (this.currentCharacter == testedChar1) {
-				result = 0;
-			} else if (this.currentCharacter == testedChar2) {
-				result = 1;
-			} else {
-				this.currentPosition = temp;
-				this.withoutUnicodePtr--;
-				result = -1;
-			}
-			return result;
-		} else {
-			if (this.currentCharacter == testedChar1) {
-				result = 0;
-			} else if (this.currentCharacter == testedChar2) {
-				result = 1;
-			} else {
-				this.currentPosition = temp;
-				return -1;
-			}
-
-			if (this.withoutUnicodePtr != 0)
-				unicodeStore();
-			return result;
-		}
-	} catch(IndexOutOfBoundsException | InvalidInputException e) {
-		this.currentPosition = temp;
-		return -1;
-	}
-}
-/*
- * This method consumes digits as well as underscores if underscores are located between digits
- * @throws InvalidInputException if underscores are not located between digits or if underscores are used in source < 1.7
- */
-private final void consumeDigits(int radix) throws InvalidInputException {
-	consumeDigits(radix, false);
-}
-/*
- * This method consumes digits as well as underscores if underscores are located between digits
- * @throws InvalidInputException if underscores are not located between digits or if underscores are used in source < 1.7
- */
-private final void consumeDigits(int radix, boolean expectingDigitFirst) throws InvalidInputException {
-	final int USING_UNDERSCORE = 1;
-	final int INVALID_POSITION = 2;
-	switch(consumeDigits0(radix, USING_UNDERSCORE, INVALID_POSITION, expectingDigitFirst)) {
-		case USING_UNDERSCORE :
-			if (this.sourceLevel < ClassFileConstants.JDK1_7) {
-				throw new InvalidInputException(UNDERSCORES_IN_LITERALS_NOT_BELOW_17);
-			}
-			break;
-		case INVALID_POSITION :
-			if (this.sourceLevel < ClassFileConstants.JDK1_7) {
-				throw new InvalidInputException(UNDERSCORES_IN_LITERALS_NOT_BELOW_17);
-			}
-			throw new InvalidInputException(INVALID_UNDERSCORE);
-	}
-}
-private final int consumeDigits0(int radix, int usingUnderscore, int invalidPosition, boolean expectingDigitFirst) throws InvalidInputException {
-	int kind = 0;
-	if (getNextChar('_')) {
-		if (expectingDigitFirst) {
-			return invalidPosition;
-		}
-		kind = usingUnderscore;
-		while (getNextChar('_')) {/*empty */}
-	}
-	if (getNextCharAsDigit(radix)) {
-		// continue to read digits or underscore
-		while (getNextCharAsDigit(radix)) {/*empty */}
-		int kind2 = consumeDigits0(radix, usingUnderscore, invalidPosition, false);
-		if (kind2 == 0) {
-			return kind;
-		}
-		return kind2;
-	}
-	if (kind == usingUnderscore) return invalidPosition;
-	return kind;
-}
-public final boolean getNextCharAsDigit() throws InvalidInputException {
-	//BOOLEAN
-	//handle the case of unicode.
-	//when a unicode appears then we must use a buffer that holds char internal values
-	//At the end of this method currentCharacter holds the new visited char
-	//and currentPosition points right next after it
-	//Both previous lines are true if the currentCharacter is a digit
-	//On false, no side effect has occured.
-
-	//ALL getNextChar.... ARE OPTIMIZED COPIES
-	if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront
-		return false;
-
-	int temp = this.currentPosition;
-	try {
-		if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-			&& (this.source[this.currentPosition] == 'u')) {
-			getNextUnicodeChar();
-			if (!ScannerHelper.isDigit(this.currentCharacter)) {
-				this.currentPosition = temp;
-				this.withoutUnicodePtr--;
-				return false;
-			}
-			return true;
-		} else {
-			if (!ScannerHelper.isDigit(this.currentCharacter)) {
-				this.currentPosition = temp;
-				return false;
-			}
-			if (this.withoutUnicodePtr != 0)
-				unicodeStore();
-			return true;
-		}
-	} catch(IndexOutOfBoundsException | InvalidInputException e) {
-		this.currentPosition = temp;
-		return false;
-	}
-}
-public final boolean getNextCharAsDigit(int radix) {
-	//BOOLEAN
-	//handle the case of unicode.
-	//when a unicode appears then we must use a buffer that holds char internal values
-	//At the end of this method currentCharacter holds the new visited char
-	//and currentPosition points right next after it
-	//Both previous lines are true if the currentCharacter is a digit base on radix
-	//On false, no side effect has occured.
-
-	//ALL getNextChar.... ARE OPTIMIZED COPIES
-	if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront
-		return false;
-
-	int temp = this.currentPosition;
-	try {
-		if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-			&& (this.source[this.currentPosition] == 'u')) {
-			getNextUnicodeChar();
-			if (ScannerHelper.digit(this.currentCharacter, radix) == -1) {
-				this.currentPosition = temp;
-				this.withoutUnicodePtr--;
-				return false;
-			}
-			return true;
-		} else {
-			if (ScannerHelper.digit(this.currentCharacter, radix) == -1) {
-				this.currentPosition = temp;
-				return false;
-			}
-			if (this.withoutUnicodePtr != 0)
-				unicodeStore();
-			return true;
-		}
-	} catch(IndexOutOfBoundsException | InvalidInputException e) {
-		this.currentPosition = temp;
-		return false;
-	}
-}
-public boolean getNextCharAsJavaIdentifierPartWithBoundCheck() {
-	//BOOLEAN
-	//handle the case of unicode.
-	//when a unicode appears then we must use a buffer that holds char internal values
-	//At the end of this method currentCharacter holds the new visited char
-	//and currentPosition points right next after it
-	//Both previous lines are true if the currentCharacter is a JavaIdentifierPart
-	//On false, no side effect has occured.
-
-	//ALL getNextChar.... ARE OPTIMIZED COPIES
-	int pos = this.currentPosition;
-	if (pos >= this.eofPosition) // handle the obvious case upfront
-		return false;
-
-	int temp2 = this.withoutUnicodePtr;
-	try {
-		boolean unicode = false;
-		this.currentCharacter = this.source[this.currentPosition++];
-		if (this.currentPosition < this.eofPosition) {
-			if (this.currentCharacter == '\\' && this.source[this.currentPosition] == 'u') {
-				getNextUnicodeChar();
-				unicode = true;
-			}
-		}
-		char c = this.currentCharacter;
-		boolean isJavaIdentifierPart = false;
-		if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
-			if (this.complianceLevel < ClassFileConstants.JDK1_5) {
-				this.currentPosition = pos;
-				this.withoutUnicodePtr = temp2;
-				return false;
-			}
-			// Unicode 4 detection
-			char low = (char) getNextCharWithBoundChecks();
-			if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
-				// illegal low surrogate
-				this.currentPosition = pos;
-				this.withoutUnicodePtr = temp2;
-				return false;
-			}
-			isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c, low);
-		}
-		else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
-			this.currentPosition = pos;
-			this.withoutUnicodePtr = temp2;
-			return false;
-		} else {
-			isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c);
-		}
-		if (unicode) {
-			if (!isJavaIdentifierPart) {
-				this.currentPosition = pos;
-				this.withoutUnicodePtr = temp2;
-				return false;
-			}
-			return true;
-		} else {
-			if (!isJavaIdentifierPart) {
-				this.currentPosition = pos;
-				return false;
-			}
-
-			if (this.withoutUnicodePtr != 0)
-			    unicodeStore();
-			return true;
-		}
-	} catch(InvalidInputException e) {
-		this.currentPosition = pos;
-		this.withoutUnicodePtr = temp2;
-		return false;
-	}
-}
-public boolean getNextCharAsJavaIdentifierPart() {
-	//BOOLEAN
-	//handle the case of unicode.
-	//when a unicode appears then we must use a buffer that holds char internal values
-	//At the end of this method currentCharacter holds the new visited char
-	//and currentPosition points right next after it
-	//Both previous lines are true if the currentCharacter is a JavaIdentifierPart
-	//On false, no side effect has occured.
-
-	//ALL getNextChar.... ARE OPTIMIZED COPIES
-	int pos;
-	if ((pos = this.currentPosition) >= this.eofPosition) // handle the obvious case upfront
-		return false;
-
-	int temp2 = this.withoutUnicodePtr;
-	try {
-		boolean unicode = false;
-		if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-			&& (this.source[this.currentPosition] == 'u')) {
-			getNextUnicodeChar();
-			unicode = true;
-		}
-		char c = this.currentCharacter;
-		boolean isJavaIdentifierPart = false;
-		if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
-			if (this.complianceLevel < ClassFileConstants.JDK1_5) {
-				this.currentPosition = pos;
-				this.withoutUnicodePtr = temp2;
-				return false;
-			}
-			// Unicode 4 detection
-			char low = (char) getNextChar();
-			if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
-				// illegal low surrogate
-				this.currentPosition = pos;
-				this.withoutUnicodePtr = temp2;
-				return false;
-			}
-			isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c, low);
-		}
-		else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
-			this.currentPosition = pos;
-			this.withoutUnicodePtr = temp2;
-			return false;
-		} else {
-			isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c);
-		}
-		if (unicode) {
-			if (!isJavaIdentifierPart) {
-				this.currentPosition = pos;
-				this.withoutUnicodePtr = temp2;
-				return false;
-			}
-			return true;
-		} else {
-			if (!isJavaIdentifierPart) {
-				this.currentPosition = pos;
-				return false;
-			}
-
-			if (this.withoutUnicodePtr != 0)
-			    unicodeStore();
-			return true;
-		}
-	} catch(IndexOutOfBoundsException | InvalidInputException e) {
-		this.currentPosition = pos;
-		this.withoutUnicodePtr = temp2;
-		return false;
-	}
-}
-/*
- * External API in JavaConventions.
- * This is used to optimize the case where the scanner is used to scan a single identifier.
- * In this case, the AIOOBE is slower to handle than a bound check
- */
-public int scanIdentifier() throws InvalidInputException {
-	int whiteStart = 0;
-	while (true) { //loop for jumping over comments
-		this.withoutUnicodePtr = 0;
-		//start with a new token (even comment written with unicode )
-		// ---------Consume white space and handles startPosition---------
-		whiteStart = this.currentPosition;
-		boolean isWhiteSpace, hasWhiteSpaces = false;
-		int offset;
-		int unicodePtr;
-		boolean checkIfUnicode = false;
-		do {
-			unicodePtr = this.withoutUnicodePtr;
-			offset = this.currentPosition;
-			this.startPosition = this.currentPosition;
-			if (this.currentPosition < this.eofPosition) {
-				this.currentCharacter = this.source[this.currentPosition++];
-				checkIfUnicode = this.currentPosition < this.eofPosition
-						&& this.currentCharacter == '\\'
-						&& this.source[this.currentPosition] == 'u';
-			} else if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) {
-				// reposition scanner in case we are interested by spaces as tokens
-				this.currentPosition--;
-				this.startPosition = whiteStart;
-				return TokenNameWHITESPACE;
-			} else {
-				return TokenNameEOF;
-			}
-			if (checkIfUnicode) {
-				isWhiteSpace = jumpOverUnicodeWhiteSpace();
-				offset = this.currentPosition - offset;
-			} else {
-				offset = this.currentPosition - offset;
-				// inline version of:
-				//isWhiteSpace =
-				//	(this.currentCharacter == ' ') || ScannerHelper.isWhitespace(this.currentCharacter);
-				switch (this.currentCharacter) {
-					case 10 : /* \ u000a: LINE FEED               */
-					case 12 : /* \ u000c: FORM FEED               */
-					case 13 : /* \ u000d: CARRIAGE RETURN         */
-					case 32 : /* \ u0020: SPACE                   */
-					case 9 : /* \ u0009: HORIZONTAL TABULATION   */
-						isWhiteSpace = true;
-						break;
-					default :
-						isWhiteSpace = false;
-				}
-			}
-			if (isWhiteSpace) {
-				hasWhiteSpaces = true;
-			}
-		} while (isWhiteSpace);
-		if (hasWhiteSpaces) {
-			if (this.tokenizeWhiteSpace) {
-				// reposition scanner in case we are interested by spaces as tokens
-				this.currentPosition-=offset;
-				this.startPosition = whiteStart;
-				if (checkIfUnicode) {
-					this.withoutUnicodePtr = unicodePtr;
-				}
-				return TokenNameWHITESPACE;
-			} else if (checkIfUnicode) {
-				this.withoutUnicodePtr = 0;
-				unicodeStore();
-			} else {
-				this.withoutUnicodePtr = 0;
-			}
-		}
-		char c = this.currentCharacter;
-		if (c < ScannerHelper.MAX_OBVIOUS) {
-			if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) {
-				return scanIdentifierOrKeywordWithBoundCheck();
-			}
-			return TokenNameERROR;
-		}
-		boolean isJavaIdStart;
-		if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
-			if (this.complianceLevel < ClassFileConstants.JDK1_5) {
-				throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-			}
-			// Unicode 4 detection
-			char low = (char) getNextCharWithBoundChecks();
-			if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
-				// illegal low surrogate
-				throw new InvalidInputException(INVALID_LOW_SURROGATE);
-			}
-			isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low);
-		} else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
-			if (this.complianceLevel < ClassFileConstants.JDK1_5) {
-				throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-			}
-			throw new InvalidInputException(INVALID_HIGH_SURROGATE);
-		} else {
-			// optimized case already checked
-			isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c);
-		}
-		if (isJavaIdStart)
-			return scanIdentifierOrKeywordWithBoundCheck();
-		return TokenNameERROR;
-	}
-}
-@Override
-public int getNextToken() throws InvalidInputException {
-//{ObjectTeams: support '.' 'team':
-	if (this._dotSeen > 0)
-		this._dotSeen--; // "aging"
-// SH}
-	this.wasAcr = false;
-	if (this.diet) {
-		jumpOverMethodBody();
-		this.diet = false;
-		return this.currentPosition > this.eofPosition ? TokenNameEOF : TokenNameRBRACE;
-	}
-	int whiteStart = 0;
-	try {
-		while (true) { //loop for jumping over comments
-			this.withoutUnicodePtr = 0;
-			//start with a new token (even comment written with unicode )
-
-			// ---------Consume white space and handles startPosition---------
-			whiteStart = this.currentPosition;
-			boolean isWhiteSpace, hasWhiteSpaces = false;
-			int offset;
-			int unicodePtr;
-			boolean checkIfUnicode = false;
-			do {
-				unicodePtr = this.withoutUnicodePtr;
-				offset = this.currentPosition;
-				this.startPosition = this.currentPosition;
-				boolean repositionNeeded = false;
-				if(this.currentPosition < this.source.length){
-					this.currentCharacter = this.source[this.currentPosition];
-					this.currentPosition++;
-					if(this.currentCharacter == '\\') {
-						if (this.currentPosition < this.source.length) {
-							checkIfUnicode = this.source[this.currentPosition] == 'u';
-						} else {
-							repositionNeeded = true;
-						}
-					} else {
-						checkIfUnicode = false;
-					}
-				} else {
-					this.currentPosition++;
-					repositionNeeded = true;
-				}
-				if(repositionNeeded){
-					if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) {
-						// reposition scanner in case we are interested by spaces as tokens
-						this.currentPosition--;
-						this.startPosition = whiteStart;
-						return TokenNameWHITESPACE;
-					}
-					if (this.currentPosition > this.eofPosition)
-						return TokenNameEOF;
-				}
-				if (this.currentPosition > this.eofPosition) {
-					if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) {
-						this.currentPosition--;
-						// reposition scanner in case we are interested by spaces as tokens
-						this.startPosition = whiteStart;
-						return TokenNameWHITESPACE;
-					}
-					return TokenNameEOF;
-				}
-				if (checkIfUnicode) {
-					isWhiteSpace = jumpOverUnicodeWhiteSpace();
-					offset = this.currentPosition - offset;
-				} else {
-					offset = this.currentPosition - offset;
-					if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
-						if (this.recordLineSeparator) {
-							pushLineSeparator();
-						}
-					}
-					// inline version of:
-					//isWhiteSpace =
-					//	(this.currentCharacter == ' ') || ScannerHelper.isWhitespace(this.currentCharacter);
-					switch (this.currentCharacter) {
-						case 10 : /* \ u000a: LINE FEED               */
-						case 12 : /* \ u000c: FORM FEED               */
-						case 13 : /* \ u000d: CARRIAGE RETURN         */
-						case 32 : /* \ u0020: SPACE                   */
-						case 9 : /* \ u0009: HORIZONTAL TABULATION   */
-							isWhiteSpace = true;
-							break;
-						default :
-							isWhiteSpace = false;
-					}
-				}
-				if (isWhiteSpace) {
-					hasWhiteSpaces = true;
-				}
-			} while (isWhiteSpace);
-			if (hasWhiteSpaces) {
-				if (this.tokenizeWhiteSpace) {
-					// reposition scanner in case we are interested by spaces as tokens
-					this.currentPosition-=offset;
-					this.startPosition = whiteStart;
-					if (checkIfUnicode) {
-						this.withoutUnicodePtr = unicodePtr;
-					}
-					return TokenNameWHITESPACE;
-				} else if (checkIfUnicode) {
-					this.withoutUnicodePtr = 0;
-					unicodeStore();
-				} else {
-					this.withoutUnicodePtr = 0;
-				}
-			}
-			// ---------Identify the next token-------------
-			switch (this.currentCharacter) {
-				case '@' :
-/*					if (this.sourceLevel >= ClassFileConstants.JDK1_5) {
-						return TokenNameAT;
-					} else {
-						return TokenNameERROR;
-					}*/
-					return TokenNameAT;
-				case '(' :
-					return TokenNameLPAREN;
-				case ')' :
-					return TokenNameRPAREN;
-				case '{' :
-					return TokenNameLBRACE;
-				case '}' :
-					return TokenNameRBRACE;
-				case '[' :
-					return TokenNameLBRACKET;
-				case ']' :
-					return TokenNameRBRACKET;
-				case ';' :
-					return TokenNameSEMICOLON;
-				case ',' :
-					return TokenNameCOMMA;
-				case '.' :
-					if (getNextCharAsDigit()) {
-						return scanNumber(true);
-					}
-					int temp = this.currentPosition;
-					if (getNextChar('.')) {
-						if (getNextChar('.')) {
-							return TokenNameELLIPSIS;
-						} else {
-							this.currentPosition = temp;
-							return TokenNameDOT;
-						}
-					} else {
-//{ObjectTeams: record the one '.':
-						this._dotSeen = 2; // now
-// SH}
-						this.currentPosition = temp;
-						return TokenNameDOT;
-					}
-				case '+' :
-					{
-						int test;
-						if ((test = getNextChar('+', '=')) == 0)
-							return TokenNamePLUS_PLUS;
-						if (test > 0)
-							return TokenNamePLUS_EQUAL;
-						return TokenNamePLUS;
-					}
-				case '-' :
-					{
-						int test;
-						if ((test = getNextChar('-', '=')) == 0)
-							return TokenNameMINUS_MINUS;
-						if (test > 0)
-							return TokenNameMINUS_EQUAL;
-// FIXME:
-						if (getNextChar('>'))
-							return TokenNameARROW;
-//{ObjectTeams: check for callout binding after '-' tokens
-						else {
-							if (test < 0 && this._isOTSource)
-								if (getNextChar('>')) {
-									this._calloutSeen = true;
-									return TokenNameBINDOUT;
-								}
-						}
-// Markus Witte}
-						return TokenNameMINUS;
-					}
-				case '~' :
-					return TokenNameTWIDDLE;
-				case '!' :
-					if (getNextChar('='))
-						return TokenNameNOT_EQUAL;
-					return TokenNameNOT;
-				case '*' :
-					if (getNextChar('='))
-						return TokenNameMULTIPLY_EQUAL;
-					return TokenNameMULTIPLY;
-				case '%' :
-					if (getNextChar('='))
-						return TokenNameREMAINDER_EQUAL;
-					return TokenNameREMAINDER;
-				case '<' :
-					{
-						int test;
-						if ((test = getNextChar('=', '<')) == 0)
-							return TokenNameLESS_EQUAL;
-						if (test > 0) {
-							if (getNextChar('='))
-								return TokenNameLEFT_SHIFT_EQUAL;
-							return TokenNameLEFT_SHIFT;
-						}
-//{ObjectTeams: check for callin binding after '<' tokens
-						else {
-							if (test < 0  && this._isOTSource)
-								if (getNextChar('-')) {
-									this._callinSeen = true;
-									return TokenNameBINDIN;
-								}
-						}
-// Markus Witte}
-						return TokenNameLESS;
-					}
-				case '>' :
-					{
-						int test;
-						if (this.returnOnlyGreater) {
-							return TokenNameGREATER;
-						}
-						if ((test = getNextChar('=', '>')) == 0)
-							return TokenNameGREATER_EQUAL;
-						if (test > 0) {
-							if ((test = getNextChar('=', '>')) == 0)
-								return TokenNameRIGHT_SHIFT_EQUAL;
-							if (test > 0) {
-								if (getNextChar('='))
-									return TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL;
-								return TokenNameUNSIGNED_RIGHT_SHIFT;
-							}
-							return TokenNameRIGHT_SHIFT;
-						}
-						return TokenNameGREATER;
-					}
-				case '=' :
-//{ObjectTeams: check for callout override after '=' tokens
-				/* @original
-					if (getNextChar('='))
-						return TokenNameEQUAL_EQUAL;
-					return TokenNameEQUAL;
-				*/
-				{
-					int test;
-					if ((test = getNextChar('=', '>')) == 0)
-						return TokenNameEQUAL_EQUAL;
-					if (test > 0) {
-						this._calloutSeen = true;
-						return TokenNameCALLOUT_OVERRIDE;
-					} else {
-						return TokenNameEQUAL;
-					}
-				}
-// Markus Witte}
-				case '&' :
-					{
-						int test;
-						if ((test = getNextChar('&', '=')) == 0)
-							return TokenNameAND_AND;
-						if (test > 0)
-							return TokenNameAND_EQUAL;
-						return TokenNameAND;
-					}
-				case '|' :
-					{
-						int test;
-						if ((test = getNextChar('|', '=')) == 0)
-							return TokenNameOR_OR;
-						if (test > 0)
-							return TokenNameOR_EQUAL;
-						return TokenNameOR;
-					}
-				case '^' :
-					if (getNextChar('='))
-						return TokenNameXOR_EQUAL;
-					return TokenNameXOR;
-				case '?' :
-					return TokenNameQUESTION;
-				case ':' :
-					if (getNextChar(':'))
-						return TokenNameCOLON_COLON;
-					return TokenNameCOLON;
-				case '\'' :
-					{
-						int test;
-						if ((test = getNextChar('\n', '\r')) == 0) {
-							throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
-						}
-						if (test > 0) {
-							// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
-							for (int lookAhead = 0; lookAhead < 3; lookAhead++) {
-								if (this.currentPosition + lookAhead == this.eofPosition)
-									break;
-								if (this.source[this.currentPosition + lookAhead] == '\n')
-									break;
-								if (this.source[this.currentPosition + lookAhead] == '\'') {
-									this.currentPosition += lookAhead + 1;
-									break;
-								}
-							}
-							throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
-						}
-					}
-					if (getNextChar('\'')) {
-						// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
-						for (int lookAhead = 0; lookAhead < 3; lookAhead++) {
-							if (this.currentPosition + lookAhead == this.eofPosition)
-								break;
-							if (this.source[this.currentPosition + lookAhead] == '\n')
-								break;
-							if (this.source[this.currentPosition + lookAhead] == '\'') {
-								this.currentPosition += lookAhead + 1;
-								break;
-							}
-						}
-						throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
-					}
-					if (getNextChar('\\')) {
-						if (this.unicodeAsBackSlash) {
-							// consume next character
-							this.unicodeAsBackSlash = false;
-							if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
-								getNextUnicodeChar();
-							} else {
-								if (this.withoutUnicodePtr != 0) {
-									unicodeStore();
-								}
-							}
-						} else {
-							this.currentCharacter = this.source[this.currentPosition++];
-						}
-						scanEscapeCharacter();
-					} else { // consume next character
-						this.unicodeAsBackSlash = false;
-						checkIfUnicode = false;
-						try {
-							checkIfUnicode = ((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-							&& (this.source[this.currentPosition] == 'u');
-						} catch(IndexOutOfBoundsException e) {
-							this.currentPosition--;
-							throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
-						}
-						if (checkIfUnicode) {
-							getNextUnicodeChar();
-						} else {
-							if (this.withoutUnicodePtr != 0) {
-								unicodeStore();
-							}
-						}
-					}
-					if (getNextChar('\''))
-						return TokenNameCharacterLiteral;
-					// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
-					for (int lookAhead = 0; lookAhead < 20; lookAhead++) {
-						if (this.currentPosition + lookAhead == this.eofPosition)
-							break;
-						if (this.source[this.currentPosition + lookAhead] == '\n')
-							break;
-						if (this.source[this.currentPosition + lookAhead] == '\'') {
-							this.currentPosition += lookAhead + 1;
-							break;
-						}
-					}
-					throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
-				case '"' :
-					boolean isTextBlock = false;
-					int lastQuotePos = 0;
-					try {
-						// consume next character
-						this.unicodeAsBackSlash = false;
-						boolean isUnicode = false;
-						isTextBlock = scanForTextBlockBeginning();
-						if (!isTextBlock) {
-							if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-									&& (this.source[this.currentPosition] == 'u')) {
-								getNextUnicodeChar();
-								isUnicode = true;
-							} else {
-								if (this.withoutUnicodePtr != 0) {
-									unicodeStore();
-								}
-							}
-						}
-						this.rawStart = this.currentPosition - this.startPosition;
-						int terminators = 0;
-						while (this.currentPosition <= this.eofPosition) {
-							if (this.currentCharacter == '"') {
-								if (!isTextBlock) {
-									return ITerminalSymbols.TokenNameStringLiteral;
-								}
-								lastQuotePos = this.currentPosition;
-								// look for text block delimiter
-								if (scanForTextBlockClose()) {
-									// Account for just the snippet being passed around
-									// If already at the EOF, bail out.
-									if (this.currentPosition + 2 < this.source.length && this.source[this.currentPosition + 2] == '"') {
-										terminators++;
-										if (terminators > 2)
-											throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK);
-									} else {
-										this.currentPosition += 2;
-										return ITerminalSymbols.TokenNameTextBlock;
-									}
-								}
-								if (this.withoutUnicodePtr != 0) {
-									unicodeStore();
-								}
-							} else {
-								terminators = 0;
-							}
-							/**** \r and \n are not valid in string literals ****/
-							if (!isTextBlock &&  (this.currentCharacter == '\n') || (this.currentCharacter == '\r')) {
-								// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
-								if (isUnicode) {
-									int start = this.currentPosition;
-									for (int lookAhead = 0; lookAhead < 50; lookAhead++) {
-										if (this.currentPosition >= this.eofPosition) {
-											this.currentPosition = start;
-											break;
-										}
-										if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
-											isUnicode = true;
-											getNextUnicodeChar();
-										} else {
-											isUnicode = false;
-										}
-										if (!isUnicode && this.currentCharacter == '\n') {
-											this.currentPosition--; // set current position on new line character
-											break;
-										}
-										if (this.currentCharacter == '\"') {
-											throw new InvalidInputException(INVALID_CHAR_IN_STRING);
-										}
-									}
-								} else {
-									this.currentPosition--; // set current position on new line character
-								}
-								throw new InvalidInputException(INVALID_CHAR_IN_STRING);
-							}
-							if (this.currentCharacter == '\\') {
-								if (this.unicodeAsBackSlash) {
-									this.withoutUnicodePtr--;
-									// consume next character
-									this.unicodeAsBackSlash = false;
-									if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
-										getNextUnicodeChar();
-										isUnicode = true;
-										this.withoutUnicodePtr--;
-									} else {
-										isUnicode = false;
-									}
-								} else {
-									if (this.withoutUnicodePtr == 0) {
-										unicodeInitializeBuffer(this.currentPosition - this.startPosition);
-									}
-									this.withoutUnicodePtr --;
-									this.currentCharacter = this.source[this.currentPosition++];
-								}
-								// we need to compute the escape character in a separate buffer
-								scanEscapeCharacter();
-								if (this.withoutUnicodePtr != 0) {
-									unicodeStore();
-								}
-							}
-							// consume next character
-							if (this.currentPosition >= this.eofPosition) {
-								break;
-							}
-							this.unicodeAsBackSlash = false;
-							if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-									&& (this.source[this.currentPosition] == 'u')) {
-								getNextUnicodeChar();
-								isUnicode = true;
-							} else {
-								isUnicode = false;
-								if (isTextBlock && this.currentCharacter == '"')
-									continue;
-								if (this.withoutUnicodePtr != 0) {
-									unicodeStore();
-								}
-							}
-						}
-						if (isTextBlock) {
-							if (lastQuotePos > 0)
-								this.currentPosition = lastQuotePos;
-							this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.rawStart;
-							throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK);
-						} else {
-							throw new InvalidInputException(UNTERMINATED_STRING);
-						}
-					} catch (IndexOutOfBoundsException e) {
-						if (isTextBlock) {
-							this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.rawStart;
-							throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK);
-						} else {
-							this.currentPosition--;
-							throw new InvalidInputException(UNTERMINATED_STRING);
-						}
-					} catch (InvalidInputException e) {
-						if (e.getMessage().equals(INVALID_ESCAPE)) {
-							// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
-							for (int lookAhead = 0; lookAhead < 50; lookAhead++) {
-								if (this.currentPosition + lookAhead == this.eofPosition)
-									break;
-								if (this.source[this.currentPosition + lookAhead] == '\n')
-									break;
-								if (this.source[this.currentPosition + lookAhead] == '\"') {
-									this.currentPosition += lookAhead + 1;
-									break;
-								}
-							}
-
-						}
-						throw e; // rethrow
-					}
-				case '/' :
-					if (!this.skipComments) {
-						int test = getNextChar('/', '*');
-						if (test == 0) { //line comment
-							this.lastCommentLinePosition = this.currentPosition;
-							try { //get the next char
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-								}
-
-								//handle the \\u case manually into comment
-								if (this.currentCharacter == '\\') {
-									if (this.source[this.currentPosition] == '\\')
-										this.currentPosition++;
-								} //jump over the \\
-								boolean isUnicode = false;
-								while (this.currentCharacter != '\r' && this.currentCharacter != '\n') {
-									if (this.currentPosition >= this.eofPosition) {
-										this.lastCommentLinePosition = this.currentPosition;
-										this.currentPosition ++;
-										// this avoids duplicating the code in the catch(IndexOutOfBoundsException e)
-										throw new IndexOutOfBoundsException();
-									}
-									this.lastCommentLinePosition = this.currentPosition;
-									//get the next char
-									isUnicode = false;
-									if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-											&& (this.source[this.currentPosition] == 'u')) {
-										getNextUnicodeChar();
-										isUnicode = true;
-									}
-									//handle the \\u case manually into comment
-									if (this.currentCharacter == '\\') {
-										if (this.source[this.currentPosition] == '\\')
-											this.currentPosition++;
-									} //jump over the \\
-								}
-								/*
-								 * We need to completely consume the line break
-								 */
-								if (this.currentCharacter == '\r'
-										&& this.eofPosition > this.currentPosition) {
-									if (this.source[this.currentPosition] == '\n') {
-										this.currentPosition++;
-										this.currentCharacter = '\n';
-									} else if ((this.source[this.currentPosition] == '\\')
-										&& (this.source[this.currentPosition + 1] == 'u')) {
-										getNextUnicodeChar();
-										isUnicode = true;
-									}
-								}
-								recordComment(TokenNameCOMMENT_LINE);
-								if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition);
-								if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
-									if (this.checkNonExternalizedStringLiterals &&
-											this.lastPosition < this.currentPosition) {
-										parseTags();
-									}
-									if (this.recordLineSeparator) {
-										if (isUnicode) {
-											pushUnicodeLineSeparator();
-										} else {
-											pushLineSeparator();
-										}
-									}
-								}
-								if (this.tokenizeComments) {
-									return TokenNameCOMMENT_LINE;
-								}
-							} catch (IndexOutOfBoundsException e) {
-								this.currentPosition--;
-								recordComment(TokenNameCOMMENT_LINE);
-								if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition);
-								if (this.checkNonExternalizedStringLiterals &&
-										this.lastPosition < this.currentPosition) {
-									parseTags();
-								}
-								if (this.tokenizeComments) {
-									return TokenNameCOMMENT_LINE;
-								} else {
-									this.currentPosition++;
-								}
-							}
-							break;
-						}
-						if (test > 0) { //traditional and javadoc comment
-							try { //get the next char
-								boolean isJavadoc = false, star = false;
-								boolean isUnicode = false;
-								int previous;
-								// consume next character
-								this.unicodeAsBackSlash = false;
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-									&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-									isUnicode = true;
-								} else {
-									isUnicode = false;
-									if (this.withoutUnicodePtr != 0) {
-										unicodeStore();
-									}
-								}
-
-								if (this.currentCharacter == '*') {
-									isJavadoc = true;
-									star = true;
-								}
-								if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
-									if (this.recordLineSeparator) {
-										if (isUnicode) {
-											pushUnicodeLineSeparator();
-										} else {
-											pushLineSeparator();
-										}
-									}
-								}
-								isUnicode = false;
-								previous = this.currentPosition;
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-									&& (this.source[this.currentPosition] == 'u')) {
-									//-------------unicode traitement ------------
-									getNextUnicodeChar();
-									isUnicode = true;
-								} else {
-									isUnicode = false;
-								}
-								//handle the \\u case manually into comment
-								if (this.currentCharacter == '\\') {
-									if (this.source[this.currentPosition] == '\\')
-										this.currentPosition++; //jump over the \\
-								}
-								// empty comment is not a javadoc /**/
-								if (this.currentCharacter == '/') {
-									isJavadoc = false;
-								}
-								//loop until end of comment */
-								int firstTag = 0;
-								while ((this.currentCharacter != '/') || (!star)) {
-									if (this.currentPosition >= this.eofPosition) {
-										throw new InvalidInputException(UNTERMINATED_COMMENT);
-									}
-									if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
-										if (this.recordLineSeparator) {
-											if (isUnicode) {
-												pushUnicodeLineSeparator();
-											} else {
-												pushLineSeparator();
-											}
-										}
-									}
-									switch (this.currentCharacter) {
-										case '*':
-											star = true;
-											break;
-										case '@':
-											if (firstTag == 0 && this.isFirstTag()) {
-												firstTag = previous;
-											}
-											//$FALL-THROUGH$ default case to set star to false
-										default:
-											star = false;
-									}
-									//get next char
-									previous = this.currentPosition;
-									if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-										//-------------unicode traitement ------------
-										getNextUnicodeChar();
-										isUnicode = true;
-									} else {
-										isUnicode = false;
-									}
-									//handle the \\u case manually into comment
-									if (this.currentCharacter == '\\') {
-										if (this.source[this.currentPosition] == '\\')
-											this.currentPosition++;
-									} //jump over the \\
-								}
-								int token = isJavadoc ? TokenNameCOMMENT_JAVADOC : TokenNameCOMMENT_BLOCK;
-								recordComment(token);
-								this.commentTagStarts[this.commentPtr] = firstTag;
-								if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition);
-								if (this.tokenizeComments) {
-									/*
-									if (isJavadoc)
-										return TokenNameCOMMENT_JAVADOC;
-									return TokenNameCOMMENT_BLOCK;
-									*/
-									return token;
-								}
-							} catch (IndexOutOfBoundsException e) {
-								this.currentPosition--;
-								throw new InvalidInputException(UNTERMINATED_COMMENT);
-							}
-							break;
-						}
-					}
-					if (getNextChar('='))
-						return TokenNameDIVIDE_EQUAL;
-					return TokenNameDIVIDE;
-				case '\u001a' :
-					if (atEnd())
-						return TokenNameEOF;
-					//the atEnd may not be <currentPosition == source.length> if source is only some part of a real (external) stream
-					throw new InvalidInputException("Ctrl-Z"); //$NON-NLS-1$
-				default :
-					char c = this.currentCharacter;
-					if (c < ScannerHelper.MAX_OBVIOUS) {
-						if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) {
-							return scanIdentifierOrKeyword();
-						} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_DIGIT) != 0) {
-								return scanNumber(false);
-						} else {
-							return TokenNameERROR;
-						}
-					}
-					boolean isJavaIdStart;
-					if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
-						if (this.complianceLevel < ClassFileConstants.JDK1_5) {
-							throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-						}
-						// Unicode 4 detection
-						char low = (char) getNextChar();
-						if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
-							// illegal low surrogate
-							throw new InvalidInputException(INVALID_LOW_SURROGATE);
-						}
-						isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low);
-					}
-					else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
-						if (this.complianceLevel < ClassFileConstants.JDK1_5) {
-							throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-						}
-						throw new InvalidInputException(INVALID_HIGH_SURROGATE);
-					} else {
-						// optimized case already checked
-						isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c);
-					}
-					if (isJavaIdStart)
-						return scanIdentifierOrKeyword();
-					if (ScannerHelper.isDigit(this.currentCharacter)) {
-						return scanNumber(false);
-					}
-					return TokenNameERROR;
-			}
-		}
-	} //-----------------end switch while try--------------------
-	catch (IndexOutOfBoundsException e) {
-		if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) {
-			// reposition scanner in case we are interested by spaces as tokens
-			this.currentPosition--;
-			this.startPosition = whiteStart;
-			return TokenNameWHITESPACE;
-		}
-	}
-	return TokenNameEOF;
-}
-public void getNextUnicodeChar()
-	throws InvalidInputException {
-	//VOID
-	//handle the case of unicode.
-	//when a unicode appears then we must use a buffer that holds char internal values
-	//At the end of this method currentCharacter holds the new visited char
-	//and currentPosition points right next after it
-
-	//ALL getNextChar.... ARE OPTIMIZED COPIES
-	int c1 = 0, c2 = 0, c3 = 0, c4 = 0, unicodeSize = 6;
-	this.currentPosition++;
-	if (this.currentPosition < this.eofPosition) {
-		while (this.source[this.currentPosition] == 'u') {
-			this.currentPosition++;
-			if (this.currentPosition >= this.eofPosition) {
-				this.currentPosition--;
-				throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-			}
-			unicodeSize++;
-		}
-	} else {
-		this.currentPosition--;
-		throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-	}
-
-	if ((this.currentPosition + 4) > this.eofPosition) {
-		this.currentPosition += (this.eofPosition - this.currentPosition);
-		throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-	}
-	if ((c1 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
-    		|| c1 < 0
-    		|| (c2 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
-    		|| c2 < 0
-    		|| (c3 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
-    		|| c3 < 0
-    		|| (c4 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15
-    		|| c4 < 0){
-		throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-	}
-	this.currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
-	//need the unicode buffer
-	if (this.withoutUnicodePtr == 0) {
-		//buffer all the entries that have been left aside....
-		unicodeInitializeBuffer(this.currentPosition - unicodeSize - this.startPosition);
-	}
-	//fill the buffer with the char
-	unicodeStore();
-	this.unicodeAsBackSlash = this.currentCharacter == '\\';
-}
-public NLSTag[] getNLSTags() {
-	final int length = this.nlsTagsPtr;
-	if (length != 0) {
-		NLSTag[] result = new NLSTag[length];
-		System.arraycopy(this.nlsTags, 0, result, 0, length);
-		this.nlsTagsPtr = 0;
-		return result;
-	}
-	return null;
-}
-@Override
-public char[] getSource(){
-	return this.source;
-}
-protected boolean isFirstTag() {
-	return true;
-}
-public final void jumpOverMethodBody() {
-
-	this.wasAcr = false;
-	int found = 1;
-	try {
-		while (true) { //loop for jumping over comments
-			this.withoutUnicodePtr = 0;
-			// ---------Consume white space and handles startPosition---------
-			boolean isWhiteSpace;
-			do {
-				this.startPosition = this.currentPosition;
-				if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-					&& (this.source[this.currentPosition] == 'u')) {
-					isWhiteSpace = jumpOverUnicodeWhiteSpace();
-				} else {
-					if (this.recordLineSeparator
-							&& ((this.currentCharacter == '\r') || (this.currentCharacter == '\n'))) {
-						pushLineSeparator();
-					}
-					isWhiteSpace = CharOperation.isWhitespace(this.currentCharacter);
-				}
-			} while (isWhiteSpace);
-
-			// -------consume token until } is found---------
-			NextToken: switch (this.currentCharacter) {
-				case '{' :
-					found++;
-					break NextToken;
-				case '}' :
-					found--;
-					if (found == 0)
-						return;
-					break NextToken;
-				case '\'' :
-					{
-						boolean test;
-						test = getNextChar('\\');
-						if (test) {
-							try {
-								if (this.unicodeAsBackSlash) {
-									// consume next character
-									this.unicodeAsBackSlash = false;
-									if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
-										getNextUnicodeChar();
-									} else {
-										if (this.withoutUnicodePtr != 0) {
-											unicodeStore();
-										}
-									}
-								} else {
-									this.currentCharacter = this.source[this.currentPosition++];
-								}
-								scanEscapeCharacter();
-							} catch (InvalidInputException ex) {
-								// ignore
-							}
-						} else {
-							try { // consume next character
-								this.unicodeAsBackSlash = false;
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-								} else {
-									if (this.withoutUnicodePtr != 0) {
-										unicodeStore();
-									}
-								}
-							} catch (InvalidInputException ex) {
-								// ignore
-							}
-						}
-						getNextChar('\'');
-						break NextToken;
-					}
-				case '"' :
-					boolean isTextBlock = false;
-					int firstClosingBrace = 0;
-					try {
-						try { // consume next character
-							isTextBlock = scanForTextBlockBeginning();
-							if (!isTextBlock) {
-								this.unicodeAsBackSlash = false;
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-								} else {
-									if (this.withoutUnicodePtr != 0) {
-										unicodeStore();
-									}
-								}
-							}
-						} catch (InvalidInputException ex) {
-								// ignore
-						}
-						Inner: while (this.currentPosition <= this.eofPosition) {
-							if (isTextBlock) {
-								switch (this.currentCharacter) {
-									case '"':
-										// look for text block delimiter
-										if (scanForTextBlockClose()) {
-											this.currentPosition += 2;
-											this.currentCharacter = this.source[this.currentPosition];
-											isTextBlock = false;
-											break Inner;
-										}
-										break;
-									case '}':
-										if (firstClosingBrace == 0)
-											firstClosingBrace = this.currentPosition;
-										break;
-									case '\r' :
-										if (this.source[this.currentPosition] == '\n')
-											this.currentPosition++;
-										//$FALL-THROUGH$
-									case '\n' :
-										pushLineSeparator();
-										//$FALL-THROUGH$
-									default:
-										if (this.currentCharacter == '\\' && this.source[this.currentPosition] == '"') {
-											this.currentPosition++;
-										}
-										this.currentCharacter = this.source[this.currentPosition++];
-										continue Inner;
-								}
-							} else if (this.currentCharacter == '"') {
-								break Inner;
-							}
-							if (this.currentCharacter == '\r'){
-								if (this.source[this.currentPosition] == '\n') this.currentPosition++;
-								break NextToken; // the string cannot go further that the line
-							}
-							if (this.currentCharacter == '\n'){
-								break; // the string cannot go further that the line
-							}
-							if (this.currentCharacter == '\\') {
-								try {
-									if (this.unicodeAsBackSlash) {
-										// consume next character
-										this.unicodeAsBackSlash = false;
-										if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) {
-											getNextUnicodeChar();
-										} else {
-											if (this.withoutUnicodePtr != 0) {
-												unicodeStore();
-											}
-										}
-									} else {
-										this.currentCharacter = this.source[this.currentPosition++];
-									}
-									scanEscapeCharacter();
-								} catch (InvalidInputException ex) {
-									// ignore
-								}
-							}
-							try { // consume next character
-								this.unicodeAsBackSlash = false;
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-								} else {
-									if (this.withoutUnicodePtr != 0) {
-										unicodeStore();
-									}
-								}
-							} catch (InvalidInputException ex) {
-								// ignore
-							}
-						}
-					} catch (IndexOutOfBoundsException e) {
-						if(isTextBlock) {
-							// Pull it back to the first closing brace after the beginning
-							// of the unclosed text block and let recovery take over.
-							if (firstClosingBrace > 0) {
-								this.currentPosition = firstClosingBrace - 1;
-							}
-						}
-					}
-					break NextToken;
-				case '/' :
-					{
-						int test;
-						if ((test = getNextChar('/', '*')) == 0) { //line comment
-							try {
-								this.lastCommentLinePosition = this.currentPosition;
-								//get the next char
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-								}
-								//handle the \\u case manually into comment
-								if (this.currentCharacter == '\\') {
-									if (this.source[this.currentPosition] == '\\')
-										this.currentPosition++;
-								} //jump over the \\
-								boolean isUnicode = false;
-								while (this.currentCharacter != '\r' && this.currentCharacter != '\n') {
-									if (this.currentPosition >= this.eofPosition) {
-										this.lastCommentLinePosition = this.currentPosition;
-										this.currentPosition ++;
-										// this avoids duplicating the code inside the catch(IndexOutOfBoundsException e) below
-										throw new IndexOutOfBoundsException();
-									}
-									this.lastCommentLinePosition = this.currentPosition;
-									//get the next char
-									isUnicode = false;
-									if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-											&& (this.source[this.currentPosition] == 'u')) {
-										isUnicode = true;
-										getNextUnicodeChar();
-									}
-									//handle the \\u case manually into comment
-									if (this.currentCharacter == '\\') {
-										if (this.source[this.currentPosition] == '\\')
-											this.currentPosition++;
-									} //jump over the \\
-								}
-								/*
-								 * We need to completely consume the line break
-								 */
-								if (this.currentCharacter == '\r'
-										&& this.eofPosition > this.currentPosition) {
-									if (this.source[this.currentPosition] == '\n') {
-										this.currentPosition++;
-										this.currentCharacter = '\n';
-									} else if ((this.source[this.currentPosition] == '\\')
-											&& (this.source[this.currentPosition + 1] == 'u')) {
-										isUnicode = true;
-										getNextUnicodeChar();
-									}
-								}
-								recordComment(TokenNameCOMMENT_LINE);
-								if (this.recordLineSeparator
-									&& ((this.currentCharacter == '\r') || (this.currentCharacter == '\n'))) {
-										if (this.checkNonExternalizedStringLiterals &&
-												this.lastPosition < this.currentPosition) {
-											parseTags();
-										}
-										if (this.recordLineSeparator) {
-											if (isUnicode) {
-												pushUnicodeLineSeparator();
-											} else {
-												pushLineSeparator();
-											}
-										}
-									}
-							} catch (IndexOutOfBoundsException e) {
-								 //an eof will then be generated
-								this.currentPosition--;
-								recordComment(TokenNameCOMMENT_LINE);
-								if (this.checkNonExternalizedStringLiterals &&
-										this.lastPosition < this.currentPosition) {
-									parseTags();
-								}
-								if (!this.tokenizeComments) {
-									this.currentPosition++;
-								}
-							}
-							break NextToken;
-						}
-						if (test > 0) { //traditional and javadoc comment
-							boolean isJavadoc = false;
-							try { //get the next char
-								boolean star = false;
-								int previous;
-								boolean isUnicode = false;
-								// consume next character
-								this.unicodeAsBackSlash = false;
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-									isUnicode = true;
-								} else {
-									isUnicode = false;
-									if (this.withoutUnicodePtr != 0) {
-										unicodeStore();
-									}
-								}
-
-								if (this.currentCharacter == '*') {
-									isJavadoc = true;
-									star = true;
-								}
-								if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
-									if (this.recordLineSeparator) {
-										if (isUnicode) {
-											pushUnicodeLineSeparator();
-										} else {
-											pushLineSeparator();
-										}
-									}
-								}
-								isUnicode = false;
-								previous = this.currentPosition;
-								if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-										&& (this.source[this.currentPosition] == 'u')) {
-									getNextUnicodeChar();
-									isUnicode = true;
-								} else {
-									isUnicode = false;
-								}
-								//handle the \\u case manually into comment
-								if (this.currentCharacter == '\\') {
-									if (this.source[this.currentPosition] == '\\')
-										this.currentPosition++; //jump over the \\
-								}
-								// empty comment is not a javadoc /**/
-								if (this.currentCharacter == '/') {
-									isJavadoc = false;
-								}
-								//loop until end of comment */
-								int firstTag = 0;
-								while ((this.currentCharacter != '/') || (!star)) {
-									if (this.currentPosition >= this.eofPosition) {
-										return;
-									}
-									if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) {
-										if (this.recordLineSeparator) {
-											if (isUnicode) {
-												pushUnicodeLineSeparator();
-											} else {
-												pushLineSeparator();
-											}
-										}
-									}
-									switch (this.currentCharacter) {
-										case '*':
-											star = true;
-											break;
-										case '@':
-											if (firstTag == 0 && this.isFirstTag()) {
-												firstTag = previous;
-											}
-											//$FALL-THROUGH$ default case to set star to false
-										default:
-											star = false;
-									}
-									//get next char
-									previous = this.currentPosition;
-									if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-											&& (this.source[this.currentPosition] == 'u')) {
-										getNextUnicodeChar();
-										isUnicode = true;
-									} else {
-										isUnicode = false;
-									}
-									//handle the \\u case manually into comment
-									if (this.currentCharacter == '\\') {
-										if (this.source[this.currentPosition] == '\\')
-											this.currentPosition++;
-									} //jump over the \\
-								}
-								recordComment(isJavadoc ? TokenNameCOMMENT_JAVADOC : TokenNameCOMMENT_BLOCK);
-								this.commentTagStarts[this.commentPtr] = firstTag;
-							} catch (IndexOutOfBoundsException e) {
-								return;
-							}
-							break NextToken;
-						}
-						break NextToken;
-					}
-
-				default :
-					try {
-						char c = this.currentCharacter;
-						if (c < ScannerHelper.MAX_OBVIOUS) {
-							if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) {
-								scanIdentifierOrKeyword();
-								break NextToken;
-							} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_DIGIT) != 0) {
-								scanNumber(false);
-								break NextToken;
-							} else {
-								break NextToken;
-							}
-						}
-						boolean isJavaIdStart;
-						if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) {
-							if (this.complianceLevel < ClassFileConstants.JDK1_5) {
-								throw new InvalidInputException(INVALID_UNICODE_ESCAPE);
-							}
-							// Unicode 4 detection
-							char low = (char) getNextChar();
-							if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) {
-								// illegal low surrogate
-								break NextToken;
-							}
-							isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low);
-						} else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) {
-							break NextToken;
-						} else {
-							// optimized case already checked
-							isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c);
-						}
-						if (isJavaIdStart) {
-							scanIdentifierOrKeyword();
-							break NextToken;
-						}
-//						if (ScannerHelper.isDigit(this.currentCharacter)) {
-//							scanNumber(false);
-//							break NextToken;
-//						}
-					} catch (InvalidInputException ex) {
-						// ignore
-					}
-			}
-		}
-		//-----------------end switch while try--------------------
-	} catch (IndexOutOfBoundsException | InvalidInputException e) {
-		// ignore
-	}
-	return;
-}
-public final boolean jumpOverUnicodeWhiteSpace() throws InvalidInputException {
-	//BOOLEAN
-	//handle the case of unicode. Jump over the next whiteSpace
-	//making startPosition pointing on the next available char
-	//On false, the currentCharacter is filled up with a potential
-	//correct char
-
-	this.wasAcr = false;
-	getNextUnicodeChar();
-	return CharOperation.isWhitespace(this.currentCharacter);
-}
-
-private void parseTags() {
-	int position = 0;
-	final int currentStartPosition = this.startPosition;
-	final int currentLinePtr = this.linePtr;
-	if (currentLinePtr >= 0) {
-		position = this.lineEnds[currentLinePtr] + 1;
-	}
-	while (ScannerHelper.isWhitespace(this.source[position])) {
-		position++;
-	}
-	if (currentStartPosition == position) {
-		// the whole line is commented out
-		return;
-	}
-	char[] s = null;
-	int sourceEnd = this.currentPosition;
-	int sourceStart = currentStartPosition;
-	int sourceDelta = 0;
-	if (this.withoutUnicodePtr != 0) {
-		// 0 is used as a fast test flag so the real first char is in position 1
-		System.arraycopy(
-			this.withoutUnicodeBuffer,
-			1,
-			s = new char[this.withoutUnicodePtr],
-			0,
-			this.withoutUnicodePtr);
-		sourceEnd = this.withoutUnicodePtr;
-		sourceStart = 1;
-		sourceDelta = currentStartPosition;
-	} else {
-		s = this.source;
-	}
-	int pos = CharOperation.indexOf(TAG_PREFIX, s, true, sourceStart, sourceEnd);
-	if (pos != -1) {
-		if (this.nlsTags == null) {
-			this.nlsTags = new NLSTag[10];
-			this.nlsTagsPtr = 0;
-		}
-		while (pos != -1) {
-			int start = pos + TAG_PREFIX_LENGTH;
-			int end = CharOperation.indexOf(TAG_POSTFIX, s, start, sourceEnd);
-			if (end != -1) {
-				NLSTag currentTag = null;
-				final int currentLine = currentLinePtr + 1;
-				try {
-					currentTag = new NLSTag(pos + sourceDelta, end + sourceDelta, currentLine, extractInt(s, start, end));
-				} catch (NumberFormatException e) {
-					currentTag = new NLSTag(pos + sourceDelta, end + sourceDelta, currentLine, -1);
-				}
-				if (this.nlsTagsPtr == this.nlsTags.length) {
-					// resize
-					System.arraycopy(this.nlsTags, 0, (this.nlsTags = new NLSTag[this.nlsTagsPtr + 10]), 0, this.nlsTagsPtr);
-				}
-				this.nlsTags[this.nlsTagsPtr++] = currentTag;
-			} else {
-				end = start;
-			}
-			pos = CharOperation.indexOf(TAG_PREFIX, s, true, end, sourceEnd);
-		}
-	}
-}
-private int extractInt(char[] array, int start, int end) {
-	int value = 0;
-	for (int i = start; i < end; i++) {
-		final char currentChar = array[i];
-		int digit = 0;
-		switch(currentChar) {
-			case '0' :
-				digit = 0;
-				break;
-			case '1' :
-				digit = 1;
-				break;
-			case '2' :
-				digit = 2;
-				break;
-			case '3' :
-				digit = 3;
-				break;
-			case '4' :
-				digit = 4;
-				break;
-			case '5' :
-				digit = 5;
-				break;
-			case '6' :
-				digit = 6;
-				break;
-			case '7' :
-				digit = 7;
-				break;
-			case '8' :
-				digit = 8;
-				break;
-			case '9' :
-				digit = 9;
-				break;
-			default :
-				throw new NumberFormatException();
-		}
-		value *= 10;
-		if (digit < 0) throw new NumberFormatException();
-		value += digit;
-	}
-	return value;
-}
-public final void pushLineSeparator() {
-	//see comment on isLineDelimiter(char) for the use of '\n' and '\r'
-	final int INCREMENT = 250;
-	//currentCharacter is at position currentPosition-1
-	// cr 000D
-	if (this.currentCharacter == '\r') {
-		int separatorPos = this.currentPosition - 1;
-		if ((this.linePtr >= 0) && (this.lineEnds[this.linePtr] >= separatorPos)) return;
-		int length = this.lineEnds.length;
-		if (++this.linePtr >=  length)
-			System.arraycopy(this.lineEnds, 0, this.lineEnds = new int[length + INCREMENT], 0, length);
-		this.lineEnds[this.linePtr] = separatorPos;
-		// look-ahead for merged cr+lf
-		try {
-			if (this.source[this.currentPosition] == '\n') {
-				//System.out.println("look-ahead LF-" + this.currentPosition);
-				this.lineEnds[this.linePtr] = this.currentPosition;
-				this.currentPosition++;
-				this.wasAcr = false;
-			} else {
-				this.wasAcr = true;
-			}
-		} catch(IndexOutOfBoundsException e) {
-			this.wasAcr = true;
-		}
-	} else {
-		// lf 000A
-		if (this.currentCharacter == '\n') { //must merge eventual cr followed by lf
-			if (this.wasAcr && (this.lineEnds[this.linePtr] == (this.currentPosition - 2))) {
-				//System.out.println("merge LF-" + (this.currentPosition - 1));
-				this.lineEnds[this.linePtr] = this.currentPosition - 1;
-			} else {
-				int separatorPos = this.currentPosition - 1;
-				if ((this.linePtr >= 0) && (this.lineEnds[this.linePtr] >= separatorPos)) return;
-				int length = this.lineEnds.length;
-				if (++this.linePtr >=  length)
-					System.arraycopy(this.lineEnds, 0, this.lineEnds = new int[length + INCREMENT], 0, length);
-				this.lineEnds[this.linePtr] = separatorPos;
-			}
-			this.wasAcr = false;
-		}
-	}
-}
-public final void pushUnicodeLineSeparator() {
-	// cr 000D
-	if (this.currentCharacter == '\r') {
-		if (this.source[this.currentPosition] == '\n') {
-			this.wasAcr = false;
-		} else {
-			this.wasAcr = true;
-		}
-	} else {
-		// lf 000A
-		if (this.currentCharacter == '\n') { //must merge eventual cr followed by lf
-			this.wasAcr = false;
-		}
-	}
-}
-
-public void recordComment(int token) {
-	// compute position
-	int commentStart = this.startPosition;
-	int stopPosition = this.currentPosition;
-	switch (token) {
-		case TokenNameCOMMENT_LINE:
-			// both positions are negative
-			commentStart = -this.startPosition;
-			stopPosition = -this.lastCommentLinePosition;
-			break;
-		case TokenNameCOMMENT_BLOCK:
-			// only end position is negative
-			stopPosition = -this.currentPosition;
-			break;
-	}
-
-	// a new comment is recorded
-	int length = this.commentStops.length;
-	if (++this.commentPtr >=  length) {
-		int newLength = length + COMMENT_ARRAYS_SIZE*10;
-		System.arraycopy(this.commentStops, 0, this.commentStops = new int[newLength], 0, length);
-		System.arraycopy(this.commentStarts, 0, this.commentStarts = new int[newLength], 0, length);
-		System.arraycopy(this.commentTagStarts, 0, this.commentTagStarts = new int[newLength], 0, length);
-	}
-	this.commentStops[this.commentPtr] = stopPosition;
-	this.commentStarts[this.commentPtr] = commentStart;
-}
-
-/**
- * Reposition the scanner on some portion of the original source. The given endPosition is the last valid position.
- * Beyond this position, the scanner will answer EOF tokens (<code>ITerminalSymbols.TokenNameEOF</code>).
- *
- * @param begin the given start position
- * @param end the given end position
- */
-@Override
-public void resetTo(int begin, int end) {
-	//reset the scanner to a given position where it may rescan again
-
-	this.diet = false;
-	this.initialPosition = this.startPosition = this.currentPosition = begin;
-	if (this.source != null && this.source.length < end) {
-		this.eofPosition = this.source.length;
-	} else {
-		this.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
-	}
-	this.commentPtr = -1; // reset comment stack
-	this.foundTaskCount = 0;
-}
-
-protected final void scanEscapeCharacter() throws InvalidInputException {
-	// the string with "\\u" is a legal string of two chars \ and u
-	//thus we use a direct access to the source (for regular cases).
-	switch (this.currentCharacter) {
-		case 'b' :
-			this.currentCharacter = '\b';
-			break;
-		case 't' :
-			this.currentCharacter = '\t';
-			break;
-		case 'n' :
-			this.currentCharacter = '\n';
-			break;
-		case 'f' :
-			this.currentCharacter = '\f';
-			break;
-		case 'r' :
-			this.currentCharacter = '\r';
-			break;
-		case '\"' :
-			this.currentCharacter = '\"';
-			break;
-		case '\'' :
-			this.currentCharacter = '\'';
-			break;
-		case 's' :
-			if (this.sourceLevel < ClassFileConstants.JDK15) {
-				throw new InvalidInputException(INVALID_ESCAPE);
-			}
-			this.currentCharacter = ' ';
-			break;
-		case '\\' :
-			this.currentCharacter = '\\';
-			break;
-		default :
-			// -----------octal escape--------------
-			// OctalDigit
-			// OctalDigit OctalDigit
-			// ZeroToThree OctalDigit OctalDigit
-
-			int number = ScannerHelper.getHexadecimalValue(this.currentCharacter);
-			if (number >= 0 && number <= 7) {
-				boolean zeroToThreeNot = number > 3;
-				if (ScannerHelper.isDigit(this.currentCharacter = this.source[this.currentPosition++])) {
-					int digit = ScannerHelper.getHexadecimalValue(this.currentCharacter);
-					if (digit >= 0 && digit <= 7) {
-						number = (number * 8) + digit;
-						if (ScannerHelper.isDigit(this.currentCharacter = this.source[this.currentPosition++])) {
-							if (zeroToThreeNot) {// has read \NotZeroToThree OctalDigit Digit --> ignore last character
-								this.currentPosition--;
-							} else {
-								digit = ScannerHelper.getHexadecimalValue(this.currentCharacter);
-								if (digit >= 0 && digit <= 7){ // has read \ZeroToThree OctalDigit OctalDigit
-									number = (number * 8) + digit;
-								} else {// has read \ZeroToThree OctalDigit NonOctalDigit --> ignore last character
-									this.currentPosition--;
-								}
-							}
-						} else { // has read \OctalDigit NonDigit--> ignore last character
-							this.currentPosition--;
-						}
-					} else { // has read \OctalDigit NonOctalDigit--> ignore last character
-						this.currentPosition--;
-					}
-				} else { // has read \OctalDigit --> ignore last character
-					this.currentPosition--;
-				}
-				if (number > 255)
-					throw new InvalidInputException(INVALID_ESCAPE);
-				this.currentCharacter = (char) number;
-			} else
-				throw new InvalidInputException(INVALID_ESCAPE);
-	}
-}
-
-//{ObjectTeams: added to retrieve OT-specific special identifiers (after, before, replace)
-/*
- * If the Identifier is an SpecialIdentifier it will return the appropriate symbol from TerminalTokens.
- * @param data[] Identifier String
- * @return The int-type of an Special Identifier or TokenNameERROR
- */
-public int getCallinModifierToken(char[] data) {
-// replace after before
-
-  	int length=data.length;
-	int index=0;
-	char firstLetter = data[index];
-	switch (firstLetter) {
-		case 'a' :
-			switch(length) {
-				case 5: //after
-					if ((data[++index] == 'f')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'r'))
-							return TokenNameafter;
-			}
-			break;
-		case 'b' :
-			switch (length) {
-				case 6:// before
-					if ((data[++index] == 'e')
-						&& (data[++index] == 'f')
-						&& (data[++index] == 'o')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 'e'))
-							return TokenNamebefore;
-			}
-			break;
-		case 'r' :
-			switch (length) {
-				case 7: //replace
-					if ((data[++index] == 'e')
-						&& (data[++index] == 'p')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 'e'))
-							return TokenNamereplace;
-			}
-	}
-	return TokenNameERROR;
-}
-// Markus Witte}
-
-public int scanIdentifierOrKeywordWithBoundCheck() {
-	//test keywords
-
-	//first dispatch on the first char.
-	//then the length. If there are several
-	//keywors with the same length AND the same first char, then do another
-	//dispatch on the second char
-	this.useAssertAsAnIndentifier = false;
-	this.useEnumAsAnIndentifier = false;
-
-	char[] src = this.source;
-	identLoop: {
-		int pos;
-		int srcLength = this.eofPosition;
-		while (true) {
-			if ((pos = this.currentPosition) >= srcLength) // handle the obvious case upfront
-				break identLoop;
-			char c = src[pos];
-			if (c < ScannerHelper.MAX_OBVIOUS) {
-				if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] &
-						(ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_IDENT_PART | ScannerHelper.C_DIGIT)) != 0) {
-					if (this.withoutUnicodePtr != 0) {
-							this.currentCharacter = c;
-							unicodeStore();
-						}
-						this.currentPosition++;
-				} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_SEPARATOR | ScannerHelper.C_JLS_SPACE)) != 0) {
-						this.currentCharacter = c;
-						break identLoop;
-				} else {
-					//System.out.println("slow<=128:  "+ c);
-					while (getNextCharAsJavaIdentifierPartWithBoundCheck()){/*empty*/}
-					break identLoop;
-				}
-			} else {
-				//System.out.println("slow>>128:  "+ c);
-				while (getNextCharAsJavaIdentifierPartWithBoundCheck()){/*empty*/}
-				break identLoop;
-			}
-		}
-	}
-
-	int index, length;
-	char[] data;
-	if (this.withoutUnicodePtr == 0) {
-		//quick test on length == 1 but not on length > 12 while most identifier
-		//have a length which is <= 12...but there are lots of identifier with
-		//only one char....
-		if ((length = this.currentPosition - this.startPosition) == 1) {
-			return InternalTokenNameIdentifier;
-		}
-		data = this.source;
-		index = this.startPosition;
-	} else {
-		if ((length = this.withoutUnicodePtr) == 1)
-			return InternalTokenNameIdentifier;
-		data = this.withoutUnicodeBuffer;
-		index = 1;
-	}
-
-	return internalScanIdentifierOrKeyword(index, length, data);
-}
-public int scanIdentifierOrKeyword() {
-	//test keywords
-
-	//first dispatch on the first char.
-	//then the length. If there are several
-	//keywords with the same length AND the same first char, then do another
-	//dispatch on the second char
-	this.useAssertAsAnIndentifier = false;
-	this.useEnumAsAnIndentifier = false;
-
-	char[] src = this.source;
-	identLoop: {
-		int pos;
-		int srcLength = this.eofPosition;
-		while (true) {
-			if ((pos = this.currentPosition) >= srcLength) // handle the obvious case upfront
-				break identLoop;
-			char c = src[pos];
-			if (c < ScannerHelper.MAX_OBVIOUS) {
-				if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] &
-						(ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_IDENT_PART | ScannerHelper.C_DIGIT)) != 0) {
-					if (this.withoutUnicodePtr != 0) {
-							this.currentCharacter = c;
-							unicodeStore();
-						}
-						this.currentPosition++;
-				} else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_SEPARATOR | ScannerHelper.C_JLS_SPACE)) != 0) {
-						this.currentCharacter = c;
-						break identLoop;
-				} else {
-					//System.out.println("slow<=128:  "+ c);
-					while (getNextCharAsJavaIdentifierPart()){/*empty*/}
-					break identLoop;
-				}
-			} else {
-				//System.out.println("slow>>128:  "+ c);
-				while (getNextCharAsJavaIdentifierPart()){/*empty*/}
-				break identLoop;
-			}
-		}
-	}
-
-	int index, length;
-	char[] data;
-	if (this.withoutUnicodePtr == 0) {
-		//quick test on length == 1 but not on length > 12 while most identifier
-		//have a length which is <= 12...but there are lots of identifier with
-		//only one char....
-		if ((length = this.currentPosition - this.startPosition) == 1) {
-			return InternalTokenNameIdentifier;
-		}
-		data = this.source;
-		index = this.startPosition;
-	} else {
-		if ((length = this.withoutUnicodePtr) == 1)
-			return InternalTokenNameIdentifier;
-		data = this.withoutUnicodeBuffer;
-		index = 1;
-	}
-
-	return internalScanIdentifierOrKeyword(index, length, data);
-}
-private int internalScanIdentifierOrKeyword(int index, int length, char[] data) {
-//{ObjectTeams: callin/calloutSeen/atStartOfImport/precedenceSeen has effect only once:
-	boolean calloutSeen = this._calloutSeen;
-	this._calloutSeen = false;
-	boolean callinSeen = this._callinSeen;
-	this._callinSeen = false;
-	boolean precedenceSeen = this._precedenceSeen;
-	this._precedenceSeen = false;
-	boolean atStartOfImport = this._atStartOfImport;
-	this._atStartOfImport = false;
-// SH}
-	switch (data[index]) {
-		case 'a' :
-			switch(length) {
-//{ObjectTeams: keyword 'as':
-				case 2: //as
-					if (   this._isOTSource
-						&& (data[++index] == 's')) {
-						return TokenNameas;
-					} else {
-						return InternalTokenNameIdentifier;
-					}
-// MW}
-				case 8: //abstract
-					if ((data[++index] == 'b')
-						&& (data[++index] == 's')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 't')) {
-							return TokenNameabstract;
-						} else {
-							return InternalTokenNameIdentifier;
-						}
-//{ObjectTeams: 'after' (only if after "<-" or "precedence"):
-				case 5:
-					if (callinSeen || precedenceSeen) {
-						if (   (data[++index] == 'f')
-							&& (data[++index] == 't')
-							&& (data[++index] == 'e')
-							&& (data[++index] == 'r'))
-							return TokenNameafter;
-					}
-					return InternalTokenNameIdentifier;
-// SH}
-				case 6: // assert
-					if ((data[++index] == 's')
-						&& (data[++index] == 's')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 't')) {
-							if (this.sourceLevel >= ClassFileConstants.JDK1_4) {
-								this.containsAssertKeyword = true;
-								return TokenNameassert;
-							} else {
-								this.useAssertAsAnIndentifier = true;
-								return InternalTokenNameIdentifier;
-							}
-						} else {
-							return InternalTokenNameIdentifier;
-						}
-				default:
-					return InternalTokenNameIdentifier;
-			}
-		case 'b' : //boolean break byte (before)
-			switch (length) {
-				case 4 :
-					if ((data[++index] == 'y') && (data[++index] == 't') && (data[++index] == 'e'))
-						return TokenNamebyte;
-					else
-//{ObjectTeams: check for base keyword
-						/*@original
-						return InternalTokenNameIdentifier;
-						 */
-						{
-						if (   baseIsKeyword(atStartOfImport)
-							&& (data[index] == 'a')
-							&& (data[++index] == 's')
-							&& (data[++index] == 'e'))
-									return TokenNamebase;
-							else
-								return InternalTokenNameIdentifier;
-						}
-//Markus Witte}
-				case 5 :
-					if ((data[++index] == 'r')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'k'))
-						return TokenNamebreak;
-					else
-						return InternalTokenNameIdentifier;
-
-//{ObjectTeams: 'before' (only if after "<-"):
-				case 6:
-					if (callinSeen) {
-						if (   (data[++index] == 'e')
-							&& (data[++index] == 'f')
-							&& (data[++index] == 'o')
-							&& (data[++index] == 'r')
-							&& (data[++index] == 'e'))
-							return TokenNamebefore;
-					}
-					return InternalTokenNameIdentifier;
-// SH}
-				case 7 :
-					if ((data[++index] == 'o')
-						&& (data[++index] == 'o')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'n'))
-						return TokenNameboolean;
-					else
-						return InternalTokenNameIdentifier;
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		case 'c' : //case char catch const class continue
-			switch (length) {
-				case 4 :
-					if (data[++index] == 'a')
-						if ((data[++index] == 's') && (data[++index] == 'e'))
-							return TokenNamecase;
-						else
-							return InternalTokenNameIdentifier;
-					else
-						if ((data[index] == 'h') && (data[++index] == 'a') && (data[++index] == 'r'))
-							return TokenNamechar;
-						else
-							return InternalTokenNameIdentifier;
-				case 5 :
-					if (data[++index] == 'a')
-						if ((data[++index] == 't') && (data[++index] == 'c') && (data[++index] == 'h'))
-							return TokenNamecatch;
-						else
-							return InternalTokenNameIdentifier;
-					else
-						if (data[index] == 'l')
-							if ((data[++index] == 'a')
-								&& (data[++index] == 's')
-								&& (data[++index] == 's'))
-//{ObjectTeams: advance scanner state:
-							{
-								if (this._teamKeywordSeen)
-									enterOTSource(); // either in team class or in ROFI class
-// orig:
-								return TokenNameclass;
-// :giro
-							}
-// SH}
-							else
-								return InternalTokenNameIdentifier;
-						else if ((data[index] == 'o')
-							&& (data[++index] == 'n')
-							&& (data[++index] == 's')
-							&& (data[++index] == 't'))
-							return TokenNameconst; //const is not used in java ???????
-						else
-							return InternalTokenNameIdentifier;
-//{ObjectTeams: check for callin keyword
-				case 6: //callin
-					if (   this._isOTSource
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'n'))
-						return TokenNamecallin;
-					else
-						return InternalTokenNameIdentifier;
-
-//Markus Witte}
-				case 8 :
-					if ((data[++index] == 'o')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 'u')
-						&& (data[++index] == 'e'))
-						return TokenNamecontinue;
-					else
-						return InternalTokenNameIdentifier;
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		case 'd' : //default do double
-			switch (length) {
-				case 2 :
-					if ((data[++index] == 'o'))
-						return TokenNamedo;
-					else
-						return InternalTokenNameIdentifier;
-				case 6 :
-					if ((data[++index] == 'o')
-						&& (data[++index] == 'u')
-						&& (data[++index] == 'b')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'e'))
-						return TokenNamedouble;
-					else
-						return InternalTokenNameIdentifier;
-				case 7 :
-					if ((data[++index] == 'e')
-						&& (data[++index] == 'f')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'u')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 't'))
-						return TokenNamedefault;
-					else
-						return InternalTokenNameIdentifier;
-				default :
-					return InternalTokenNameIdentifier;
-			}
-		case 'e' : //else extends
-			switch (length) {
-				case 4 :
-					if (data[++index] == 'l') {
-						if ((data[++index] == 's') && (data[++index] == 'e')) {
-							return TokenNameelse;
-						} else {
-							return InternalTokenNameIdentifier;
-						}
-					} else if ((data[index] == 'n')
-							&& (data[++index] == 'u')
-							&& (data[++index] == 'm')) {
-						if (this.sourceLevel >= ClassFileConstants.JDK1_5) {
-							return TokenNameenum;
-						} else {
-							this.useEnumAsAnIndentifier = true;
-							return InternalTokenNameIdentifier;
-						}
-					}
-					return InternalTokenNameIdentifier;
-				case 7 :
-					if ((data[++index] == 'x')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 'd')
-						&& (data[++index] == 's'))
-						return TokenNameextends;
-					else
-						return InternalTokenNameIdentifier;
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		case 'f' : //final finally float for false
-			switch (length) {
-				case 3 :
-					if ((data[++index] == 'o') && (data[++index] == 'r'))
-						return TokenNamefor;
-					else
-						return InternalTokenNameIdentifier;
-				case 5 :
-					if (data[++index] == 'i')
-						if ((data[++index] == 'n')
-							&& (data[++index] == 'a')
-							&& (data[++index] == 'l')) {
-							return TokenNamefinal;
-						} else
-							return InternalTokenNameIdentifier;
-					else
-						if (data[index] == 'l')
-							if ((data[++index] == 'o')
-								&& (data[++index] == 'a')
-								&& (data[++index] == 't'))
-								return TokenNamefloat;
-							else
-								return InternalTokenNameIdentifier;
-						else
-							if ((data[index] == 'a')
-								&& (data[++index] == 'l')
-								&& (data[++index] == 's')
-								&& (data[++index] == 'e'))
-								return TokenNamefalse;
-							else
-								return InternalTokenNameIdentifier;
-				case 7 :
-					if ((data[++index] == 'i')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'y'))
-						return TokenNamefinally;
-					else
-						return InternalTokenNameIdentifier;
-
-				default :
-					return InternalTokenNameIdentifier;
-			}
-		case 'g' :
-//{ObjectTeams: 'get':
-			if (length == 3 && calloutSeen) {
-				if (   (data[++index] == 'e')
-					&& (data[++index] == 't'))
-					return TokenNameget;
-			}
-// SH}
-			//goto
-			if (length == 4) {
-				if ((data[++index] == 'o')
-					&& (data[++index] == 't')
-					&& (data[++index] == 'o')) {
-					return TokenNamegoto;
-				}
-			} //no goto in java are allowed, so why java removes this keyword ???
-			return InternalTokenNameIdentifier;
-
-		case 'i' : //if implements import instanceof int interface
-			switch (length) {
-				case 2 :
-					if (data[++index] == 'f')
-						return TokenNameif;
-					else
-						return InternalTokenNameIdentifier;
-				case 3 :
-					if ((data[++index] == 'n') && (data[++index] == 't'))
-						return TokenNameint;
-					else
-						return InternalTokenNameIdentifier;
-				case 6 :
-					if ((data[++index] == 'm')
-						&& (data[++index] == 'p')
-						&& (data[++index] == 'o')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 't'))
-//{ObjectTeams: support 'base' as the very next word:
-					{
-						this._atStartOfImport = true;
-// orig:
-						return TokenNameimport;
-// :giro
-					}
-// SH}
-					else
-						return InternalTokenNameIdentifier;
-				case 9 :
-					if ((data[++index] == 'n')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 'f')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 'e'))
-//{ObjectTeams: advance scanner state:
-					{
-						if (this._teamKeywordSeen)
-							enterOTSource(); // in ROFI interface
-// orig:
-						return TokenNameinterface;
-// :giro
-					}
-// SH}
-					else
-						return InternalTokenNameIdentifier;
-				case 10 :
-					if (data[++index] == 'm')
-						if ((data[++index] == 'p')
-							&& (data[++index] == 'l')
-							&& (data[++index] == 'e')
-							&& (data[++index] == 'm')
-							&& (data[++index] == 'e')
-							&& (data[++index] == 'n')
-							&& (data[++index] == 't')
-							&& (data[++index] == 's'))
-							return TokenNameimplements;
-						else
-							return InternalTokenNameIdentifier;
-					else
-						if ((data[index] == 'n')
-							&& (data[++index] == 's')
-							&& (data[++index] == 't')
-							&& (data[++index] == 'a')
-							&& (data[++index] == 'n')
-							&& (data[++index] == 'c')
-							&& (data[++index] == 'e')
-							&& (data[++index] == 'o')
-							&& (data[++index] == 'f'))
-							return TokenNameinstanceof;
-						else
-							return InternalTokenNameIdentifier;
-
-				default :
-					return InternalTokenNameIdentifier;
-			}
-		case 'l' : //long
-			if (length == 4) {
-				if ((data[++index] == 'o')
-					&& (data[++index] == 'n')
-					&& (data[++index] == 'g')) {
-					return TokenNamelong;
-				}
-			}
-			return InternalTokenNameIdentifier;
-
-		case 'n' : //native new null
-			switch (length) {
-				case 3 :
-					if ((data[++index] == 'e') && (data[++index] == 'w'))
-						return TokenNamenew;
-					else
-						return InternalTokenNameIdentifier;
-				case 4 :
-					if ((data[++index] == 'u') && (data[++index] == 'l') && (data[++index] == 'l'))
-						return TokenNamenull;
-					else
-						return InternalTokenNameIdentifier;
-				case 6 :
-					if ((data[++index] == 'a')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'v')
-						&& (data[++index] == 'e')) {
-						return TokenNamenative;
-					} else
-						return InternalTokenNameIdentifier;
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		case 'p' : //package private protected public
-			switch (length) {
-				case 6 :
-					if ((data[++index] == 'u')
-						&& (data[++index] == 'b')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'c')) {
-						return TokenNamepublic;
-					} else
-						return InternalTokenNameIdentifier;
-				case 7 :
-					if (data[++index] == 'a')
-						if ((data[++index] == 'c')
-							&& (data[++index] == 'k')
-							&& (data[++index] == 'a')
-							&& (data[++index] == 'g')
-							&& (data[++index] == 'e'))
-							return TokenNamepackage;
-						else
-							return InternalTokenNameIdentifier;
-					else
-						if ((data[index] == 'r')
-							&& (data[++index] == 'i')
-							&& (data[++index] == 'v')
-							&& (data[++index] == 'a')
-							&& (data[++index] == 't')
-							&& (data[++index] == 'e')) {
-							return TokenNameprivate;
-						} else
-							return InternalTokenNameIdentifier;
-//{ObjectTeams: check for playedBy/precedence keywords
-				case 8 :
-					if (   this._isOTSource
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'y')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'd')
-						&& (data[++index] == 'B')
-						&& (data[++index] == 'y')) {
-						return TokenNameplayedBy;
-					} else
-						return InternalTokenNameIdentifier;
-				case 10 :
-					if (   this._isOTSource
-						&& (data[++index] == 'r')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'd')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 'e')) {
-						this._precedenceSeen = true;
-						return TokenNameprecedence;
-					} else
-						return InternalTokenNameIdentifier;
-//Markus Witte/SH}
-				case 9 :
-					if ((data[++index] == 'r')
-						&& (data[++index] == 'o')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'd')) {
-						return TokenNameprotected;
-					} else
-						return InternalTokenNameIdentifier;
-
-				default :
-					return InternalTokenNameIdentifier;
-			}
-		case 'r' : //return
-			if (length == 6) {
-				if ((data[++index] == 'e')
-					&& (data[++index] == 't')
-					&& (data[++index] == 'u')
-					&& (data[++index] == 'r')
-					&& (data[++index] == 'n')) {
-					return TokenNamereturn;
-				}
-			}
-//{ObjectTeams: 'replace' (only if after "<-"):
-			else if (length == 7 && callinSeen) {
-				if (   (data[++index] == 'e')
-					&& (data[++index] == 'p')
-					&& (data[++index] == 'l')
-					&& (data[++index] == 'a')
-					&& (data[++index] == 'c')
-					&& (data[++index] == 'e'))
-					return TokenNamereplace;
-			}
-// SH}
-			return InternalTokenNameIdentifier;
-
-		case 's' : //short static super switch synchronized strictfp
-			switch (length) {
-//{ObjectTeams: 'set' (after '->')
-				case 3:
-					if (calloutSeen) {
-						if (   (data[++index] == 'e')
-					        && (data[++index] == 't'))
-								return TokenNameset;
-					}
-					return InternalTokenNameIdentifier;
-// SH}
-				case 5 :
-					if (data[++index] == 'h')
-						if ((data[++index] == 'o') && (data[++index] == 'r') && (data[++index] == 't'))
-							return TokenNameshort;
-						else
-							return InternalTokenNameIdentifier;
-					else
-						if ((data[index] == 'u')
-							&& (data[++index] == 'p')
-							&& (data[++index] == 'e')
-							&& (data[++index] == 'r'))
-							return TokenNamesuper;
-						else
-							return InternalTokenNameIdentifier;
-
-				case 6 :
-					if (data[++index] == 't')
-						if ((data[++index] == 'a')
-							&& (data[++index] == 't')
-							&& (data[++index] == 'i')
-							&& (data[++index] == 'c')) {
-							return TokenNamestatic;
-						} else
-							return InternalTokenNameIdentifier;
-					else
-						if ((data[index] == 'w')
-							&& (data[++index] == 'i')
-							&& (data[++index] == 't')
-							&& (data[++index] == 'c')
-							&& (data[++index] == 'h'))
-							return TokenNameswitch;
-						else
-							return InternalTokenNameIdentifier;
-				case 8 :
-					if ((data[++index] == 't')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'f')
-						&& (data[++index] == 'p'))
-						return TokenNamestrictfp;
-					else
-						return InternalTokenNameIdentifier;
-				case 12 :
-					if ((data[++index] == 'y')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 'c')
-						&& (data[++index] == 'h')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 'o')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'z')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'd')) {
-						return TokenNamesynchronized;
-					} else
-						return InternalTokenNameIdentifier;
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		case 't' : //try throw throws transient this true
-			switch (length) {
-				case 3 :
-					if ((data[++index] == 'r') && (data[++index] == 'y'))
-						return TokenNametry;
-					else
-						return InternalTokenNameIdentifier;
-				case 4 :
-					if (data[++index] == 'h')
-						if ((data[++index] == 'i') && (data[++index] == 's'))
-							return TokenNamethis;
-						else
-							return InternalTokenNameIdentifier;
-					else
-						if ((data[index] == 'r') && (data[++index] == 'u') && (data[++index] == 'e'))
-							return TokenNametrue;
-						else
-//{ObjectTeams: check for team keyword (always checked except after '.' -- if seen set isOTsource)
-							if ((data[index] == 'e')
-								&& (data[++index] == 'a')
-								&& (data[++index] == 'm')
-								&& (this._isOTSource || this._dotSeen == 0)
-								&& !this.parsePureJavaOnly)
-							{
-								this._teamKeywordSeen = true;
-								return TokenNameteam;
-							} else
-//Markus Witte}
-							return InternalTokenNameIdentifier;
-				case 5 :
-					if ((data[++index] == 'h')
-						&& (data[++index] == 'r')
-						&& (data[++index] == 'o')
-						&& (data[++index] == 'w'))
-						return TokenNamethrow;
-					else
-						return InternalTokenNameIdentifier;
-				case 6 :
-//{ObjectTeams: check for tsuper keyword
-/* orig:
-					if ((data[++index] == 'h')
-						&& (data[++index] == 'r')
- :giro */
-				  if ((data[++index] == 'h')) {
-					if ((data[++index] == 'r')
-// orig:
-						&& (data[++index] == 'o')
-						&& (data[++index] == 'w')
-						&& (data[++index] == 's'))
-						return TokenNamethrows;
-					else
-						return InternalTokenNameIdentifier;
-// :giro
-				  } else {
-					if (   this._isOTSource
-						&& (data[index] == 's')
-						&& (data[++index] == 'u')
-						&& (data[++index] == 'p')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'r'))
-							return TokenNametsuper;
-					else
-						return InternalTokenNameIdentifier;
-				  }
-//Markus Witte}
-				case 9 :
-					if ((data[++index] == 'r')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 's')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'e')
-						&& (data[++index] == 'n')
-						&& (data[++index] == 't')) {
-						return TokenNametransient;
-					} else
-						return InternalTokenNameIdentifier;
-
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		case 'v' : //void volatile
-			switch (length) {
-				case 4 :
-					if ((data[++index] == 'o') && (data[++index] == 'i') && (data[++index] == 'd'))
-						return TokenNamevoid;
-					else
-						return InternalTokenNameIdentifier;
-				case 8 :
-					if ((data[++index] == 'o')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'a')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'e')) {
-						return TokenNamevolatile;
-					} else
-						return InternalTokenNameIdentifier;
-
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		case 'w' : //while widefp
-			switch (length) {
-//{ObjectTeams: check for with, when keywords
-
-				case 4 : // with when
-					switch (data[++index]) {
-					case 'i':
-						if (   this._isOTSource
-							&& (data[++index] == 't')
-						    && (data[++index] == 'h'))
-							return TokenNamewith;
-						break;
-					case 'h':
-						if (   this._isOTSource
-							&& (data[++index] == 'e')
-						    && (data[++index] == 'n'))
-							return TokenNamewhen;
-					}
-					return InternalTokenNameIdentifier;
-
-//Markus Witte}
-				case 5 :
-					if ((data[++index] == 'h')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'l')
-						&& (data[++index] == 'e'))
-						return TokenNamewhile;
-					else
-						return InternalTokenNameIdentifier;
-					//case 6:if ( (data[++index] =='i') && (data[++index]=='d') && (data[++index]=='e') && (data[++index]=='f')&& (data[++index]=='p'))
-					//return TokenNamewidefp ;
-					//else
-					//return TokenNameIdentifier;
-//{ObjectTeams: check for within keyword (also in std-Java files).
-				case 6 :
-					if ((data[++index] == 'i')
-						&& (data[++index] == 't')
-						&& (data[++index] == 'h')
-						&& (data[++index] == 'i')
-						&& (data[++index] == 'n')
-						&& !this.parsePureJavaOnly)
-						return TokenNamewithin;
-					else
-						return InternalTokenNameIdentifier;
-
-//Markus Witte}
-				default :
-					return InternalTokenNameIdentifier;
-			}
-
-		default :
-			return InternalTokenNameIdentifier;
-	}
-}
-
-
-public int scanNumber(boolean dotPrefix) throws InvalidInputException {
-
-	//when entering this method the currentCharacter is the first
-	//digit of the number. It may be preceeded by a '.' when
-	//dotPrefix is true
-
-	boolean floating = dotPrefix;
-	if (!dotPrefix && (this.currentCharacter == '0')) {
-		if (getNextChar('x', 'X') >= 0) { //----------hexa-----------------
-			int start = this.currentPosition;
-			consumeDigits(16, true);
-			int end = this.currentPosition;
-			if (getNextChar('l', 'L') >= 0) {
-				if (end == start) {
-					throw new InvalidInputException(INVALID_HEXA);
-				}
-				return TokenNameLongLiteral;
-			} else if (getNextChar('.')) {
-				// hexadecimal floating point literal
-				// read decimal part
-				boolean hasNoDigitsBeforeDot = end == start;
-				start = this.currentPosition;
-				consumeDigits(16, true);
-				end = this.currentPosition;
-				if (hasNoDigitsBeforeDot && end == start) {
-					if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-						throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-					}
-					throw new InvalidInputException(INVALID_HEXA);
-				}
-
-				if (getNextChar('p', 'P') >= 0) { // consume next character
-					this.unicodeAsBackSlash = false;
-					if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-							&& (this.source[this.currentPosition] == 'u')) {
-						getNextUnicodeChar();
-					} else {
-						if (this.withoutUnicodePtr != 0) {
-							unicodeStore();
-						}
-					}
-
-					if ((this.currentCharacter == '-')
-							|| (this.currentCharacter == '+')) { // consume next character
-						this.unicodeAsBackSlash = false;
-						if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-								&& (this.source[this.currentPosition] == 'u')) {
-							getNextUnicodeChar();
-						} else {
-							if (this.withoutUnicodePtr != 0) {
-								unicodeStore();
-							}
-						}
-					}
-					if (!ScannerHelper.isDigit(this.currentCharacter)) {
-						if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-							throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-						}
-						if (this.currentCharacter == '_') {
-							// wrongly place '_'
-							consumeDigits(10);
-							throw new InvalidInputException(INVALID_UNDERSCORE);
-						}
-						throw new InvalidInputException(INVALID_HEXA);
-					}
-					consumeDigits(10);
-					if (getNextChar('f', 'F') >= 0) {
-						if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-							throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-						}
-						return TokenNameFloatingPointLiteral;
-					}
-					if (getNextChar('d', 'D') >= 0) {
-						if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-							throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-						}
-						return TokenNameDoubleLiteral;
-					}
-					if (getNextChar('l', 'L') >= 0) {
-						if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-							throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-						}
-						throw new InvalidInputException(INVALID_HEXA);
-					}
-					if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-						throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-					}
-					return TokenNameDoubleLiteral;
-				} else {
-					if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-						throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-					}
-					throw new InvalidInputException(INVALID_HEXA);
-				}
-			} else if (getNextChar('p', 'P') >= 0) { // consume next character
-				this.unicodeAsBackSlash = false;
-				if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-						&& (this.source[this.currentPosition] == 'u')) {
-					getNextUnicodeChar();
-				} else {
-					if (this.withoutUnicodePtr != 0) {
-						unicodeStore();
-					}
-				}
-
-				if ((this.currentCharacter == '-')
-						|| (this.currentCharacter == '+')) { // consume next character
-					this.unicodeAsBackSlash = false;
-					if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-							&& (this.source[this.currentPosition] == 'u')) {
-						getNextUnicodeChar();
-					} else {
-						if (this.withoutUnicodePtr != 0) {
-							unicodeStore();
-						}
-					}
-				}
-				if (!ScannerHelper.isDigit(this.currentCharacter)) {
-					if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-						throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-					}
-					if (this.currentCharacter == '_') {
-						// wrongly place '_'
-						consumeDigits(10);
-						throw new InvalidInputException(INVALID_UNDERSCORE);
-					}
-					throw new InvalidInputException(INVALID_FLOAT);
-				}
-				consumeDigits(10);
-				if (getNextChar('f', 'F') >= 0) {
-					if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-						throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-					}
-					return TokenNameFloatingPointLiteral;
-				}
-				if (getNextChar('d', 'D') >= 0) {
-					if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-						throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-					}
-					return TokenNameDoubleLiteral;
-				}
-				if (getNextChar('l', 'L') >= 0) {
-					if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-						throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-					}
-					throw new InvalidInputException(INVALID_HEXA);
-				}
-				if (this.sourceLevel < ClassFileConstants.JDK1_5) {
-					throw new InvalidInputException(ILLEGAL_HEXA_LITERAL);
-				}
-				return TokenNameDoubleLiteral;
-			} else {
-				if (end == start)
-					throw new InvalidInputException(INVALID_HEXA);
-				return TokenNameIntegerLiteral;
-			}
-		} else if (getNextChar('b', 'B') >= 0) { //----------binary-----------------
-			int start = this.currentPosition;
-			consumeDigits(2, true);
-			int end = this.currentPosition;
-			if (end == start) {
-				if (this.sourceLevel < ClassFileConstants.JDK1_7) {
-					throw new InvalidInputException(BINARY_LITERAL_NOT_BELOW_17);
-				}
-				throw new InvalidInputException(INVALID_BINARY);
-			}
-			if (getNextChar('l', 'L') >= 0) {
-				if (this.sourceLevel < ClassFileConstants.JDK1_7) {
-					throw new InvalidInputException(BINARY_LITERAL_NOT_BELOW_17);
-				}
-				return TokenNameLongLiteral;
-			}
-			if (this.sourceLevel < ClassFileConstants.JDK1_7) {
-				throw new InvalidInputException(BINARY_LITERAL_NOT_BELOW_17);
-			}
-			return TokenNameIntegerLiteral;
-		}
-
-		//there is no x or X nor b or B in the number
-		//potential octal
-		if (getNextCharAsDigit()) { //-------------potential octal-----------------
-			consumeDigits(10);
-
-			if (getNextChar('l', 'L') >= 0) {
-				return TokenNameLongLiteral;
-			}
-
-			if (getNextChar('f', 'F') >= 0) {
-				return TokenNameFloatingPointLiteral;
-			}
-
-			if (getNextChar('d', 'D') >= 0) {
-				return TokenNameDoubleLiteral;
-			} else { //make the distinction between octal and float ....
-				boolean isInteger = true;
-				if (getNextChar('.')) {
-					isInteger = false;
-					consumeDigits(10);
-				}
-				if (getNextChar('e', 'E') >= 0) { // consume next character
-					isInteger = false;
-					this.unicodeAsBackSlash = false;
-					if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-							&& (this.source[this.currentPosition] == 'u')) {
-						getNextUnicodeChar();
-					} else {
-						if (this.withoutUnicodePtr != 0) {
-							unicodeStore();
-						}
-					}
-
-					if ((this.currentCharacter == '-')
-							|| (this.currentCharacter == '+')) { // consume next character
-						this.unicodeAsBackSlash = false;
-						if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-								&& (this.source[this.currentPosition] == 'u')) {
-							getNextUnicodeChar();
-						} else {
-							if (this.withoutUnicodePtr != 0) {
-								unicodeStore();
-							}
-						}
-					}
-					if (!ScannerHelper.isDigit(this.currentCharacter)) {
-						if (this.currentCharacter == '_') {
-							// wrongly place '_'
-							consumeDigits(10);
-							throw new InvalidInputException(INVALID_UNDERSCORE);
-						}
-						throw new InvalidInputException(INVALID_FLOAT);
-					}
-					consumeDigits(10);
-				}
-				if (getNextChar('f', 'F') >= 0)
-					return TokenNameFloatingPointLiteral;
-				if (getNextChar('d', 'D') >= 0 || !isInteger)
-					return TokenNameDoubleLiteral;
-				return TokenNameIntegerLiteral;
-			}
-		} else {
-			/* carry on */
-		}
-	}
-
-	consumeDigits(10);
-
-	if ((!dotPrefix) && (getNextChar('l', 'L') >= 0))
-		return TokenNameLongLiteral;
-
-	if ((!dotPrefix) && (getNextChar('.'))) { //decimal part that can be empty
-		consumeDigits(10, true);
-		floating = true;
-	}
-
-	//if floating is true both exponant and suffix may be optional
-
-	if (getNextChar('e', 'E') >= 0) {
-		floating = true;
-		// consume next character
-		this.unicodeAsBackSlash = false;
-		if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-				&& (this.source[this.currentPosition] == 'u')) {
-			getNextUnicodeChar();
-		} else {
-			if (this.withoutUnicodePtr != 0) {
-				unicodeStore();
-			}
-		}
-
-		if ((this.currentCharacter == '-')
-				|| (this.currentCharacter == '+')) { // consume next character
-			this.unicodeAsBackSlash = false;
-			if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
-					&& (this.source[this.currentPosition] == 'u')) {
-				getNextUnicodeChar();
-			} else {
-				if (this.withoutUnicodePtr != 0) {
-					unicodeStore();
-				}
-			}
-		}
-		if (!ScannerHelper.isDigit(this.currentCharacter)) {
-			if (this.currentCharacter == '_') {
-				// wrongly place '_'
-				consumeDigits(10);
-				throw new InvalidInputException(INVALID_UNDERSCORE);
-			}
-			throw new InvalidInputException(INVALID_FLOAT);
-		}
-		// current character is a digit so we expect no digit first (the next character could be an underscore)
-		consumeDigits(10);
-	}
-
-	if (getNextChar('d', 'D') >= 0)
-		return TokenNameDoubleLiteral;
-	if (getNextChar('f', 'F') >= 0)
-		return TokenNameFloatingPointLiteral;
-
-	//the long flag has been tested before
-
-	return floating ? TokenNameDoubleLiteral : TokenNameIntegerLiteral;
-}
-
-/**
- * Search the line number corresponding to a specific position
- * @param position int
- * @return int
- */
-@Override
-public final int getLineNumber(int position) {
-	return Util.getLineNumber(position, this.lineEnds, 0, this.linePtr);
-}
-@Override
-public final void setSource(char[] sourceString){
-	//the source-buffer is set to sourceString
-
-	int sourceLength;
-	if (sourceString == null) {
-		this.source = CharOperation.NO_CHAR;
-		sourceLength = 0;
-	} else {
-		this.source = sourceString;
-		sourceLength = sourceString.length;
-	}
-	this.startPosition = -1;
-	this.eofPosition = sourceLength;
-	this.initialPosition = this.currentPosition = 0;
-	this.containsAssertKeyword = false;
-	this.linePtr = -1;
-//{ObjectTeams: reset to default mode (std-Java).
-	resetOTFlags();
-// SH}
-}
-/*
- * Should be used if a parse (usually a diet parse) has already been performed on the unit,
- * so as to get the already computed line end positions.
- */
-public final void setSource(char[] contents, CompilationResult compilationResult) {
-	if (contents == null) {
-		char[] cuContents = compilationResult.compilationUnit.getContents();
-		setSource(cuContents);
-	} else {
-		setSource(contents);
-	}
-	int[] lineSeparatorPositions = compilationResult.lineSeparatorPositions;
-	if (lineSeparatorPositions != null) {
-		this.lineEnds = lineSeparatorPositions;
-		this.linePtr = lineSeparatorPositions.length - 1;
-	}
-}
-/*
- * Should be used if a parse (usually a diet parse) has already been performed on the unit,
- * so as to get the already computed line end positions.
- */
-public final void setSource(CompilationResult compilationResult) {
-	setSource(null, compilationResult);
-}
-@Override
-public String toString() {
-	if (this.startPosition == this.eofPosition)
-		return "EOF\n\n" + new String(this.source); //$NON-NLS-1$
-	if (this.currentPosition > this.eofPosition)
-		return "behind the EOF\n\n" + new String(this.source); //$NON-NLS-1$
-	if (this.currentPosition <= 0)
-		return "NOT started!\n\n"+ new String(this.source); //$NON-NLS-1$
-
-	StringBuilder buffer = new StringBuilder();
-	if (this.startPosition < 1000) {
-		buffer.append(this.source, 0, this.startPosition);
-	} else {
-		buffer.append("<source beginning>\n...\n"); //$NON-NLS-1$
-		int line = Util.getLineNumber(this.startPosition-1000, this.lineEnds, 0, this.linePtr);
-		int lineStart = getLineStart(line);
-		buffer.append(this.source, lineStart, this.startPosition-lineStart);
-	}
-
-	buffer.append("\n===============================\nStarts here -->"); //$NON-NLS-1$
-	int middleLength = (this.currentPosition - 1) - this.startPosition + 1;
-	if (middleLength > -1) {
-		buffer.append(this.source, this.startPosition, middleLength);
-	}
-	buffer.append("<-- Ends here\n===============================\n"); //$NON-NLS-1$
-
-	buffer.append(this.source, (this.currentPosition - 1) + 1, this.eofPosition - (this.currentPosition - 1) - 1);
-
-	return buffer.toString();
-}
-public String toStringAction(int act) {
-	switch (act) {
-		case InternalTokenNameIdentifier :
-			return "Identifier(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-		case TokenNameabstract :
-			return "abstract"; //$NON-NLS-1$
-//{ObjectTeams: deal with OT-specific identifiers
-		case TokenNameas :
-			return "as";  //$NON-NLS-1$
-		case TokenNamecallin :
-			return "callin"; //$NON-NLS-1$
-		case TokenNameplayedBy :
-			return "playedBy"; //$NON-NLS-1$
-		case TokenNameprecedence :
-			return "precedence"; //$NON-NLS-1$
-		case TokenNameteam :
-			return "team"; //$NON-NLS-1$
-		case TokenNamewith :
-			return "with"; //$NON-NLS-1$
-		case TokenNamewithin :
-			return "within";  //$NON-NLS-1$
-		case TokenNamereplace:
-			return "replace"; //$NON-NLS-1$
-		case TokenNamebefore:
-			return "before"; //$NON-NLS-1$
-		case TokenNameafter:
-			return "after"; //$NON-NLS-1$
-		case TokenNameget:
-			return "get"; //$NON-NLS-1$
-		case TokenNameset:
-			return "set"; //$NON-NLS-1$
-// Markus Witte}
-		case TokenNameboolean :
-			return "boolean"; //$NON-NLS-1$
-		case TokenNamebreak :
-			return "break"; //$NON-NLS-1$
-		case TokenNamebyte :
-			return "byte"; //$NON-NLS-1$
-		case TokenNamecase :
-			return "case"; //$NON-NLS-1$
-		case TokenNamecatch :
-			return "catch"; //$NON-NLS-1$
-		case TokenNamechar :
-			return "char"; //$NON-NLS-1$
-		case TokenNameclass :
-			return "class"; //$NON-NLS-1$
-		case TokenNamecontinue :
-			return "continue"; //$NON-NLS-1$
-		case TokenNamedefault :
-			return "default"; //$NON-NLS-1$
-		case TokenNamedo :
-			return "do"; //$NON-NLS-1$
-		case TokenNamedouble :
-			return "double"; //$NON-NLS-1$
-		case TokenNameelse :
-			return "else"; //$NON-NLS-1$
-		case TokenNameextends :
-			return "extends"; //$NON-NLS-1$
-		case TokenNamefalse :
-			return "false"; //$NON-NLS-1$
-		case TokenNamefinal :
-			return "final"; //$NON-NLS-1$
-		case TokenNamefinally :
-			return "finally"; //$NON-NLS-1$
-		case TokenNamefloat :
-			return "float"; //$NON-NLS-1$
-		case TokenNamefor :
-			return "for"; //$NON-NLS-1$
-		case TokenNameif :
-			return "if"; //$NON-NLS-1$
-		case TokenNameimplements :
-			return "implements"; //$NON-NLS-1$
-		case TokenNameimport :
-			return "import"; //$NON-NLS-1$
-		case TokenNameinstanceof :
-			return "instanceof"; //$NON-NLS-1$
-		case TokenNameint :
-			return "int"; //$NON-NLS-1$
-		case TokenNameinterface :
-			return "interface"; //$NON-NLS-1$
-		case TokenNamelong :
-			return "long"; //$NON-NLS-1$
-		case TokenNamenative :
-			return "native"; //$NON-NLS-1$
-		case TokenNamenew :
-			return "new"; //$NON-NLS-1$
-		case TokenNamenull :
-			return "null"; //$NON-NLS-1$
-		case TokenNamepackage :
-			return "package"; //$NON-NLS-1$
-		case TokenNameprivate :
-			return "private"; //$NON-NLS-1$
-		case TokenNameprotected :
-			return "protected"; //$NON-NLS-1$
-		case TokenNamepublic :
-			return "public"; //$NON-NLS-1$
-		case TokenNamereturn :
-			return "return"; //$NON-NLS-1$
-		case TokenNameshort :
-			return "short"; //$NON-NLS-1$
-		case TokenNamestatic :
-			return "static"; //$NON-NLS-1$
-		case TokenNamesuper :
-			return "super"; //$NON-NLS-1$
-		case TokenNameswitch :
-			return "switch"; //$NON-NLS-1$
-		case TokenNamesynchronized :
-			return "synchronized"; //$NON-NLS-1$
-		case TokenNamethis :
-			return "this"; //$NON-NLS-1$
-		case TokenNamethrow :
-			return "throw"; //$NON-NLS-1$
-		case TokenNamethrows :
-			return "throws"; //$NON-NLS-1$
-		case TokenNametransient :
-			return "transient"; //$NON-NLS-1$
-		case TokenNametrue :
-			return "true"; //$NON-NLS-1$
-		case TokenNametry :
-			return "try"; //$NON-NLS-1$
-		case TokenNamevoid :
-			return "void"; //$NON-NLS-1$
-		case TokenNamevolatile :
-			return "volatile"; //$NON-NLS-1$
-		case TokenNamewhile :
-			return "while"; //$NON-NLS-1$
-
-		case TokenNameIntegerLiteral :
-			return "Integer(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-		case TokenNameLongLiteral :
-			return "Long(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-		case TokenNameFloatingPointLiteral :
-			return "Float(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-		case TokenNameDoubleLiteral :
-			return "Double(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-		case TokenNameCharacterLiteral :
-			return "Char(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-		case TokenNameStringLiteral :
-			return "String(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-
-		case TokenNamePLUS_PLUS :
-			return "++"; //$NON-NLS-1$
-		case TokenNameMINUS_MINUS :
-			return "--"; //$NON-NLS-1$
-		case TokenNameEQUAL_EQUAL :
-			return "=="; //$NON-NLS-1$
-//{ObjectTeams: deal with callins/callouts
-		case TokenNameBINDIN :
-			return "<-"; //$NON-NLS-1$
-		case TokenNameBINDOUT :
-			return "->"; //$NON-NLS-1$
-		case TokenNameCALLOUT_OVERRIDE :
-			return "=>"; //$NON-NLS-1$
-// Markus Witte}
-		case TokenNameLESS_EQUAL :
-			return "<="; //$NON-NLS-1$
-		case TokenNameGREATER_EQUAL :
-			return ">="; //$NON-NLS-1$
-		case TokenNameNOT_EQUAL :
-			return "!="; //$NON-NLS-1$
-		case TokenNameLEFT_SHIFT :
-			return "<<"; //$NON-NLS-1$
-		case TokenNameRIGHT_SHIFT :
-			return ">>"; //$NON-NLS-1$
-		case TokenNameUNSIGNED_RIGHT_SHIFT :
-			return ">>>"; //$NON-NLS-1$
-		case TokenNamePLUS_EQUAL :
-			return "+="; //$NON-NLS-1$
-		case TokenNameMINUS_EQUAL :
-			return "-="; //$NON-NLS-1$
-		case TokenNameARROW :
-			return "->"; //$NON-NLS-1$
-		case TokenNameMULTIPLY_EQUAL :
-			return "*="; //$NON-NLS-1$
-		case TokenNameDIVIDE_EQUAL :
-			return "/="; //$NON-NLS-1$
-		case TokenNameAND_EQUAL :
-			return "&="; //$NON-NLS-1$
-		case TokenNameOR_EQUAL :
-			return "|="; //$NON-NLS-1$
-		case TokenNameXOR_EQUAL :
-			return "^="; //$NON-NLS-1$
-		case TokenNameREMAINDER_EQUAL :
-			return "%="; //$NON-NLS-1$
-		case TokenNameLEFT_SHIFT_EQUAL :
-			return "<<="; //$NON-NLS-1$
-		case TokenNameRIGHT_SHIFT_EQUAL :
-			return ">>="; //$NON-NLS-1$
-		case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL :
-			return ">>>="; //$NON-NLS-1$
-		case TokenNameOR_OR :
-			return "||"; //$NON-NLS-1$
-		case TokenNameAND_AND :
-			return "&&"; //$NON-NLS-1$
-		case TokenNamePLUS :
-			return "+"; //$NON-NLS-1$
-		case TokenNameMINUS :
-			return "-"; //$NON-NLS-1$
-		case TokenNameNOT :
-			return "!"; //$NON-NLS-1$
-		case TokenNameREMAINDER :
-			return "%"; //$NON-NLS-1$
-		case TokenNameXOR :
-			return "^"; //$NON-NLS-1$
-		case TokenNameAND :
-			return "&"; //$NON-NLS-1$
-		case TokenNameMULTIPLY :
-			return "*"; //$NON-NLS-1$
-		case TokenNameOR :
-			return "|"; //$NON-NLS-1$
-		case TokenNameTWIDDLE :
-			return "~"; //$NON-NLS-1$
-		case TokenNameDIVIDE :
-			return "/"; //$NON-NLS-1$
-		case TokenNameGREATER :
-			return ">"; //$NON-NLS-1$
-		case TokenNameLESS :
-			return "<"; //$NON-NLS-1$
-		case TokenNameLPAREN :
-			return "("; //$NON-NLS-1$
-		case TokenNameRPAREN :
-			return ")"; //$NON-NLS-1$
-		case TokenNameLBRACE :
-			return "{"; //$NON-NLS-1$
-		case TokenNameRBRACE :
-			return "}"; //$NON-NLS-1$
-		case TokenNameLBRACKET :
-			return "["; //$NON-NLS-1$
-		case TokenNameRBRACKET :
-			return "]"; //$NON-NLS-1$
-		case TokenNameSEMICOLON :
-			return ";"; //$NON-NLS-1$
-		case TokenNameQUESTION :
-			return "?"; //$NON-NLS-1$
-		case TokenNameCOLON :
-			return ":"; //$NON-NLS-1$
-		case TokenNameCOLON_COLON :
-			return "::"; //$NON-NLS-1$
-		case TokenNameCOMMA :
-			return ","; //$NON-NLS-1$
-		case TokenNameDOT :
-			return "."; //$NON-NLS-1$
-		case TokenNameEQUAL :
-			return "="; //$NON-NLS-1$
-		case TokenNameEOF :
-			return "EOF"; //$NON-NLS-1$
-		case TokenNameWHITESPACE :
-			return "white_space(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
-		default :
-			return "not-a-token"; //$NON-NLS-1$
-	}
-}
-public void unicodeInitializeBuffer(int length) {
-	this.withoutUnicodePtr = length;
-	if (this.withoutUnicodeBuffer == null) this.withoutUnicodeBuffer = new char[length+(1+10)];
-	int bLength = this.withoutUnicodeBuffer.length;
-	if (1+length >= bLength) {
-		System.arraycopy(this.withoutUnicodeBuffer, 0, this.withoutUnicodeBuffer = new char[length + (1+10)], 0, bLength);
-	}
-	System.arraycopy(this.source, this.startPosition, this.withoutUnicodeBuffer, 1, length);
-}
-public void unicodeStore() {
-	int pos = ++this.withoutUnicodePtr;
-	if (this.withoutUnicodeBuffer == null) this.withoutUnicodeBuffer = new char[10];
-	int length = this.withoutUnicodeBuffer.length;
-	if (pos == length) {
-		System.arraycopy(this.withoutUnicodeBuffer, 0, this.withoutUnicodeBuffer = new char[length * 2], 0, length);
-	}
-	this.withoutUnicodeBuffer[pos] = this.currentCharacter;
-}
-public void unicodeStore(char character) {
-	int pos = ++this.withoutUnicodePtr;
-	if (this.withoutUnicodeBuffer == null) this.withoutUnicodeBuffer = new char[10];
-	int length = this.withoutUnicodeBuffer.length;
-	if (pos == length) {
-		System.arraycopy(this.withoutUnicodeBuffer, 0, this.withoutUnicodeBuffer = new char[length * 2], 0, length);
-	}
-	this.withoutUnicodeBuffer[pos] = character;
-}
-
-public static boolean isIdentifier(int token) {
-	return token == TerminalTokens.TokenNameIdentifier;
-}
-
-public static boolean isLiteral(int token) {
-	switch(token) {
-		case TerminalTokens.TokenNameIntegerLiteral:
-		case TerminalTokens.TokenNameLongLiteral:
-		case TerminalTokens.TokenNameFloatingPointLiteral:
-		case TerminalTokens.TokenNameDoubleLiteral:
-		case TerminalTokens.TokenNameStringLiteral:
-		case TerminalTokens.TokenNameTextBlock:
-		case TerminalTokens.TokenNameCharacterLiteral:
-			return true;
-		default:
-			return false;
+	@Override
+	public String toString() {
+		return this.delegate.toString();
 	}
-}
 
-public static boolean isKeyword(int token) {
-	switch(token) {
-		case TerminalTokens.TokenNameabstract:
-		case TerminalTokens.TokenNameassert:
-		case TerminalTokens.TokenNamebyte:
-		case TerminalTokens.TokenNamebreak:
-		case TerminalTokens.TokenNameboolean:
-		case TerminalTokens.TokenNamecase:
-		case TerminalTokens.TokenNamechar:
-		case TerminalTokens.TokenNamecatch:
-		case TerminalTokens.TokenNameclass:
-		case TerminalTokens.TokenNamecontinue:
-		case TerminalTokens.TokenNamedo:
-		case TerminalTokens.TokenNamedouble:
-		case TerminalTokens.TokenNamedefault:
-		case TerminalTokens.TokenNameelse:
-		case TerminalTokens.TokenNameextends:
-		case TerminalTokens.TokenNamefor:
-		case TerminalTokens.TokenNamefinal:
-		case TerminalTokens.TokenNamefloat:
-		case TerminalTokens.TokenNamefalse:
-		case TerminalTokens.TokenNamefinally:
-		case TerminalTokens.TokenNameif:
-		case TerminalTokens.TokenNameint:
-		case TerminalTokens.TokenNameimport:
-		case TerminalTokens.TokenNameinterface:
-		case TerminalTokens.TokenNameimplements:
-		case TerminalTokens.TokenNameinstanceof:
-		case TerminalTokens.TokenNamelong:
-		case TerminalTokens.TokenNamenew:
-		case TerminalTokens.TokenNamenon_sealed:
-		case TerminalTokens.TokenNamenull:
-		case TerminalTokens.TokenNamenative:
-		case TerminalTokens.TokenNamepublic:
-		case TerminalTokens.TokenNamepackage:
-		case TerminalTokens.TokenNameprivate:
-		case TerminalTokens.TokenNameprotected:
-		case TerminalTokens.TokenNamereturn:
-		case TerminalTokens.TokenNameshort:
-		case TerminalTokens.TokenNamesuper:
-		case TerminalTokens.TokenNamestatic:
-		case TerminalTokens.TokenNameswitch:
-		case TerminalTokens.TokenNamestrictfp:
-		case TerminalTokens.TokenNamesynchronized:
-		case TerminalTokens.TokenNametry:
-		case TerminalTokens.TokenNamethis:
-		case TerminalTokens.TokenNametrue:
-		case TerminalTokens.TokenNamethrow:
-		case TerminalTokens.TokenNamethrows:
-		case TerminalTokens.TokenNametransient:
-		case TerminalTokens.TokenNamevoid:
-		case TerminalTokens.TokenNamevolatile:
-		case TerminalTokens.TokenNamewhile:
-			return true;
-		default:
-			return false;
-	}
-}
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/IndexLocation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/IndexLocation.java
index bcac151..ca92268 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/IndexLocation.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/IndexLocation.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2013 IBM Corporation and others.
+ * Copyright (c) 2011, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -16,13 +16,14 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-
 import java.net.MalformedURLException;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 
 import org.eclipse.core.runtime.FileLocator;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.internal.core.util.Util;
 
 /**
  * The location of the index files are represented as {@link IndexLocation}
@@ -55,6 +56,7 @@
 	}
 
 	private final URL url; // url of the given index location
+	private final URI uri; // uri of the given index location
 
 	/**
 	 * Set to true if this index location is of an index file specified
@@ -65,16 +67,28 @@
 
 	protected IndexLocation(File file) {
 		URL tempUrl = null;
+		URI tempUri = null;
 		try {
-			tempUrl = file.toURI().toURL();
+			tempUri = file.toURI();
+			tempUrl = tempUri.toURL();
 		} catch (MalformedURLException e) {
 			// should not happen
+			Util.log(e, "Unexpected uri to url failure"); //$NON-NLS-1$
 		}
 		this.url = tempUrl;
+		this.uri = tempUri;
 	}
 
 	public IndexLocation(URL url) {
 		this.url = url;
+		URI tempUri = null;
+		try {
+			tempUri = url.toURI();
+		} catch (URISyntaxException e) {
+			// should not happen
+			Util.log(e, "Unexpected url to uri failure"); //$NON-NLS-1$
+		}
+		this.uri = tempUri;
 	}
 
 	/**
@@ -110,9 +124,13 @@
 		return this.url;
 	}
 
+	public URI getUri() {
+		return this.uri;
+	}
+
 	@Override
 	public int hashCode() {
-		return this.url.hashCode();
+		return this.uri != null ? this.uri.hashCode() : this.url.hashCode();
 	}
 
 	public boolean isParticipantIndex() {
@@ -133,6 +151,6 @@
 
 	@Override
 	public String toString() {
-		return this.url.toString();
+		return this.uri.toString();
 	}
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/JarIndexLocation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/JarIndexLocation.java
index c2aa864..3a11cf4 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/JarIndexLocation.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/JarIndexLocation.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011 IBM Corporation and others.
+ * Copyright (c) 2011, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -17,7 +17,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.JarURLConnection;
+import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.Objects;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
@@ -59,7 +61,11 @@
 	@Override
 	public boolean equals(Object other) {
 		if (!(other instanceof JarIndexLocation)) return false;
-		return this.localUrl.equals(((JarIndexLocation) other).localUrl);
+		try {
+			return Objects.equals(this.localUrl.toURI(),((JarIndexLocation) other).localUrl.toURI());
+		} catch (URISyntaxException e) {
+			return false;
+		}
 	}
 
 	@Override
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
index 9e7a8f2..c725686 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * 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
@@ -18,6 +18,7 @@
 import java.io.FileNotFoundException;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -28,6 +29,7 @@
 import java.util.ListIterator;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -296,8 +298,13 @@
 		// an existing index location exists - make sure it has not changed (i.e. the URL has not changed)
 		URL existingURL = indexLocation.getUrl();
 		if (newIndexURL != null) {
-			// if either URL is different then the index location has been updated so rebuild.
-			if(!newIndexURL.equals(existingURL)) {
+			boolean urisarequal = false;
+			try {
+				urisarequal = Objects.equals(newIndexURL.toURI(), existingURL.toURI());
+			} catch (URISyntaxException e) {
+				// ignore missing RFC 2396 compliance
+			}
+			if(!urisarequal) {
 				// URL has changed so remove the old index and create a new one
 				this.removeIndex(containerPath);
 				// create a new one
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java
index 490ce51..fd34337 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java
@@ -36,7 +36,7 @@
 
 	/* background processing */
 	protected volatile Thread processingThread;
-	protected Job progressJob;
+	protected volatile Job progressJob;
 
 	/* counter indicating whether job execution is enabled or not, disabled if <= 0
 	    it cannot go beyond 1 */
@@ -392,6 +392,8 @@
 						}
 						job = currentJob();
 					}
+					//make sure next index job will schedule new ProgressJob:
+					JobManager.this.progressJob = null;
 					return Status.OK_STATUS;
 				}
 			}
@@ -405,8 +407,9 @@
 
 						// must check for new job inside this sync block to avoid timing hole
 						if ((job = currentJob()) == null) {
-							if (this.progressJob != null) {
-								this.progressJob.cancel();
+							Job pJob = this.progressJob;
+							if (pJob != null) {
+								pJob.cancel();
 								this.progressJob = null;
 							}
 							if (idlingStart < 0)
@@ -433,10 +436,11 @@
 					try {
 						this.executing = true;
 						if (this.progressJob == null) {
-							this.progressJob = new ProgressJob(Messages.bind(Messages.jobmanager_indexing, "", "")); //$NON-NLS-1$ //$NON-NLS-2$
-							this.progressJob.setPriority(Job.LONG);
-							this.progressJob.setSystem(true);
-							this.progressJob.schedule();
+							ProgressJob pJob = new ProgressJob(Messages.bind(Messages.jobmanager_indexing, "", "")); //$NON-NLS-1$ //$NON-NLS-2$
+							pJob.setPriority(Job.LONG);
+							pJob.setSystem(true);
+							pJob.schedule();
+							this.progressJob = pJob;
 						}
 						/*boolean status = */job.execute(null);
 						//if (status == FAILED) request(job);