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 ¬ 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. "binary-only" 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>