506149 advanced source lookup implementation

Change-Id: I690b45f9aa5912d77dd02609b583808d487306ea
Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ArgumentTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ArgumentTests.java
index 286cf49..d27b4f5 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ArgumentTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ArgumentTests.java
@@ -68,7 +68,10 @@
 			try {
                 assertNotNull("received notification of invalid line", line);
                 assertNotNull("buffer is null", buffer);
-                buffer.append(document.get(line.getOffset(), line.getLength()));
+                String text = document.get(line.getOffset(), line.getLength());
+				if (!JavaOutputHelpers.isKnownExtraneousOutput(text)) {
+					buffer.append(text);
+				}
 			} catch (BadLocationException e) {
 				e.printStackTrace();
 			}
@@ -346,7 +349,7 @@
 		//workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, JavaRuntime.newJREContainerPath(vm).toPortableString());
 
 		// use 'java' instead of 'javaw' to launch tests (javaw is problematic on JDK1.4.2)
-		Map<String, String> map = new HashMap<String, String>(1);
+		Map<String, String> map = new HashMap<>(1);
 		map.put(IJavaLaunchConfigurationConstants.ATTR_JAVA_COMMAND, "java");
 		workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE_SPECIFIC_ATTRS_MAP, map);
 
@@ -355,7 +358,7 @@
 		IProcess process = null;
 		ILaunch launch = null;
 		try {
-			HashSet<String> set = new HashSet<String>();
+			HashSet<String> set = new HashSet<>();
 			set.add(ILaunchManager.RUN_MODE);
 			ensurePreferredDelegate(workingCopy, set);
 			launch = workingCopy.launch(ILaunchManager.RUN_MODE, null);
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleInputTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleInputTests.java
index f147e17..966c47e 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleInputTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleInputTests.java
@@ -13,6 +13,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.core.runtime.Platform;
 import org.eclipse.debug.core.ILaunch;
 import org.eclipse.debug.core.ILaunchConfiguration;
 import org.eclipse.debug.core.ILaunchManager;
@@ -37,7 +38,7 @@
  */
 public class ConsoleInputTests extends AbstractDebugTest implements IConsoleLineTrackerExtension {
 
-	protected List<String> fLinesRead = new ArrayList<String>();
+	protected List<String> fLinesRead = new ArrayList<>();
 
 	protected boolean fStarted = false;
 
@@ -80,12 +81,7 @@
 		ILaunch launch = null;
 		try {
 			launch = configuration.launch(ILaunchManager.RUN_MODE, null);
-			synchronized (fConsoleLock) {
-				if (!fStarted) {
-					fConsoleLock.wait(30000);
-				}
-			}
-			assertNotNull("Console is null", fConsole);
+			waitStarted();
 			String[] list = appendAndGet(fConsole, "one\ntwo\nexit", 4);
 			verifyOutput(new String[]{"one", "two", "exitone", "two"}, list);
 
@@ -108,12 +104,7 @@
 		ILaunch launch = null;
 		try {
 			launch = configuration.launch(ILaunchManager.RUN_MODE, null);
-			synchronized (fConsoleLock) {
-				if (!fStarted) {
-					fConsoleLock.wait(30000);
-				}
-			}
-			assertNotNull("Console is null", fConsole);
+			waitStarted();
 			String[] list = appendAndGet(fConsole, "one\ntwo\n", 4);
 			verifyOutput(new String[]{"one", "two", "one", "two"}, list);
 
@@ -264,7 +255,9 @@
 			synchronized (fLinesRead) {
 				try {
 					String text = fConsole.getDocument().get(line.getOffset(), line.getLength());
-					fLinesRead.add(text);
+					if (!JavaOutputHelpers.isKnownExtraneousOutput(text)) {
+						fLinesRead.add(text);
+					}
 				} catch (BadLocationException e) {
 				    e.printStackTrace();
 				}
@@ -273,6 +266,20 @@
 		}
 	}
 
+	private void waitStarted() throws InterruptedException {
+		synchronized (fConsoleLock) {
+			if (!fStarted) {
+				fConsoleLock.wait(30000);
+			}
+		}
+		assertNotNull("Console is null", fConsole);
+		if (Platform.OS_MACOSX.equals(Platform.getOS())) {
+			// on OSX java process writes unexpected message to stderr due to https://bugs.openjdk.java.net/browse/JDK-8022291
+			// need to wait for the message to fully appear so it can be filtered in #lineAppended above
+			Thread.sleep(1000L);
+		}
+	}
+
 	/**
 	 * @see org.eclipse.debug.ui.console.IConsoleLineTracker#streamClosed()
 	 */
@@ -296,12 +303,7 @@
 		ILaunch launch = null;
 		try {
 			launch = configuration.launch(ILaunchManager.RUN_MODE, null);
-			synchronized (fConsoleLock) {
-				if (!fStarted) {
-					fConsoleLock.wait(30000);
-				}
-			}
-			assertNotNull("Console is null", fConsole);
+			waitStarted();
 			append(fConsole, "a");
 			deleteAll(fConsole);
 			append(fConsole, "b");
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JavaOutputHelpers.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JavaOutputHelpers.java
new file mode 100644
index 0000000..30f66f6
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/JavaOutputHelpers.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.debug.tests.core;
+
+import org.eclipse.core.runtime.Platform;
+
+public class JavaOutputHelpers {
+
+	/**
+	 * Workaround https://bugs.openjdk.java.net/browse/JDK-8022291, which results in extra java output line like below printed to stderr of java
+	 * process on OSX.
+	 *
+	 * <pre>
+	 * objc[62928]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java and /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/libinstrument.dylib. One of the two will be used. Which one is undefined.
+	 * </pre>
+	 */
+	public static boolean isKnownExtraneousOutput(String txt) {
+		if (!Platform.OS_MACOSX.equals(Platform.getOS())) {
+			return false;
+		}
+
+		return txt.startsWith("objc[") && txt.contains("Class JavaLaunchHelper is implemented in both");
+	}
+
+}
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/LineTrackerTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/LineTrackerTests.java
index bbffc19..6f79d79 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/LineTrackerTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/LineTrackerTests.java
@@ -45,7 +45,7 @@
 
 	protected IJavaDebugTarget fTarget;
 
-	protected List<String> fLinesRead = new ArrayList<String>();
+	protected List<String> fLinesRead = new ArrayList<>();
 
 	protected boolean fStarted = false;
 
@@ -195,7 +195,9 @@
 		if (fStarted) {
 			try {
 				String text = fConsole.getDocument().get(line.getOffset(), line.getLength());
-				fLinesRead.add(text);
+				if (!JavaOutputHelpers.isKnownExtraneousOutput(text)) {
+					fLinesRead.add(text);
+				}
 			} catch (BadLocationException e) {
 			    e.printStackTrace();
 			}
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/StratumTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/StratumTests.java
index ffe9f36..a3b5b85 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/StratumTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/StratumTests.java
@@ -10,11 +10,14 @@
  *******************************************************************************/
 package org.eclipse.jdt.debug.tests.core;
 
+import java.util.Arrays;
+
 import org.eclipse.jdt.debug.core.IJavaDebugTarget;
 import org.eclipse.jdt.debug.core.IJavaReferenceType;
 import org.eclipse.jdt.debug.core.IJavaStackFrame;
 import org.eclipse.jdt.debug.core.IJavaThread;
 import org.eclipse.jdt.debug.tests.AbstractDebugTest;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.JDIHelpers;
 
 /**
  * Tests strata.
@@ -40,8 +43,10 @@
 			assertNotNull("Breakpoint not hit within timeout period", thread);
 			IJavaReferenceType type = ((IJavaStackFrame)thread.getTopStackFrame()).getReferenceType();
 			String[] strata = type.getAvailableStrata();
-			assertEquals("Wrong number of available strata", 1, strata.length);
+			Arrays.sort(strata);
+			assertEquals("Wrong number of available strata", 2, strata.length);
 			assertEquals("Wrong strata", "Java", strata[0]);
+			assertEquals("Wrong strata", JDIHelpers.STRATA_ID, strata[1]);
 		} finally {
 			terminateAndRemove(thread);
 			removeAllBreakpoints();
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java
index d468df6..d06901e 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java
@@ -71,6 +71,8 @@
 	public static String JavaDebugPreferencePage_0;
 	public static String JavaDebugPreferencePage_20;
 
+	public static String JavaDebugPreferencePage_advancedSourcelookup;
+
 	public static String JavaDebugPreferencePage_only_include_exported_entries;
 	public static String JavaDebugPreferencePage_filterUnrelatedBreakpoints;
 	public static String JavaDebugPreferencePage_promptWhenDeletingCondidtionalBreakpoint;
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties
index 9e68739..23f19aa 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties
@@ -300,6 +300,7 @@
 JavaDebugPreferencePage_27=Modification
 JavaDebugPreferencePage_promptWhenDeletingCondidtionalBreakpoint=&Prompt for confirmation when deleting a conditional breakpoint from editor
 JavaDebugPreferencePage_0=See <a>''{0}''</a> for general debug settings.
+JavaDebugPreferencePage_advancedSourcelookup=Enable advanced source lookup
 JavaDebugPreferencePage_only_include_exported_entries=Onl&y include exported classpath entries when launching
 JavaDebugPreferencePage_filterUnrelatedBreakpoints=Do &not install breakpoints from unrelated projects
 JavaVariableLabelProvider_0=unavailable
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugPreferencePage.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugPreferencePage.java
index d4543af..eefbccf 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugPreferencePage.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugPreferencePage.java
@@ -91,6 +91,7 @@
 	private Button fAlertObsoleteButton;
 	private Button fPerformHCRWithCompilationErrors;
 	private Button fShowStepResult;
+	private Button fAdvancedSourcelookup;
 
 	// Timeout preference widgets
 	private JavaDebugIntegerFieldEditor fTimeoutText;
@@ -172,6 +173,9 @@
 		SWTFactory.createVerticalSpacer(composite, 1);
 		fShowStepResult = SWTFactory.createCheckButton(composite, DebugUIMessages.JavaDebugPreferencePage_ShowStepResult_1, null, false, 1);
 
+		SWTFactory.createVerticalSpacer(composite, 1);
+		fAdvancedSourcelookup = SWTFactory.createCheckButton(composite, DebugUIMessages.JavaDebugPreferencePage_advancedSourcelookup, null, false, 1);
+
 		setValues();
 
 		fTimeoutText.setPropertyChangeListener(this);
@@ -214,6 +218,7 @@
 			prefs.putBoolean(JDIDebugModel.PREF_SHOW_STEP_RESULT, fShowStepResult.getSelection());
 			prefs.putInt(JDIDebugModel.PREF_REQUEST_TIMEOUT, fTimeoutText.getIntValue());
 			prefs.putBoolean(JDIDebugModel.PREF_FILTER_BREAKPOINTS_FROM_UNRELATED_SOURCES, fFilterUnrelatedBreakpoints.getSelection());
+			prefs.putBoolean(JDIDebugPlugin.PREF_ENABLE_ADVANCED_SOURCELOOKUP, fAdvancedSourcelookup.getSelection());
 			try {
 				prefs.flush();
 			}
@@ -260,6 +265,7 @@
 			fShowStepResult.setSelection(prefs.getBoolean(JDIDebugModel.PREF_SHOW_STEP_RESULT, true));
 			fTimeoutText.setStringValue(new Integer(prefs.getInt(JDIDebugModel.PREF_REQUEST_TIMEOUT, JDIDebugModel.DEF_REQUEST_TIMEOUT)).toString());
 			fFilterUnrelatedBreakpoints.setSelection(prefs.getBoolean(JDIDebugModel.PREF_FILTER_BREAKPOINTS_FROM_UNRELATED_SOURCES, true));
+			fAdvancedSourcelookup.setSelection(prefs.getBoolean(JDIDebugPlugin.PREF_ENABLE_ADVANCED_SOURCELOOKUP, true));
 		}
 		prefs = DefaultScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN);
 		if(prefs != null) {
@@ -294,6 +300,7 @@
 			fShowStepResult.setSelection(prefs.getBoolean(JDIDebugModel.PREF_SHOW_STEP_RESULT, true));
 			fTimeoutText.setStringValue(new Integer(prefs.getInt(JDIDebugModel.PREF_REQUEST_TIMEOUT, JDIDebugModel.DEF_REQUEST_TIMEOUT)).toString());
 			fFilterUnrelatedBreakpoints.setSelection(prefs.getBoolean(JDIDebugModel.PREF_FILTER_BREAKPOINTS_FROM_UNRELATED_SOURCES, true));
+			fAdvancedSourcelookup.setSelection(prefs.getBoolean(JDIDebugPlugin.PREF_ENABLE_ADVANCED_SOURCELOOKUP, true));
 		}
 		prefs = InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN);
 		if(prefs != null) {
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPlugin.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPlugin.java
index 5d92208..fcef761 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPlugin.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPlugin.java
@@ -108,6 +108,14 @@
 			.getUniqueIdentifier() + ".all_instances_max_count"; //$NON-NLS-1$
 
 	/**
+	 * Boolean preference controlling if advanced sourcelookup is enabled.
+	 * 
+	 * @since 3.11
+	 */
+	public static final String PREF_ENABLE_ADVANCED_SOURCELOOKUP = JDIDebugPlugin
+			.getUniqueIdentifier() + ".enable_advanced_sourcelookup"; //$NON-NLS-1$
+
+	/**
 	 * Extension point for java logical structures.
 	 *
 	 * @since 3.1
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPluginPreferenceInitializer.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPluginPreferenceInitializer.java
index ca27983..df13b94 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPluginPreferenceInitializer.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/JDIDebugPluginPreferenceInitializer.java
@@ -44,5 +44,6 @@
 		node.putInt(JDIDebugPlugin.PREF_ALL_INSTANCES_MAX_COUNT, 100);
 		node.putBoolean(JDIDebugModel.PREF_FILTER_BREAKPOINTS_FROM_UNRELATED_SOURCES, true);
 		node.putBoolean(JDIDebugModel.PREF_SHOW_STEP_RESULT, true);
+		node.putBoolean(JDIDebugPlugin.PREF_ENABLE_ADVANCED_SOURCELOOKUP, true);
 	}
 }
diff --git a/org.eclipse.jdt.launching.javaagent/.classpath b/org.eclipse.jdt.launching.javaagent/.classpath
new file mode 100644
index 0000000..d1cc5bb
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jdt.launching.javaagent/.project b/org.eclipse.jdt.launching.javaagent/.project
new file mode 100644
index 0000000..173d0dc
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/.project
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jdt.launching.javaagent</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jdt.launching.javaagent/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.launching.javaagent/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..50e7022
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,414 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=error
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=error
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=ignore
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=error
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=error
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=error
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=enabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=ignore
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=error
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=error
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=error
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=error
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled
+org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,NORMAL,HIGH,HIGH
+org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX,EXPERIMENTAL,CONTEXTLAUNCHING
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=0
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=0
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=150
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=150
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/org.eclipse.jdt.launching.javaagent/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jdt.launching.javaagent/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..666d89f
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,109 @@
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=true
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=true
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=_Platform Debug Cleanups
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Platform Debug Formatting
+formatter_settings_version=12
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jdt.launching.javaagent/META-INF/MANIFEST.MF b/org.eclipse.jdt.launching.javaagent/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b24a91a
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jdt.launching.javaagent;singleton:=true
+Bundle-Version: 3.9.0.qualifier
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.objectweb.asm;bundle-version="[5.0.0,6.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.5
diff --git a/org.eclipse.jdt.launching.javaagent/pom.xml b/org.eclipse.jdt.launching.javaagent/pom.xml
new file mode 100644
index 0000000..1aea504
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2011-2016 Igor Fedorenko
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Public License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.eclipse.jdt</groupId>
+  <artifactId>org.eclipse.jdt.launching.javaagent</artifactId>
+  <version>3.9.0-SNAPSHOT</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>5.0.1</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.5.1</version>
+        <configuration>
+          <!-- http://maven.apache.org/plugins/maven-compiler-plugin/ -->
+          <source>1.5</source>
+          <target>1.5</target>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>3.0.2</version>
+        <configuration>
+          <archive>
+            <manifestEntries>
+              <Premain-Class>org.eclipse.jdt.launching.internal.javaagent.Premain</Premain-Class>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>2.4.3</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <createSourcesJar>true</createSourcesJar>
+              <relocations>
+                <relocation>
+                  <!--
+                    | javaagent classes are added to application classpath
+                    | relocate ASM to our internal package to avoid possible conflicts 
+                    -->
+                  <pattern>org.objectweb.asm</pattern>
+                  <shadedPattern>org.eclipse.jdt.launching.internal.org.objectweb.asm</shadedPattern>
+                </relocation>
+              </relocations>
+              <finalName>javaagent-shaded</finalName>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.eclipse.jdt.launching.javaagent/src/main/java/org/eclipse/jdt/launching/internal/javaagent/Premain.java b/org.eclipse.jdt.launching.javaagent/src/main/java/org/eclipse/jdt/launching/internal/javaagent/Premain.java
new file mode 100644
index 0000000..e286992
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/src/main/java/org/eclipse/jdt/launching/internal/javaagent/Premain.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.launching.internal.javaagent;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+
+import org.eclipse.jdt.launching.internal.weaving.ClassfileTransformer;
+
+public class Premain {
+	private static final ClassfileTransformer transformer = new ClassfileTransformer();
+
+	public static void premain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) {
+		// System.err.println("Advanced source lookup support loaded."); //$NON-NLS-1$
+
+		inst.addTransformer(new ClassFileTransformer() {
+			public byte[] transform(ClassLoader loader, final String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+				try {
+					if (protectionDomain == null) {
+						return null;
+					}
+
+					if (className == null) {
+						return null;
+					}
+
+					final CodeSource codeSource = protectionDomain.getCodeSource();
+					if (codeSource == null) {
+						return null;
+					}
+
+					final URL locationUrl = codeSource.getLocation();
+					if (locationUrl == null) {
+						return null;
+					}
+
+					final String location = locationUrl.toExternalForm();
+
+					return transformer.transform(classfileBuffer, location);
+				}
+				catch (Exception e) {
+					System.err.print("Could not instrument class " + className + ": "); //$NON-NLS-1$ //$NON-NLS-2$
+					e.printStackTrace(System.err);
+				}
+				return null;
+			}
+		});
+	}
+}
diff --git a/org.eclipse.jdt.launching.javaagent/src/main/java/org/eclipse/jdt/launching/internal/weaving/ClassfileTransformer.java b/org.eclipse.jdt.launching.javaagent/src/main/java/org/eclipse/jdt/launching/internal/weaving/ClassfileTransformer.java
new file mode 100644
index 0000000..a8cda8a
--- /dev/null
+++ b/org.eclipse.jdt.launching.javaagent/src/main/java/org/eclipse/jdt/launching/internal/weaving/ClassfileTransformer.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.launching.internal.weaving;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+
+public class ClassfileTransformer {
+
+	// must match JDIHelpers.STRATA_ID
+	private static final String STRATA_ID = "jdt"; //$NON-NLS-1$
+
+	public byte[] transform(byte[] classfileBuffer, final String location) {
+
+		final ClassReader r = new ClassReader(classfileBuffer, 0, classfileBuffer.length);
+		final ClassWriter w = new ClassWriter(0);
+
+		r.accept(new ClassVisitor(Opcodes.ASM5, w) {
+			@Override
+			public void visitSource(String source, String debug) {
+				String javaSource = source;
+				// TODO merge SMAP if present (always present when used together with Equinox weaver)
+				if (debug == null) {
+					StringBuilder smap = new StringBuilder();
+					smap.append("SMAP\n"); //$NON-NLS-1$
+					smap.append(javaSource).append("\n"); //$NON-NLS-1$
+					// default strata name
+					smap.append("Java\n"); //$NON-NLS-1$
+					smap.append("*S " + STRATA_ID + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					smap.append("*F\n"); //$NON-NLS-1$
+					smap.append("1 ").append(source).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					smap.append("2 ").append(location).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					// JSR-045, StratumSection
+					// "One FileSection and one LineSection (in either order) must follow the StratumSection"
+					smap.append("*L\n"); //$NON-NLS-1$
+					smap.append("*E\n"); //$NON-NLS-1$
+					debug = smap.toString();
+				}
+
+				super.visitSource(javaSource, debug);
+			}
+		}, 0);
+
+		return w.toByteArray();
+	}
+}
diff --git a/org.eclipse.jdt.launching/META-INF/MANIFEST.MF b/org.eclipse.jdt.launching/META-INF/MANIFEST.MF
index fc0027d..6f11206 100644
--- a/org.eclipse.jdt.launching/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.launching/META-INF/MANIFEST.MF
@@ -8,9 +8,11 @@
 Bundle-Localization: plugin
 Export-Package: org.eclipse.jdt.internal.launching;x-friends:="org.eclipse.jdt.debug.ui,org.eclipse.jdt.launching.macosx",
  org.eclipse.jdt.internal.launching.environments;x-internal:=true,
+ org.eclipse.jdt.internal.launching.sourcelookup.advanced;x-internal:=true,
  org.eclipse.jdt.launching,
  org.eclipse.jdt.launching.environments,
  org.eclipse.jdt.launching.sourcelookup,
+ org.eclipse.jdt.launching.sourcelookup.advanced,
  org.eclipse.jdt.launching.sourcelookup.containers
 Require-Bundle: org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
  org.eclipse.jdt.core;bundle-version="[3.8.0,4.0.0)",
diff --git a/org.eclipse.jdt.launching/build.properties b/org.eclipse.jdt.launching/build.properties
index 4d9e533..019787e 100644
--- a/org.eclipse.jdt.launching/build.properties
+++ b/org.eclipse.jdt.launching/build.properties
@@ -14,6 +14,7 @@
                java.policy.applet,\
                .,\
                lib/launchingsupport.jar,\
+               lib/javaagent-shaded.jar,\
                META-INF/,\
                .options
 
@@ -27,4 +28,4 @@
 
 javacWarnings..=-unavoidableGenericProblems
 customBuildCallbacks = customBuildCallbacks.xml
-customBuildCallbacks.failonerror = true
\ No newline at end of file
+customBuildCallbacks.failonerror = true
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/LaunchingPlugin.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/LaunchingPlugin.java
index 6711557..a738f22 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/LaunchingPlugin.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/LaunchingPlugin.java
@@ -69,6 +69,7 @@
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport;
 import org.eclipse.jdt.launching.IRuntimeClasspathEntry2;
 import org.eclipse.jdt.launching.IVMConnector;
 import org.eclipse.jdt.launching.IVMInstall;
@@ -509,6 +510,8 @@
 	@Override
 	public void stop(BundleContext context) throws Exception {
 		try {
+			AdvancedSourceLookupSupport.stop();
+
 			DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
 			DebugPlugin.getDefault().removeDebugEventListener(this);
 			ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
@@ -556,6 +559,8 @@
 		ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE);
 		DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
 		DebugPlugin.getDefault().addDebugEventListener(this);
+
+		AdvancedSourceLookupSupport.start();
 	}
 
 	/**
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedRemoteJavaLaunchDelegate.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedRemoteJavaLaunchDelegate.java
new file mode 100644
index 0000000..eb02b32
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedRemoteJavaLaunchDelegate.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.createAdvancedLaunch;
+import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.isAdvancedSourcelookupEnabled;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.jdt.internal.launching.JavaRemoteApplicationLaunchConfigurationDelegate;
+
+public class AdvancedRemoteJavaLaunchDelegate extends JavaRemoteApplicationLaunchConfigurationDelegate {
+
+	@Override
+	public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
+		if (!isAdvancedSourcelookupEnabled()) {
+			return super.getLaunch(configuration, mode);
+		}
+		return createAdvancedLaunch(configuration, mode);
+	}
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedSourceLookupDirector.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedSourceLookupDirector.java
new file mode 100644
index 0000000..49cf93b
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedSourceLookupDirector.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2014-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
+import org.eclipse.jdt.internal.launching.JavaSourceLookupDirector;
+import org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedSourceLookupParticipant;
+import org.eclipse.jdt.launching.sourcelookup.containers.JavaSourceLookupParticipant;
+
+public class AdvancedSourceLookupDirector extends JavaSourceLookupDirector {
+	// Note to self: JavaSourceLookupDirector parent class is useful because it allows custom source lookup path in the launch configuration.
+
+	public static final String ID = "org.eclipse.jdt.launching.sourceLocator.JavaAdvancedSourceLookupDirector"; //$NON-NLS-1$
+
+	private final String mode;
+
+	public AdvancedSourceLookupDirector() {
+		this(null);
+	}
+
+	public AdvancedSourceLookupDirector(String mode) {
+		this.mode = mode;
+	}
+
+	@Override
+	public void initializeParticipants() {
+		final List<ISourceLookupParticipant> participants = new ArrayList<>();
+		if (mode == null || ILaunchManager.DEBUG_MODE.equals(mode)) {
+			participants.addAll(getSourceLookupParticipants());
+		}
+
+		// fall-back to default JDT behaviour if we can't find matching sources
+		// in most cases this means scanning workspace for any source or binary with matching name
+		participants.add(new JavaSourceLookupParticipant());
+
+		addParticipants(participants.toArray(new ISourceLookupParticipant[participants.size()]));
+	}
+
+	protected Collection<ISourceLookupParticipant> getSourceLookupParticipants() {
+		return Collections.singleton(new AdvancedSourceLookupParticipant());
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedSourceLookupSupport.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedSourceLookupSupport.java
new file mode 100644
index 0000000..254ae00
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/AdvancedSourceLookupSupport.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2015-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.Launch;
+import org.eclipse.debug.core.model.IPersistableSourceLocator;
+import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2;
+import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
+import org.eclipse.jdt.internal.launching.LaunchingPlugin;
+
+public class AdvancedSourceLookupSupport {
+
+	// TODO consider moving to LaunchingPlugin
+	public static final String ID_sourceContainerResolvers = LaunchingPlugin.ID_PLUGIN + ".sourceContainerResolvers"; //$NON-NLS-1$
+
+	// TODO consider moving to LaunchingPlugin
+	public static final String ID_workspaceProjectDescribers = LaunchingPlugin.ID_PLUGIN + ".workspaceProjectDescribers"; //$NON-NLS-1$
+
+	private static BackgroundProcessingJob backgroundJob;
+
+	private static volatile WorkspaceProjectSourceContainers workspaceProjects;
+	private static final Lock workspaceProjectsLock = new ReentrantLock();
+
+	private AdvancedSourceLookupSupport() {
+	}
+
+	public static void start() {
+		backgroundJob = new BackgroundProcessingJob();
+	}
+
+	public static void stop() {
+		backgroundJob.cancel();
+		backgroundJob = null;
+
+		workspaceProjectsLock.lock();
+		try {
+			if (workspaceProjects != null) {
+				workspaceProjects.close();
+				workspaceProjects = null;
+			}
+		}
+		finally {
+			workspaceProjectsLock.unlock();
+		}
+	}
+
+	public static void schedule(IRunnableWithProgress task) {
+		backgroundJob.schedule(task);
+	}
+
+	public static WorkspaceProjectSourceContainers getWorkspaceJavaProjects(IProgressMonitor monitor) throws CoreException {
+		return getWorkspaceJavaProjects0(monitor);
+	}
+
+	private static WorkspaceProjectSourceContainers getWorkspaceJavaProjects0(IProgressMonitor monitor) throws CoreException {
+		// this is convoluted, but I could not think of a simpler implementation
+
+		// when monitor==null, we are most likely on UI thread and must not block, hence immediate return
+		if (monitor == null || workspaceProjects != null) {
+			return workspaceProjects;
+		}
+
+		// when monitor!=null, try to get the lock but check for cancellation periodically
+		try {
+			while (!workspaceProjectsLock.tryLock(500, TimeUnit.MILLISECONDS)) {
+				if (monitor.isCanceled()) {
+					return workspaceProjects;
+				}
+			}
+		}
+		catch (InterruptedException e) {
+			Thread.currentThread().interrupt(); // restore interrupted status
+			return workspaceProjects;
+		}
+
+		// got the lock, do the initialization if another thread didn't do it already
+		// note that double-check locking is okay on java 5+ with volatile fields
+		try {
+			if (workspaceProjects == null) {
+				WorkspaceProjectSourceContainers _workspaceProjects = new WorkspaceProjectSourceContainers();
+				_workspaceProjects.initialize(monitor);
+
+				// assign only fully initialized instance, otherwise monitor==null branch above may misbehave
+				workspaceProjects = _workspaceProjects;
+			}
+		}
+		finally {
+			// release the lock in finally{} block
+			workspaceProjectsLock.unlock();
+		}
+
+		return workspaceProjects;
+	}
+
+	public static String getJavaagentString() {
+		return "-javaagent:" + getJavaagentLocation(); //$NON-NLS-1$
+	}
+
+	public static String getJavaagentLocation() {
+		return LaunchingPlugin.getFileInPlugin(new Path("lib/javaagent-shaded.jar")).getAbsolutePath(); //$NON-NLS-1$
+	}
+
+	/**
+	 * Workaround a deficiency of ISourceLookupParticipant API, which does not provide access to a progress monitor.
+	 *
+	 * <p>
+	 * This method can be called in three different cases:
+	 * <ol>
+	 * <li>from UI thread, in which case {@code null} is return. this tells the caller to only perform fast operations (i.e. cache lookups) on this
+	 * thread and submit any long-running operations as background jobs
+	 * <li>from background job with existing IProgressMonitor, in which case the monitor is returned
+	 * <li>from background job without IProgressMonitor, in which case {@link NullProgressMonitor} is returned.
+	 * </ol>
+	 */
+	public static IProgressMonitor getContextMonitor(IProgressMonitor monitor) {
+		if (monitor == null) {
+			Job job = Job.getJobManager().currentJob();
+			if (job != null) {
+				// current implementation can perform workspace project cache initialization on system job without any user feedback
+				// although eclipse ui remains responsive, source lookup will appear to do nothing until initialization is complete
+				// a fix requires changes to ISourceLookupParticipant#findSourceElements API to accept user-visible progress monitor
+				monitor = new NullProgressMonitor();
+			}
+		}
+		return monitor;
+	}
+
+	/**
+	 * Returns a launch object to use when launching the given launch configuration in the given mode. The returned launch object is preconfigured to
+	 * use {@link AdvancedSourceLookupDirector} as the source locator.
+	 */
+	public static ILaunch createAdvancedLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
+		return new Launch(configuration, mode, createSourceLocator(AdvancedSourceLookupDirector.ID, configuration));
+	}
+
+	/**
+	 * Creates and returns new {@link IPersistableSourceLocator} of the specified type and with the provided configuration.
+	 */
+	public static IPersistableSourceLocator createSourceLocator(String type, ILaunchConfiguration configuration) throws CoreException {
+		ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+
+		IPersistableSourceLocator locator = launchManager.newSourceLocator(type);
+		String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null);
+		if (memento == null) {
+			locator.initializeDefaults(configuration);
+		} else {
+			if (locator instanceof IPersistableSourceLocator2) {
+				((IPersistableSourceLocator2) locator).initializeFromMemento(memento, configuration);
+			} else {
+				locator.initializeFromMemento(memento);
+			}
+		}
+
+		return locator;
+	}
+
+	public static boolean isAdvancedSourcelookupEnabled() {
+		return Platform.getPreferencesService().getBoolean(JDIDebugPlugin.getUniqueIdentifier(), JDIDebugPlugin.PREF_ENABLE_ADVANCED_SOURCELOOKUP, true, null);
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/BackgroundProcessingJob.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/BackgroundProcessingJob.java
new file mode 100644
index 0000000..723ed2e
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/BackgroundProcessingJob.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.internal.launching.LaunchingPlugin;
+
+/**
+ * Simple background request processing queue implemented using {@link Job} API. Requests are executed in the order they arrive. Request execution
+ * delayed by {@value #EXECUTION_DELAY} milliseconds and all requests submitted during this period will be processed together.
+ */
+public class BackgroundProcessingJob extends Job {
+	private static final long EXECUTION_DELAY = 1000L;
+
+	private final ArrayList<IRunnableWithProgress> queue = new ArrayList<>();
+
+	public BackgroundProcessingJob() {
+		super(Messages.BackgroundProcessingJob_name);
+	}
+
+	@Override
+	protected IStatus run(IProgressMonitor monitor) {
+		ArrayList<IRunnableWithProgress> tasks;
+		synchronized (this.queue) {
+			tasks = new ArrayList<>(this.queue);
+			this.queue.clear();
+		}
+
+		SubMonitor progress = SubMonitor.convert(monitor, tasks.size());
+
+		List<IStatus> errors = new ArrayList<>();
+
+		for (IRunnableWithProgress task : tasks) {
+			try {
+				task.run(progress.split(1));
+			}
+			catch (CoreException e) {
+				errors.add(e.getStatus());
+			}
+		}
+
+		if (errors.isEmpty()) {
+			return Status.OK_STATUS;
+		}
+
+		if (errors.size() == 1) {
+			return errors.get(0);
+		}
+
+		return new MultiStatus(LaunchingPlugin.ID_PLUGIN, IStatus.ERROR, errors.toArray(new IStatus[errors.size()]), Messages.BackgroundProcessingJob_failed, null);
+	}
+
+	public void schedule(IRunnableWithProgress task) {
+		synchronized (queue) {
+			queue.add(task);
+			schedule(EXECUTION_DELAY);
+		}
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/CompositeSourceContainer.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/CompositeSourceContainer.java
new file mode 100644
index 0000000..8f7c030
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/CompositeSourceContainer.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
+
+public class CompositeSourceContainer extends org.eclipse.debug.core.sourcelookup.containers.CompositeSourceContainer {
+
+	private final ISourceContainer[] members;
+
+	private CompositeSourceContainer(Collection<ISourceContainer> members) {
+		this.members = members.toArray(new ISourceContainer[members.size()]);
+	}
+
+	@Override
+	public ISourceContainer[] getSourceContainers() throws CoreException {
+		return members;
+	}
+
+	@Override
+	public String getName() {
+		return null;
+	}
+
+	@Override
+	public ISourceContainerType getType() {
+		return null;
+	}
+
+	@Override
+	protected ISourceContainer[] createSourceContainers() throws CoreException {
+		return null;
+	}
+
+	@Override
+	public void dispose() {
+		super.dispose();
+		for (ISourceContainer member : members) {
+			member.dispose();
+		}
+		Arrays.fill(members, null);
+	}
+
+	public static ISourceContainer compose(Collection<ISourceContainer> containers) {
+		if (containers.isEmpty()) {
+			throw new IllegalArgumentException();
+		}
+		if (containers.size() == 1) {
+			return containers.iterator().next();
+		}
+		return new CompositeSourceContainer(containers);
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/DefaultProjectDescriber.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/DefaultProjectDescriber.java
new file mode 100644
index 0000000..6f1c83e
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/DefaultProjectDescriber.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import static org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedSourceLookup.getClasspath;
+import static org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedSourceLookup.getOutputDirectories;
+import static org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedSourceLookup.isSourceProject;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.launching.sourcelookup.advanced.IWorkspaceProjectDescriber;
+import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer;
+
+public class DefaultProjectDescriber implements IWorkspaceProjectDescriber {
+
+	@Override
+	public void describeProject(IJavaProject project, IJavaProjectSourceDescription description) throws CoreException {
+		if (isSourceProject(project)) {
+			description.addDependencies(getClasspath(project));
+			getOutputDirectories(project).forEach(f -> description.addLocation(f));
+			description.addSourceContainerFactory(() -> new JavaProjectSourceContainer(project));
+		}
+	}
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/FileHashing.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/FileHashing.java
new file mode 100644
index 0000000..09ce870
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/FileHashing.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Helpers to compute file content digests. Provides long-lived hasher instance with bounded cache of most recently requested files, which is useful
+ * to handle source lookup requests. Also provides factory of hasher instances with unbounded caches, which is useful to perform bulk workspace
+ * indexing.
+ */
+public class FileHashing {
+
+	public static interface Hasher {
+		Object hash(File file);
+	}
+
+	// default hasher with bounded cache.
+	// this is used when performing source lookup and number of unique files requested during the same debugging session is likely to be small.
+	private static final HasherImpl HASHER = new HasherImpl(5000);
+
+	/**
+	 * Returns default long-lived Hasher instance with bounded hash cache.
+	 */
+	public static Hasher hasher() {
+		return HASHER;
+	}
+
+	/**
+	 * Returns new Hasher instance with unbounded hash cache, useful for bulk hashing of projects and their dependencies.
+	 */
+	public static Hasher newHasher() {
+		return new HasherImpl(HASHER);
+	}
+
+	private static class CacheKey {
+		public final File file;
+
+		private final long length;
+
+		private final long lastModified;
+
+		public CacheKey(File file) throws IOException {
+			this.file = file.getCanonicalFile();
+			this.length = file.length();
+			this.lastModified = file.lastModified();
+		}
+
+		@Override
+		public int hashCode() {
+			int hash = 17;
+			hash = hash * 31 + file.hashCode();
+			hash = hash * 31 + (int) length;
+			hash = hash * 31 + (int) lastModified;
+			return hash;
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (obj == this) {
+				return true;
+			}
+			if (!(obj instanceof CacheKey)) {
+				return false;
+			}
+			CacheKey other = (CacheKey) obj;
+			return file.equals(other.file) && length == other.length && lastModified == other.lastModified;
+		}
+	}
+
+	private static class HashCode {
+		private final byte[] bytes;
+
+		public HashCode(byte[] bytes) {
+			this.bytes = bytes; // assumes this class "owns" the array from now on
+		}
+
+		@Override
+		public int hashCode() {
+			return Arrays.hashCode(bytes);
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (obj == this) {
+				return true;
+			}
+			if (!(obj instanceof HashCode)) {
+				return false;
+			}
+			return Arrays.equals(bytes, ((HashCode) obj).bytes);
+		}
+
+		@Override
+		public final String toString() {
+			StringBuilder sb = new StringBuilder(2 * bytes.length);
+			for (byte b : bytes) {
+				sb.append(hexDigits[(b >> 4) & 0xf]).append(hexDigits[b & 0xf]);
+			}
+			return sb.toString();
+		}
+
+		private static final char[] hexDigits = "0123456789abcdef".toCharArray(); //$NON-NLS-1$
+	}
+
+	private static class HasherImpl implements Hasher {
+
+		private final Map<CacheKey, HashCode> cache;
+
+		@SuppressWarnings("serial")
+		public HasherImpl(int cacheSize) {
+			this.cache = new LinkedHashMap<CacheKey, HashCode>() {
+				@Override
+				protected boolean removeEldestEntry(Map.Entry<CacheKey, HashCode> eldest) {
+					return size() > cacheSize;
+				}
+			};
+		}
+
+		public HasherImpl(HasherImpl initial) {
+			this.cache = new LinkedHashMap<>(initial.cache);
+		}
+
+		@Override
+		public Object hash(File file) {
+			if (file == null || !file.isFile()) {
+				return null;
+			}
+			try {
+				CacheKey cacheKey = new CacheKey(file);
+				synchronized (cache) {
+					HashCode hashCode = cache.get(cacheKey);
+					if (hashCode != null) {
+						return hashCode;
+					}
+				}
+				// don't hold cache lock while hashing file
+				HashCode hashCode = sha1(file);
+				synchronized (cache) {
+					cache.put(cacheKey, hashCode);
+				}
+				return hashCode;
+			}
+			catch (IOException e) {
+				return null; // file does not exist or can't be read
+			}
+		}
+
+	}
+
+	private static HashCode sha1(File file) throws IOException {
+		MessageDigest digest;
+		try {
+			digest = MessageDigest.getInstance("SHA1"); //$NON-NLS-1$
+		}
+		catch (NoSuchAlgorithmException e) {
+			throw new IllegalStateException("Unsupported JVM", e); //$NON-NLS-1$
+		}
+		byte[] buf = new byte[4096];
+		try (InputStream is = new FileInputStream(file)) {
+			int len;
+			while ((len = is.read(buf)) > 0) {
+				digest.update(buf, 0, len);
+			}
+		}
+		return new HashCode(digest.digest());
+	}
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/IJDIHelpers.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/IJDIHelpers.java
new file mode 100644
index 0000000..32a917a
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/IJDIHelpers.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import java.io.File;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IStackFrame;
+
+/**
+ * Helpers to extract source lookup location information from advanced source lookup JSR-45 strata.
+ */
+public interface IJDIHelpers {
+
+	public static final IJDIHelpers INSTANCE = new JDIHelpers();
+
+	/**
+	 * Return classes location the given element was loaded from or {@code null} if the location cannot be determined.
+	 */
+	public File getClassesLocation(Object element) throws DebugException;
+
+	/**
+	 * Returns source path of the given element or {@code null} if the source path cannot be determined. The returned path is relative to a sources
+	 * container.
+	 */
+	public String getSourcePath(Object element) throws DebugException;
+
+	/**
+	 * If the given element is a {@link IStackFrame}, returns classes locations of the stack frames "beneath" the given element. The returned iterable
+	 * does not include {@code null} elements.
+	 * 
+	 * Returns empty iterable if the given element is not a {@link IStackFrame}.
+	 */
+	public Iterable<File> getStackFramesClassesLocations(Object element) throws DebugException;
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/IRunnableWithProgress.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/IRunnableWithProgress.java
new file mode 100644
index 0000000..5e992a3
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/IRunnableWithProgress.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2012-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Runnable with progress interface. Almost exact copy of similar interfaces defined in jface and p2.
+ */
+@FunctionalInterface
+public interface IRunnableWithProgress {
+	public void run(IProgressMonitor monitor) throws CoreException;
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JDIHelpers.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JDIHelpers.java
new file mode 100644
index 0000000..057eff9
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JDIHelpers.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.jdt.debug.core.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaReferenceType;
+import org.eclipse.jdt.debug.core.IJavaStackFrame;
+import org.eclipse.jdt.debug.core.IJavaType;
+import org.eclipse.jdt.debug.core.IJavaValue;
+import org.eclipse.jdt.debug.core.IJavaVariable;
+
+public final class JDIHelpers implements IJDIHelpers {
+
+	// must match ClassfileTransformer.STRATA_ID
+	public static final String STRATA_ID = "jdt"; //$NON-NLS-1$
+
+	JDIHelpers() {
+	}
+
+	// jdt debug boilerplate and other ideas were originally "borrowed" from
+	// org.eclipse.pde.internal.launching.sourcelookup.PDESourceLookupQuery.run()
+
+	@Override
+	public File getClassesLocation(Object element) throws DebugException {
+		IJavaReferenceType declaringType = null;
+		if (element instanceof IJavaStackFrame) {
+			IJavaStackFrame stackFrame = (IJavaStackFrame) element;
+			declaringType = stackFrame.getReferenceType();
+		} else if (element instanceof IJavaObject) {
+			IJavaType javaType = ((IJavaObject) element).getJavaType();
+			if (javaType instanceof IJavaReferenceType) {
+				declaringType = (IJavaReferenceType) javaType;
+			}
+		} else if (element instanceof IJavaReferenceType) {
+			declaringType = (IJavaReferenceType) element;
+		} else if (element instanceof IJavaVariable) {
+			IJavaVariable javaVariable = (IJavaVariable) element;
+			IJavaType javaType = ((IJavaValue) javaVariable.getValue()).getJavaType();
+			if (javaType instanceof IJavaReferenceType) {
+				declaringType = (IJavaReferenceType) javaType;
+			}
+		}
+
+		if (declaringType != null) {
+			String[] locations = declaringType.getSourceNames(STRATA_ID);
+
+			if (locations == null || locations.length < 2) {
+				return null;
+			}
+
+			try {
+				URL url = new URL(locations[1]);
+				if ("file".equals(url.getProtocol())) { //$NON-NLS-1$
+					return new File(url.getPath()).toPath().normalize().toFile();
+				}
+			}
+			catch (MalformedURLException e) {
+				// fall through
+			}
+		}
+
+		return null;
+	}
+
+	@Override
+	public String getSourcePath(Object element) throws DebugException {
+		IJavaReferenceType declaringType = null;
+		if (element instanceof IJavaStackFrame) {
+			IJavaStackFrame stackFrame = (IJavaStackFrame) element;
+			// under JSR 45 source path from the stack frame is more precise than anything derived from the type
+			String sourcePath = stackFrame.getSourcePath(STRATA_ID);
+			if (sourcePath != null) {
+				return sourcePath;
+			}
+
+			declaringType = stackFrame.getReferenceType();
+		} else if (element instanceof IJavaObject) {
+			IJavaType javaType = ((IJavaObject) element).getJavaType();
+			if (javaType instanceof IJavaReferenceType) {
+				declaringType = (IJavaReferenceType) javaType;
+			}
+		} else if (element instanceof IJavaReferenceType) {
+			declaringType = (IJavaReferenceType) element;
+		} else if (element instanceof IJavaVariable) {
+			IJavaType javaType = ((IJavaVariable) element).getJavaType();
+			if (javaType instanceof IJavaReferenceType) {
+				declaringType = (IJavaReferenceType) javaType;
+			}
+		}
+
+		if (declaringType != null) {
+			String[] sourcePaths = declaringType.getSourcePaths(STRATA_ID);
+
+			if (sourcePaths != null && sourcePaths.length > 0 && sourcePaths[0] != null) {
+				return sourcePaths[0];
+			}
+
+			return generateSourceName(declaringType.getName());
+		}
+
+		return null;
+	}
+
+	private static final IStackFrame[] EMPTY_STACK = new IStackFrame[0];
+
+	private IStackFrame[] getStackFrames(Object element) throws DebugException {
+		if (element instanceof IStackFrame) {
+			IStackFrame[] frames = ((IStackFrame) element).getThread().getStackFrames();
+			for (int i = 0; i < frames.length - 1; i++) {
+				if (frames[i] == element) {
+					return Arrays.copyOfRange(frames, i + 1, frames.length - 1);
+				}
+			}
+		}
+		return EMPTY_STACK;
+	}
+
+	@Override
+	public Iterable<File> getStackFramesClassesLocations(Object element) throws DebugException {
+		IStackFrame[] stack = getStackFrames(element);
+
+		return new Iterable<File>() {
+			@Override
+			public Iterator<File> iterator() {
+				return Arrays.stream(stack) //
+						.map(frame -> getClassesLocation(frame)) //
+						.filter(frameLocation -> frameLocation != null) //
+						.iterator();
+			}
+
+			File getClassesLocation(IStackFrame frame) {
+				// TODO consider ignoring DebugException for all IJDIHeloper methods
+				try {
+					return JDIHelpers.this.getClassesLocation(frame);
+				}
+				catch (DebugException e) {
+					return null;
+				}
+			}
+		};
+	}
+
+	// copy&paste from org.eclipse.pde.internal.launching.sourcelookup.PDESourceLookupQuery.generateSourceName(String)
+	private static String generateSourceName(String qualifiedTypeName) {
+		int index = qualifiedTypeName.indexOf('$');
+		if (index >= 0) {
+			qualifiedTypeName = qualifiedTypeName.substring(0, index);
+		}
+		return qualifiedTypeName.replace('.', File.separatorChar) + ".java"; //$NON-NLS-1$
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JDIStratumPropertyTester.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JDIStratumPropertyTester.java
new file mode 100644
index 0000000..74dc423
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JDIStratumPropertyTester.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2012-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.debug.core.DebugException;
+
+/**
+ * {@link PropertyTester} that returns {@code true} iff testee is a JDI object that has advanced source lookup JSR-45 stratum.
+ */
+public class JDIStratumPropertyTester extends PropertyTester {
+
+	@Override
+	public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+		boolean result;
+		try {
+			result = IJDIHelpers.INSTANCE.getClassesLocation(receiver) != null;
+		}
+		catch (DebugException e) {
+			result = false;
+		}
+		return result;
+	}
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JavaagentVariableResolver.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JavaagentVariableResolver.java
new file mode 100644
index 0000000..87633f0
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/JavaagentVariableResolver.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2015-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.variables.IDynamicVariable;
+import org.eclipse.core.variables.IDynamicVariableResolver;
+
+/**
+ * {@code sourcelookup_javaagent} dynamic variable resolver.
+ */
+public class JavaagentVariableResolver implements IDynamicVariableResolver {
+	@Override
+	public String resolveValue(IDynamicVariable variable, String argument) throws CoreException {
+		return AdvancedSourceLookupSupport.getJavaagentLocation();
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/Messages.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/Messages.java
new file mode 100644
index 0000000..d79d08f
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/Messages.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2015-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+	private static final String BUNDLE_NAME = "org.eclipse.jdt.internal.launching.sourcelookup.advanced.messages"; //$NON-NLS-1$
+	public static String BackgroundProcessingJob_name;
+	public static String BackgroundProcessingJob_failed;
+	static {
+		// initialize resource bundle
+		NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+	}
+
+	private Messages() {
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/WorkspaceProjectSourceContainers.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/WorkspaceProjectSourceContainers.java
new file mode 100644
index 0000000..3bfd7f9
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/WorkspaceProjectSourceContainers.java
@@ -0,0 +1,434 @@
+/*******************************************************************************
+ * Copyright (c) 2012-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.launching.sourcelookup.advanced;
+
+import static org.eclipse.jdt.core.IJavaElementDelta.F_ADDED_TO_CLASSPATH;
+import static org.eclipse.jdt.core.IJavaElementDelta.F_CLASSPATH_CHANGED;
+import static org.eclipse.jdt.core.IJavaElementDelta.F_CLOSED;
+import static org.eclipse.jdt.core.IJavaElementDelta.F_OPENED;
+import static org.eclipse.jdt.core.IJavaElementDelta.F_REMOVED_FROM_CLASSPATH;
+import static org.eclipse.jdt.core.IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.jdt.core.ElementChangedEvent;
+import org.eclipse.jdt.core.IElementChangedListener;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaElementDelta;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.FileHashing.Hasher;
+import org.eclipse.jdt.launching.sourcelookup.advanced.IWorkspaceProjectDescriber;
+import org.eclipse.jdt.launching.sourcelookup.advanced.IWorkspaceProjectDescriber.IJavaProjectSourceDescription;
+import org.eclipse.jdt.launching.sourcelookup.containers.PackageFragmentRootSourceContainer;
+
+/**
+ * Workspace project source container factory.
+ *
+ * <p>
+ * The factory creates both project and project classpath entry containers. Both projects and project classpath entries can be identified by their
+ * filesystem location and, if the location is a file, by the file SHA1 checksum.
+ *
+ * <p>
+ * The factory maintains up-to-date registry of workspace projects and their classpath entries and can be used to create source containers fast enough
+ * to be used from UI thread.
+ */
+public class WorkspaceProjectSourceContainers {
+	private final IElementChangedListener changeListener = new IElementChangedListener() {
+		@Override
+		public void elementChanged(ElementChangedEvent event) {
+			try {
+				final Set<IJavaProject> remove = new HashSet<>();
+				final Set<IJavaProject> add = new HashSet<>();
+
+				processDelta(event.getDelta(), remove, add);
+
+				if (!remove.isEmpty() || !add.isEmpty()) {
+					AdvancedSourceLookupSupport.schedule((m) -> updateProjects(remove, add, m));
+				}
+			}
+			catch (CoreException e) {
+				// maybe do something about it?
+			}
+		}
+
+		private void processDelta(final IJavaElementDelta delta, Set<IJavaProject> remove, Set<IJavaProject> add) throws CoreException {
+			// TODO review, this looks too complicated to add/remove java projects
+
+			final IJavaElement element = delta.getElement();
+			final int kind = delta.getKind();
+			switch (element.getElementType()) {
+				case IJavaElement.JAVA_MODEL:
+					processChangedChildren(delta, remove, add);
+					break;
+				case IJavaElement.JAVA_PROJECT:
+					switch (kind) {
+						case IJavaElementDelta.REMOVED:
+							remove.add((IJavaProject) element);
+							break;
+						case IJavaElementDelta.ADDED:
+							add.add((IJavaProject) element);
+							break;
+						case IJavaElementDelta.CHANGED:
+							if ((delta.getFlags() & F_CLOSED) != 0) {
+								remove.add((IJavaProject) element);
+							} else if ((delta.getFlags() & F_OPENED) != 0) {
+								add.add((IJavaProject) element);
+							} else if ((delta.getFlags() & (F_CLASSPATH_CHANGED | F_RESOLVED_CLASSPATH_CHANGED)) != 0) {
+								remove.add((IJavaProject) element);
+								add.add((IJavaProject) element);
+							}
+							break;
+					}
+					processChangedChildren(delta, remove, add);
+					break;
+				case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+					if ((delta.getFlags() & (F_ADDED_TO_CLASSPATH | F_REMOVED_FROM_CLASSPATH)) != 0) {
+						remove.add(element.getJavaProject());
+						add.add(element.getJavaProject());
+					}
+					break;
+			}
+		}
+
+		private void processChangedChildren(IJavaElementDelta delta, Set<IJavaProject> remove, Set<IJavaProject> add) throws CoreException {
+			for (IJavaElementDelta childDelta : delta.getAffectedChildren()) {
+				processDelta(childDelta, remove, add);
+			}
+		}
+	};
+
+	private static class JavaProjectDescriptionBuilder implements IJavaProjectSourceDescription {
+		final Set<File> locations = new HashSet<>();
+		final List<Supplier<ISourceContainer>> factories = new ArrayList<>();
+		final Map<File, IPackageFragmentRoot> dependencyLocations = new HashMap<>();
+
+		@Override
+		public void addLocation(File location) {
+			locations.add(location);
+		}
+
+		@Override
+		public void addSourceContainerFactory(Supplier<ISourceContainer> factory) {
+			factories.add(factory);
+		}
+
+		@Override
+		public void addDependencies(Map<File, IPackageFragmentRoot> dependencies) {
+			// TODO decide what happens if the same location is associated with multiple package fragment roots
+			this.dependencyLocations.putAll(dependencies);
+		}
+
+	}
+
+	private static class JavaProjectDescription {
+		final Set<File> classesLocations;
+
+		final Set<Object> classesLocationsHashes;
+
+		final List<Supplier<ISourceContainer>> sourceContainerFactories;
+
+		final Map<File, IPackageFragmentRoot> dependencies;
+
+		final Map<Object, IPackageFragmentRoot> dependencyHashes;
+
+		public JavaProjectDescription(Set<File> locations, Set<Object> hashes, List<Supplier<ISourceContainer>> factories, Map<File, IPackageFragmentRoot> dependencies, Map<Object, IPackageFragmentRoot> dependencyHashes) {
+			this.classesLocations = Collections.unmodifiableSet(locations);
+			this.classesLocationsHashes = Collections.unmodifiableSet(hashes);
+			this.sourceContainerFactories = Collections.unmodifiableList(factories);
+			this.dependencies = Collections.unmodifiableMap(dependencies);
+			this.dependencyHashes = Collections.unmodifiableMap(dependencyHashes);
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj) {
+				return true;
+			}
+			if (!(obj instanceof JavaProjectDescription)) {
+				return false;
+			}
+			JavaProjectDescription other = (JavaProjectDescription) obj;
+			return classesLocations.equals(other.classesLocations);
+		}
+
+		@Override
+		public int hashCode() {
+			return classesLocations.hashCode();
+		}
+	}
+
+	/**
+	 * Guards concurrent access to {@link #locations}, {@link #hashes} and {@link #projects}. Necessary because source lookup queries and java model
+	 * changes are processed on different threads.
+	 *
+	 * @TODO consider using ConcurrentMaps instead of explicit locking.
+	 */
+	private final Object lock = new Object() {
+	};
+
+	/**
+	 * Maps project classes location to project description.
+	 */
+	private final Map<File, JavaProjectDescription> locations = new HashMap<>();
+
+	/**
+	 * Maps project dependency hash to project descriptions. Hash-based source lookup is useful when runtime uses copies of jars used by the
+	 * workspace.
+	 */
+	private final Map<Object, Collection<JavaProjectDescription>> hashes = new HashMap<>();
+
+	/**
+	 * Maps java project to project description.
+	 */
+	private final Map<IJavaProject, JavaProjectDescription> projects = new HashMap<>();
+
+	/**
+	 * Creates and returns new source containers for the workspace project identified by the given location. Returns {@code null} if there is no such
+	 * workspace project.
+	 */
+	public ISourceContainer createProjectContainer(File projectLocation) {
+		Hasher hasher = FileHashing.hasher(); // use long-lived hasher
+
+		JavaProjectDescription description = getProjectByLocation(projectLocation);
+
+		if (description == null) {
+			Collection<JavaProjectDescription> desciptions = getProjectsByHash(projectLocation, hasher);
+			if (!desciptions.isEmpty()) {
+				// it is possible, but unlikely, to have multiple binary projects for the same jar
+				description = desciptions.iterator().next();
+			}
+		}
+
+		if (description == null) {
+			return null;
+		}
+
+		List<ISourceContainer> containers = new ArrayList<>();
+		for (Supplier<ISourceContainer> factory : description.sourceContainerFactories) {
+			containers.add(factory.get());
+		}
+
+		return CompositeSourceContainer.compose(containers);
+	}
+
+	private JavaProjectDescription getProjectByLocation(File projectLocation) {
+		synchronized (lock) {
+			return locations.get(projectLocation);
+		}
+	}
+
+	private Collection<JavaProjectDescription> getProjectsByHash(File projectLocation, FileHashing.Hasher hasher) {
+		Collection<JavaProjectDescription> projects;
+		synchronized (lock) {
+			projects = hashes.get(hasher.hash(projectLocation));
+			return projects != null ? new HashSet<>(projects) : Collections.emptySet();
+		}
+	}
+
+	/**
+	 * Creates and returns new source container for the workspace project classpath entry identified by the given project and entry locations. Returns
+	 * {@code null} if there is no such project classpath entry.
+	 */
+	public ISourceContainer createClasspathEntryContainer(File projectLocation, File entryLocation) {
+		Hasher hasher = FileHashing.hasher(); // use long-lived hasher
+
+		JavaProjectDescription projectByLocation = getProjectByLocation(projectLocation);
+
+		IPackageFragmentRoot dependency = getProjectDependency(projectByLocation, entryLocation, hasher);
+
+		if (dependency == null && projectByLocation == null) {
+			for (JavaProjectDescription projectByHash : getProjectsByHash(projectLocation, hasher)) {
+				dependency = getProjectDependency(projectByHash, entryLocation, hasher);
+				if (dependency != null) {
+					break;
+				}
+			}
+		}
+
+		if (dependency == null) {
+			return null;
+		}
+
+		return new PackageFragmentRootSourceContainer(dependency);
+	}
+
+	private IPackageFragmentRoot getProjectDependency(JavaProjectDescription project, File entryLocation, FileHashing.Hasher hasher) {
+		if (project == null) {
+			return null;
+		}
+
+		IPackageFragmentRoot dependency = project.dependencies.get(entryLocation);
+
+		if (dependency == null) {
+			dependency = project.dependencyHashes.get(hasher.hash(entryLocation));
+		}
+		return dependency;
+	}
+
+	public void initialize(IProgressMonitor monitor) throws CoreException {
+		// note that initialization and java element change events are processed by the same background job
+		// this guarantees the events aren't lost when they are delivered while the initialization is running
+		JavaCore.addElementChangedListener(changeListener);
+
+		final IJavaModel javaModel = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot());
+		final IJavaProject[] javaProjects = javaModel.getJavaProjects();
+
+		SubMonitor progress = SubMonitor.convert(monitor, javaProjects.length);
+
+		// TODO this can take significant time for large workspaces, consider running on multiple threads
+		// NB: can't persist state across restarts because java element change events are not delivered when this plugin isn't active
+
+		Hasher hasher = FileHashing.newHasher(); // short-lived hasher for bulk workspace indexing
+
+		List<IWorkspaceProjectDescriber> describers = getJavaProjectDescribers();
+		for (IJavaProject project : javaProjects) {
+			addJavaProject(project, describers, hasher, progress.split(1));
+		}
+	}
+
+	public void close() {
+		JavaCore.removeElementChangedListener(changeListener);
+		synchronized (lock) {
+			this.locations.clear();
+			this.hashes.clear();
+			this.projects.clear();
+		}
+	}
+
+	private void addJavaProject(IJavaProject project, List<IWorkspaceProjectDescriber> describers, FileHashing.Hasher hasher, IProgressMonitor monitor) throws CoreException {
+		if (project == null) {
+			throw new IllegalArgumentException();
+		}
+
+		JavaProjectDescriptionBuilder builder = new JavaProjectDescriptionBuilder();
+
+		for (IWorkspaceProjectDescriber describer : describers) {
+			describer.describeProject(project, builder);
+		}
+
+		Set<File> locations = builder.locations;
+		List<Supplier<ISourceContainer>> factories = builder.factories;
+		Map<File, IPackageFragmentRoot> dependencies = builder.dependencyLocations;
+
+		// make binary project support little easier to implement
+		locations.forEach(location -> dependencies.remove(location));
+
+		Set<Object> hashes = new HashSet<>();
+		locations.forEach(location -> {
+			Object hash = hasher.hash(location);
+			if (hash != null) {
+				hashes.add(hash);
+			}
+		});
+
+		Map<Object, IPackageFragmentRoot> dependencyHashes = new HashMap<>();
+		dependencies.forEach((location, packageFragmentRoot) -> dependencyHashes.put(hasher.hash(location), packageFragmentRoot));
+
+		JavaProjectDescription info = new JavaProjectDescription(locations, hashes, factories, dependencies, dependencyHashes);
+
+		synchronized (this.lock) {
+			for (File location : locations) {
+				this.locations.put(location, info);
+			}
+			for (Object hash : hashes) {
+				Collection<JavaProjectDescription> hashProjects = this.hashes.get(hash);
+				if (hashProjects == null) {
+					hashProjects = new HashSet<>();
+					this.hashes.put(hash, hashProjects);
+				}
+				hashProjects.add(info);
+			}
+			this.projects.put(project, info);
+		}
+
+		SubMonitor.done(monitor);
+	}
+
+	protected List<IWorkspaceProjectDescriber> getJavaProjectDescribers() {
+		List<IWorkspaceProjectDescriber> result = new ArrayList<>();
+
+		IExtensionRegistry registry = Platform.getExtensionRegistry();
+
+		IConfigurationElement[] elements = registry.getConfigurationElementsFor(AdvancedSourceLookupSupport.ID_workspaceProjectDescribers);
+
+		for (IConfigurationElement element : elements) {
+			if ("describer".equals(element.getName())) { //$NON-NLS-1$
+				try {
+					result.add((IWorkspaceProjectDescriber) element.createExecutableExtension("class")); //$NON-NLS-1$
+				}
+				catch (CoreException e) {
+				}
+			}
+		}
+
+		result.add(new DefaultProjectDescriber());
+
+		return result;
+	}
+
+	private void removeJavaProject(IJavaProject project) {
+		if (project == null) {
+			throw new IllegalArgumentException();
+		}
+		synchronized (lock) {
+			JavaProjectDescription description = projects.remove(project);
+			if (description != null) {
+				for (File location : description.classesLocations) {
+					locations.remove(location);
+				}
+				for (Object hash : description.classesLocationsHashes) {
+					Collection<JavaProjectDescription> hashProjects = hashes.get(hash);
+					if (hashProjects != null) {
+						hashProjects.remove(description);
+						if (hashProjects.isEmpty()) {
+							hashes.remove(hash);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	void updateProjects(final Set<IJavaProject> remove, final Set<IJavaProject> add, IProgressMonitor monitor) throws CoreException {
+		SubMonitor progress = SubMonitor.convert(monitor, 1 + add.size());
+
+		progress.split(1);
+		for (IJavaProject project : remove) {
+			removeJavaProject(project);
+		}
+		List<IWorkspaceProjectDescriber> describers = getJavaProjectDescribers();
+		Hasher hasher = FileHashing.newHasher();
+		for (IJavaProject project : add) {
+			addJavaProject(project, describers, hasher, progress.split(1));
+		}
+	}
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/messages.properties b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/messages.properties
new file mode 100644
index 0000000..fcbba1c
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/sourcelookup/advanced/messages.properties
@@ -0,0 +1,13 @@
+###############################################################################
+# Copyright (c) 2015-2016 Igor Fedorenko
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#      Igor Fedorenko - initial API and implementation
+###############################################################################
+
+BackgroundProcessingJob_name=Advanced source lookup job
+BackgroundProcessingJob_failed=Advanced source lookup task failed
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java
index 87c363c..eb6195f 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java
@@ -21,6 +21,7 @@
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IModuleDescription;
 import org.eclipse.jdt.internal.launching.LaunchingMessages;
+import org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedJavaLaunchDelegate;
 import org.eclipse.osgi.util.NLS;
 
 /**
@@ -28,6 +29,8 @@
  * <p>
  * Clients may subclass and instantiate this class.
  * </p>
+ * 
+ * @see AdvancedJavaLaunchDelegate
  * @since 3.1
  */
 public class JavaLaunchDelegate extends AbstractJavaLaunchConfigurationDelegate {
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedJavaLaunchDelegate.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedJavaLaunchDelegate.java
new file mode 100644
index 0000000..ead7a35
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedJavaLaunchDelegate.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.launching.sourcelookup.advanced;
+
+import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.createAdvancedLaunch;
+import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.getJavaagentString;
+import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.isAdvancedSourcelookupEnabled;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.jdt.launching.JavaLaunchDelegate;
+
+/**
+ * A launch delegate for launching local Java applications with advanced source lookup support.
+ *
+ * @since 3.10
+ * @provisional This is part of work in progress and can be changed, moved or removed without notice
+ */
+public class AdvancedJavaLaunchDelegate extends JavaLaunchDelegate {
+
+	@Override
+	public String getVMArguments(ILaunchConfiguration configuration) throws CoreException {
+		if (!isAdvancedSourcelookupEnabled()) {
+			return super.getVMArguments(configuration);
+		}
+		// TODO wish we had API similar to zt-exec or at least commons-exec
+		return getJavaagentString() + " " + super.getVMArguments(configuration); //$NON-NLS-1$
+	}
+
+	@Override
+	public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
+		if (!isAdvancedSourcelookupEnabled()) {
+			return super.getLaunch(configuration, mode);
+		}
+		return createAdvancedLaunch(configuration, mode);
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedSourceLookup.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedSourceLookup.java
new file mode 100644
index 0000000..f4325ba
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedSourceLookup.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.launching.sourcelookup.advanced;
+
+import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.getJavaagentLocation;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IPersistableSourceLocator;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.IJDIHelpers;
+
+/**
+ * Static methods for implementing advanced source lookup.
+ *
+ * @since 3.10
+ * @provisional This is part of work in progress and can be changed, moved or removed without notice
+ */
+public class AdvancedSourceLookup {
+	private static final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+
+	/**
+	 * Returns {@code true} if the given project has sources folders, {@code false} otherwise.
+	 */
+	public static boolean isSourceProject(IJavaProject project) throws JavaModelException {
+		for (IPackageFragmentRoot fragment : project.getPackageFragmentRoots()) {
+			if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public static Map<File, IPackageFragmentRoot> getClasspath(IJavaProject project) throws JavaModelException {
+		final Map<File, IPackageFragmentRoot> classpath = new LinkedHashMap<>();
+		for (IPackageFragmentRoot fragment : project.getPackageFragmentRoots()) {
+			if (fragment.getKind() == IPackageFragmentRoot.K_BINARY) {
+				File classpathLocation;
+				if (fragment.isExternal()) {
+					classpathLocation = fragment.getPath().toFile();
+				} else {
+					classpathLocation = fragment.getResource().getLocation().toFile();
+				}
+				classpath.put(classpathLocation, fragment);
+			}
+		}
+		return classpath;
+	}
+
+	public static Set<File> getOutputDirectories(IJavaProject project) throws JavaModelException {
+		final Set<File> locations = new LinkedHashSet<>();
+		addWorkspaceLocation(locations, project.getOutputLocation());
+		for (IClasspathEntry cpe : project.getRawClasspath()) {
+			if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE && cpe.getOutputLocation() != null) {
+				addWorkspaceLocation(locations, cpe.getOutputLocation());
+			}
+		}
+		return locations;
+	}
+
+	private static void addWorkspaceLocation(Collection<File> locations, IPath workspacePath) {
+		IResource resource = root.findMember(workspacePath);
+		if (resource != null) {
+			locations.add(resource.getLocation().toFile());
+		}
+	}
+
+	/**
+	 * Returns {@code -javaagent} jvm launch argument.
+	 */
+	public static String getJavaagentString() {
+		return "-javaagent:" + getJavaagentLocation(); //$NON-NLS-1$
+	}
+
+	/**
+	 * Returns filesystem classes location that corresponds to the given debug element, or {@code null} if the location cannot be determined.
+	 */
+	public static File getClassesLocation(Object fElement) throws DebugException {
+		return IJDIHelpers.INSTANCE.getClassesLocation(fElement);
+	}
+
+	/**
+	 * Creates and returns new {@link IPersistableSourceLocator} of the specified type and with the provided configuration.
+	 */
+	public static IPersistableSourceLocator createSourceLocator(String type, ILaunchConfiguration configuration) throws CoreException {
+		return AdvancedSourceLookupSupport.createSourceLocator(type, configuration);
+	}
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedSourceLookupParticipant.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedSourceLookupParticipant.java
new file mode 100644
index 0000000..59a7cc1
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/AdvancedSourceLookupParticipant.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.launching.sourcelookup.advanced;
+
+import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.getContextMonitor;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.model.DebugElement;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.CompositeSourceContainer;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.IJDIHelpers;
+import org.eclipse.jdt.internal.launching.sourcelookup.advanced.WorkspaceProjectSourceContainers;
+
+/**
+ * @since 3.10
+ * @provisional This is part of work in progress and can be changed, moved or removed without notice
+ */
+public class AdvancedSourceLookupParticipant implements ISourceLookupParticipant {
+
+	private final IJDIHelpers jdi;
+
+	private ISourceLookupDirector director;
+
+	private final Map<File, ISourceContainer> containers = new HashMap<>();
+
+	public AdvancedSourceLookupParticipant() {
+		this(IJDIHelpers.INSTANCE);
+	}
+
+	/**
+	 * @noreference this constructor is visible for test purposes only
+	 */
+	public AdvancedSourceLookupParticipant(IJDIHelpers jdi) {
+		this.jdi = jdi;
+	}
+
+	@Override
+	public void init(ISourceLookupDirector director) {
+		this.director = director;
+	}
+
+	@Override
+	public Object[] findSourceElements(Object element) throws CoreException {
+		ISourceContainer container = getSourceContainer(element, false /* don't refresh cache */, null /* async */ );
+
+		if (container == null) {
+			return null;
+		}
+
+		String sourcePath = jdi.getSourcePath(element);
+		if (sourcePath == null) {
+			// can't really happen
+			return null;
+		}
+
+		return container.findSourceElements(sourcePath);
+	}
+
+	public ISourceContainer getSourceContainer(Object element, boolean refresh, IProgressMonitor monitor) throws CoreException {
+		File location = jdi.getClassesLocation(element);
+
+		if (location == null) {
+			return null;
+		}
+
+		synchronized (containers) {
+			if (!refresh && containers.containsKey(location)) {
+				return containers.get(location);
+			}
+		}
+
+		monitor = getContextMonitor(monitor);
+
+		WorkspaceProjectSourceContainers projectLocator = AdvancedSourceLookupSupport.getWorkspaceJavaProjects(monitor);
+		if (monitor == null && projectLocator == null) {
+			// reschedule to initialize and resolve sources in background
+			AdvancedSourceLookupSupport.schedule((m) -> getSourceContainer(element, refresh, m));
+			return null;
+		}
+
+		if (projectLocator == null) {
+			// we get here when projectLocator initialization has been cancelled by the user
+			return null;
+		}
+
+		//
+		// lookup strategies that provide java project context necessary for debug expression evaluation
+		//
+
+		// workspace project identified by their runtime classes location is the preferred sources container
+		ISourceContainer projectContainer = projectLocator.createProjectContainer(location);
+		if (projectContainer != null) {
+			return cacheContainer(element, location, projectContainer);
+		}
+
+		// dependency of one of workspace projects on the call stack also provides java project context
+		for (File frameLocation : jdi.getStackFramesClassesLocations(element)) {
+			ISourceContainer entryContainer = projectLocator.createClasspathEntryContainer(frameLocation, location);
+			if (entryContainer != null) {
+				return cacheContainer(element, location, entryContainer);
+			}
+		}
+
+		if (monitor == null) {
+			// reschedule to resolve sources in background
+			AdvancedSourceLookupSupport.schedule((m) -> getSourceContainer(element, refresh, m));
+			return null;
+		}
+
+		//
+		// strategies that allow source code lookup but do not provide java project context
+		//
+
+		// checksum-based lookup in various sources (central, nexus, pde target platform, p2 repositories)
+		for (ISourceContainerResolver repository : getSourceContainerResolvers()) {
+			Collection<ISourceContainer> members = repository.resolveSourceContainers(location, monitor);
+			if (members != null && !members.isEmpty()) {
+				return cacheContainer(element, location, CompositeSourceContainer.compose(members));
+			}
+		}
+
+		return null;
+	}
+
+	private ISourceContainer cacheContainer(Object element, File location, ISourceContainer container) {
+		ISourceContainer oldContainer;
+		synchronized (containers) {
+			oldContainer = containers.put(location, container);
+			if (oldContainer != null) {
+				oldContainer.dispose();
+			}
+		}
+		if (oldContainer != null || container != null) {
+			updateDebugElement(element);
+		}
+		return container;
+	}
+
+	protected Collection<ISourceContainerResolver> getSourceContainerResolvers() {
+		List<ISourceContainerResolver> result = new ArrayList<>();
+
+		IExtensionRegistry registry = Platform.getExtensionRegistry();
+
+		IConfigurationElement[] elements = registry.getConfigurationElementsFor(AdvancedSourceLookupSupport.ID_sourceContainerResolvers);
+
+		for (IConfigurationElement element : elements) {
+			if ("resolver".equals(element.getName())) { //$NON-NLS-1$
+				try {
+					result.add((ISourceContainerResolver) element.createExecutableExtension("class")); //$NON-NLS-1$
+				}
+				catch (CoreException e) {
+				}
+			}
+		}
+
+		return result;
+	}
+
+	@Override
+	public String getSourceName(Object object) throws CoreException {
+		return null; // default name->source mapping
+	}
+
+	@Override
+	public void dispose() {
+		disposeContainers();
+	}
+
+	@Override
+	public void sourceContainersChanged(ISourceLookupDirector director) {
+		disposeContainers();
+	}
+
+	protected void disposeContainers() {
+		synchronized (containers) {
+			for (ISourceContainer container : containers.values()) {
+				if (container != null) // possible for non-maven jars
+				{
+					container.dispose();
+				}
+			}
+			containers.clear();
+		}
+	}
+
+	private void updateDebugElement(Object element) {
+		director.clearSourceElements(element);
+		if (element instanceof DebugElement) {
+			// this is apparently needed to flush StackFrameSourceDisplayAdapter cache
+			((DebugElement) element).fireChangeEvent(DebugEvent.CONTENT);
+		}
+	}
+
+	public static AdvancedSourceLookupParticipant getSourceLookup(Object element) {
+		ISourceLocator sourceLocator = null;
+		if (element instanceof IDebugElement) {
+			sourceLocator = ((IDebugElement) element).getLaunch().getSourceLocator();
+		}
+
+		AdvancedSourceLookupParticipant sourceLookup = null;
+		if (sourceLocator instanceof ISourceLookupDirector) {
+			for (ISourceLookupParticipant participant : ((ISourceLookupDirector) sourceLocator).getParticipants()) {
+				if (participant instanceof AdvancedSourceLookupParticipant) {
+					sourceLookup = (AdvancedSourceLookupParticipant) participant;
+					break;
+				}
+			}
+		}
+		return sourceLookup;
+	}
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/ISourceContainerResolver.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/ISourceContainerResolver.java
new file mode 100644
index 0000000..28f11a2
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/ISourceContainerResolver.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.launching.sourcelookup.advanced;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+
+/**
+ * Implementations of this interface identify runtime classes locations and download (or "resolve") corresponding sources from external sources
+ * repositories like Maven or P2 artifact repositories and PDE target platform. Implementations are registered with the advanced source lookup
+ * framework using {@code org.eclipse.jdt.launching.sourceContainerResolvers} extension point.
+ *
+ * @since 3.10
+ * @provisional This is part of work in progress and can be changed, moved or removed without notice
+ */
+public interface ISourceContainerResolver {
+
+	/**
+	 * Creates and returns a collection of source containers that correspond to the given filesystem classes location. Returns {@code null} or an
+	 * empty collection if requested sources cannot be located.
+	 */
+	public Collection<ISourceContainer> resolveSourceContainers(File classesLocation, IProgressMonitor monitor) throws CoreException;
+
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/IWorkspaceProjectDescriber.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/IWorkspaceProjectDescriber.java
new file mode 100644
index 0000000..7d8b2c0
--- /dev/null
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/sourcelookup/advanced/IWorkspaceProjectDescriber.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 Igor Fedorenko
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.launching.sourcelookup.advanced;
+
+import java.io.File;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+
+/**
+ * Implementations of this interface describe workspace projects for the purpose of source lookup. Implementations are registered with the advanced
+ * source lookup framework using {@code org.eclipse.jdt.launching.workspaceProjectDescribers} extension point.
+ *
+ * Workspace project runtime classes location are used to identify projects when performing source code lookup. Depending on project type and
+ * application classloading mechanism used by the runtime, classes location can be project output folders, one of project classpath entries (for PDE
+ * Binary Plug-In projects, for example) or some other runtime-specific location, like OSGi bundle installation location.
+ *
+ * The same workspace project can have different classes location if it is used with different runtime technologies. For example, PDE Plug-In project
+ * classes will have bundle installation location when used by Equinox framework and project output folder when used by standard java application.
+ * Note that different runtime technologies can coexist within the same running JVM, like it is the case with Tycho build, where Equinox, Maven and
+ * standard Java APIs are used side-by-side. For this reason multiple project describers can provide information about the same project and all
+ * projects descriptions will be considered when performing source lookup.
+ *
+ * @since 3.10
+ * @provisional This is part of work in progress and can be changed, moved or removed without notice
+ */
+public interface IWorkspaceProjectDescriber {
+
+	public static interface IJavaProjectSourceDescription {
+		/**
+		 * Adds filesystem classes directories or jar files as reported by the runtime for project classes.
+		 *
+		 * Some common examples:
+		 * <ul>
+		 * <li>for standard java projects this is project output locations
+		 * <li>for M2E Binary Maven projects, this is one of project claspath entries
+		 * <li>for PDE Plug-In projects used by Equinox, this is project bundle installation location
+		 * <li>for PDE Plug-In projects used plain java, this is still project output locations
+		 * </ul>
+		 *
+		 */
+		public void addLocation(File location);
+
+		/**
+		 * Adds factory of source container(s) for the project itself, typically:
+		 * <ul>
+		 * <li>JavaProjectSourceContainer for normal projects with sources folders
+		 * <li>PackageFragmentRootSourceContainer for PDE and M2E binary projects
+		 * </ul>
+		 *
+		 * In some cases one project will have multiple associated source container. For example, Binary Maven project that represents an "uber" jar
+		 * will have a source container factory for each jar included in the uber jar.
+		 */
+		public void addSourceContainerFactory(Supplier<ISourceContainer> factory);
+
+		/**
+		 * Adds runtime classes location of project dependencies and their corresponding package fragment roots, typically
+		 * <ul>
+		 * <li>for standard java application, this is dependency jar or classes directory
+		 * <li>for equinox, this is dependency bundle location
+		 * </ul>
+		 */
+		public void addDependencies(Map<File, IPackageFragmentRoot> dependencies);
+	}
+
+	/**
+	 * Populate the given description with the given project's description.
+	 */
+	public void describeProject(IJavaProject project, IJavaProjectSourceDescription description) throws CoreException;
+
+}
diff --git a/org.eclipse.jdt.launching/lib/javaagent-shaded.jar b/org.eclipse.jdt.launching/lib/javaagent-shaded.jar
new file mode 100644
index 0000000..ec489cc
--- /dev/null
+++ b/org.eclipse.jdt.launching/lib/javaagent-shaded.jar
Binary files differ
diff --git a/org.eclipse.jdt.launching/plugin.properties b/org.eclipse.jdt.launching/plugin.properties
index 792acb3..f4c43bd 100644
--- a/org.eclipse.jdt.launching/plugin.properties
+++ b/org.eclipse.jdt.launching/plugin.properties
@@ -11,10 +11,14 @@
 
 localJavaApplication = Java Application
 eclipseJDTLauncher.name=Eclipse JDT Launcher
+localAdvancedJavaApplication.name=Eclipse JDT Advanced
 localJavaApplicationDelegate.description=The Eclipse JDT Java Application Launcher supports running and debugging local Java applications.
+localAdvancedJavaApplicationDelegate.description=The Eclipse JDT Advanced Java Application Launcher supports running and debugging local Java applications.
 pluginName = Java Development Tools Launching Support
 remoteJavaApplication = Remote Java Application
+remoteAdvancedJavaApplication.name = Advanced Remote Java Application
 remoteJavaApplicationDelegate.description=The Eclipse JDT Java Remote Launcher supports attaching to and debugging remote Java applications.
+remoteAdvancedJavaApplicationDelegate.description=The Eclipse JDT Java Remote Launcher with advanced source code lookup.
 vmConnectors = VM Connectors
 vmInstallTypes = VM Install Types
 vmInstalls = VM Installs
@@ -24,6 +28,8 @@
 javaSourceLocatorName = Basic Java Source Locator
 executionEnvironments = Execution Environments
 libraryLocationResolvers = Library Location Resolvers
+projectSourceDescribers = Project Source Describers
+sourceContainerResolvers = Source Container Resolvers
 
 providerName=Eclipse.org
 
@@ -33,6 +39,7 @@
 javaAppletDelegate.description=The Eclipse JDT Java Applet Launcher supports running and debugging Java applets.
 
 javaSourceLookupDirector.name= Java Source Locator
+javaAdvancedSourceLookupDirector.name= Java Advanced Source Locator
 
 launchingTrace.name = JDT Launching
 
@@ -64,3 +71,4 @@
 
 ee.home.description = Returns the absolute file system path of the JRE home directory corresponding to the specified execution environment. An execution environment identifier must be provided as an argument.
 project.classpath.description = Returns the resolved class path of a project. A project name can be provided as an argument. If no argument is provided, the target project is the project of the selected resource.
+sourcelookup.javaagent.description = Returns absolute filesystem path of source lookup java agent.
\ No newline at end of file
diff --git a/org.eclipse.jdt.launching/plugin.xml b/org.eclipse.jdt.launching/plugin.xml
index 13eec51..c519538 100644
--- a/org.eclipse.jdt.launching/plugin.xml
+++ b/org.eclipse.jdt.launching/plugin.xml
@@ -23,6 +23,8 @@
    <extension-point id="executionEnvironments" name="%executionEnvironments" schema="schema/executionEnvironments.exsd"/>
    <extension-point id="vmInstalls" name="%vmInstalls" schema="schema/vmInstalls.exsd"/>
    <extension-point id="libraryLocationResolvers" name="%libraryLocationResolvers" schema="schema/libraryLocationResolvers.exsd"/>
+   <extension-point id="workspaceProjectDescribers" name="%projectSourceDescribers" schema="schema/workspaceProjectDescribers.exsd"/>
+   <extension-point id="sourceContainerResolvers" name="%sourceContainerResolvers" schema="schema/sourceContainerResolvers.exsd"/>
 
 <!-- Extensions -->
    <extension
@@ -43,7 +45,7 @@
    <extension
          point="org.eclipse.debug.core.launchConfigurationTypes">
       <launchConfigurationType
-            delegate="org.eclipse.jdt.launching.JavaLaunchDelegate"
+            delegate="org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedJavaLaunchDelegate"
             delegateDescription="%localJavaApplicationDelegate.description"
             delegateName="%eclipseJDTLauncher.name"
             id="org.eclipse.jdt.launching.localJavaApplication"
@@ -54,7 +56,7 @@
             sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
       </launchConfigurationType>
       <launchConfigurationType
-            delegate="org.eclipse.jdt.internal.launching.JavaRemoteApplicationLaunchConfigurationDelegate"
+            delegate="org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedRemoteJavaLaunchDelegate"
             delegateDescription="%remoteJavaApplicationDelegate.description"
             delegateName="%eclipseJDTLauncher.name"
             id="org.eclipse.jdt.launching.remoteJavaApplication"
@@ -201,6 +203,11 @@
    			class="org.eclipse.jdt.internal.launching.JavaSourceLookupDirector"
    			name="%javaSourceLookupDirector.name">
    		</sourceLocator>
+   		<sourceLocator
+   			id="org.eclipse.jdt.launching.sourceLocator.JavaAdvancedSourceLookupDirector"
+   			class="org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupDirector"
+   			name="%javaAdvancedSourceLookupDirector.name">
+   		</sourceLocator>
    </extension>
    <extension
          point="org.eclipse.core.runtime.preferences">
@@ -229,6 +236,12 @@
             namespace="org.eclipse.jdt.launching"
             properties="hasMain, extendsClass, extendsInterface, hasMethod, isContainer, hasProjectNature, buildpathReference, hasMethodWithAnnotation, hasTypeWithAnnotation, isPackageFragment, isPackageFragmentRoot"
             type="org.eclipse.core.runtime.IAdaptable"/>            
+      <propertyTester
+            class="org.eclipse.jdt.internal.launching.sourcelookup.advanced.JDIStratumPropertyTester"
+            id="org.eclipse.jdt.internal.launching.sourcelookup.advanced.jdtStratumTest"
+            namespace="org.eclipse.jdt.launching"
+            properties="jdtstratum"
+            type="org.eclipse.debug.core.model.DebugElement"/>
    </extension>
    <extension
          id="jreContainerMarker"
@@ -329,6 +342,12 @@
                name="project_classpath"
                resolver="org.eclipse.jdt.internal.launching.ProjectClasspathVariableResolver">
          </variable>
+         <variable
+               description="%sourcelookup.javaagent.description"
+               name="sourcelookup_javaagent"
+               resolver="org.eclipse.jdt.internal.launching.sourcelookup.advanced.JavaagentVariableResolver"
+               supportsArgument="false">
+         </variable>
       </extension>
       <extension
             point="org.eclipse.jdt.launching.libraryLocationResolvers">
diff --git a/org.eclipse.jdt.launching/schema/sourceContainerResolvers.exsd b/org.eclipse.jdt.launching/schema/sourceContainerResolvers.exsd
new file mode 100644
index 0000000..2d26f58
--- /dev/null
+++ b/org.eclipse.jdt.launching/schema/sourceContainerResolvers.exsd
@@ -0,0 +1,90 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.jdt.launching" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.jdt.launching" id="sourceContainerResolvers" name="Source Container Resolvers"/>
+      </appInfo>
+      <documentation>
+         This extension point allows clients to identify and download sources jar artifacts from external artifact repositories.
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="resolver" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="resolver">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.jdt.launching.sourcelookup.advanced.ISourceContainerResolver"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         3.9
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2011-2016 Igor Fedorenko
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+
+Contributors:
+     Igor Fedorenko - initial API and implementation
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/org.eclipse.jdt.launching/schema/workspaceProjectDescribers.exsd b/org.eclipse.jdt.launching/schema/workspaceProjectDescribers.exsd
new file mode 100644
index 0000000..88abadb
--- /dev/null
+++ b/org.eclipse.jdt.launching/schema/workspaceProjectDescribers.exsd
@@ -0,0 +1,101 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.jdt.launching" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.jdt.launching" id="workspaceProjectDescribers" name="Workspace Project Describers"/>
+      </appInfo>
+      <documentation>
+         This extension point allows clients to support advanced source lookup functionality for workspace Java projects with non-default layout, e.g. &quot;binary-only&quot; projects, projects with custom classpath container, like PDE Plugin-In or Maven projects, and so on.
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="describer" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="describer">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.jdt.launching.sourcelookup.advanced.IWorkspaceProjectDescriber"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         3.9
+      </documentation>
+   </annotation>
+
+
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         Implementation is provided for the standard Eclipse JDT java project type.
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2011-2016 Igor Fedorenko
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+
+Contributors:
+     Igor Fedorenko - initial API and implementation
+      </documentation>
+   </annotation>
+
+</schema>