Bug 486264 - [1.8] Entry breakpoint for lambda expression

Change-Id: I958f033a470598bf0da911c016cbbe8f191fbe47
Signed-off-by: Gayan Perera <gayanper@gmail.com>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/151461
Tested-by: Sarika Sinha <sarika.sinha@in.ibm.com>
Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
diff --git a/org.eclipse.jdt.debug.ui/plugin.properties b/org.eclipse.jdt.debug.ui/plugin.properties
index 613c24c..cdb3237 100644
--- a/org.eclipse.jdt.debug.ui/plugin.properties
+++ b/org.eclipse.jdt.debug.ui/plugin.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2000, 2021 IBM Corporation and others.
+# Copyright (c) 2000, 2022 IBM Corporation and others.
 #
 # This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License 2.0
@@ -18,6 +18,7 @@
 vmCapabilitiesPageName=VM Capabilities
 AddBreakpoint.label=Toggle &Breakpoint
 AddTracepoint.label=Toggle &Tracepoint
+AddLambdaEntryBreakpoint.label=Toggle &Lambda Entry Breakpoint
 
 addTypeStepFilterAction.label=&Filter Type
 addTypeStepFilterAction.tooltip=Filter the selected type(s)
@@ -236,7 +237,11 @@
 
 ToggleTracepointAction.label=Toggle Tra&cepoint
 ToggleTracepointCommand.label=Toggle Tracepoint
-ToggleTracepointCommand.description=Creates or removes a tracepoint  
+ToggleTracepointCommand.description=Creates or removes a tracepoint
+
+ToggleLambdaEntryBreakpointAction.label=Toggle Lambda Entry Breakpoint
+ToggleLambdaEntryBreakpointCommand.label=Toggle &Lambda Entry Breakpoint
+ToggleLambdaEntryBreakpointCommand.description=Creates or removes a lambda entry breakpoint  
 
 AddBookmark.label=Add Boo&kmark...
 AddBookmark.tooltip=Add Bookmark...
diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml
index d6ae998..6b6594a 100644
--- a/org.eclipse.jdt.debug.ui/plugin.xml
+++ b/org.eclipse.jdt.debug.ui/plugin.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?eclipse version="3.0"?>
 <!--
-     Copyright (c) 2005, 2021 IBM Corporation and others.
+     Copyright (c) 2005, 2022 IBM Corporation and others.
 
      This program and the accompanying materials
      are made available under the terms of the Eclipse Public License 2.0
@@ -160,6 +160,16 @@
                id="org.eclipse.jdt.debug.ui.actions.ToggleTracepoint">
          </action>
          <action
+               definitionId="org.eclipse.jdt.debug.ui.commands.ToggleLambdaEntryBreakpoint"
+               label="%ToggleLambdaEntryBreakpointAction.label"
+               icon="icons/full/obj16/brkp_obj.png"
+               disabledIcon="icons/full/obj16/brkpd_obj.png"
+               helpContextId="toggle_tracepoint_action_context"
+               class="org.eclipse.jdt.internal.debug.ui.actions.RetargetToggleLambdaEntryBreakpointAction"
+               menubarPath="org.eclipse.ui.run/lineBreakpointBeforeGroup"
+               id="org.eclipse.jdt.debug.ui.actions.ToggleLambdaEntryBreakpoint">
+         </action>
+         <action
 	           definitionId="org.eclipse.jdt.debug.ui.commands.AddClassPrepareBreakpoint"
                label="%classPrepareAction.label"
                icon="$nl$/icons/full/obj16/class_obj.png"
@@ -1006,6 +1016,15 @@
                menubarPath="debug">
          </action>
          <action
+               class="org.eclipse.jdt.internal.debug.ui.actions.RulerToggleLambdaEntryBreakpointActionDelegate"
+               helpContextId="manage_breakpoint_action_context"
+               icon="icons/full/obj16/brkp_obj.png"
+               id="org.eclipse.jdt.debug.ui.actions.RulerToggleLambdaEntryBreakpointAction"
+               definitionId="org.eclipse.jdt.debug.ui.commands.ToggleLambdaEntryBreakpoint"
+               label="%AddLambdaEntryBreakpoint.label"
+               menubarPath="debug">
+         </action>
+         <action
                label="%EnableBreakpoint.label"
                helpContextId="enable_disable_breakpoint_action_context"
                class="org.eclipse.debug.ui.actions.RulerEnableDisableBreakpointActionDelegate"
@@ -1050,6 +1069,15 @@
                menubarPath="debug">
          </action>
          <action
+               class="org.eclipse.jdt.internal.debug.ui.actions.RulerToggleLambdaEntryBreakpointActionDelegate"
+               helpContextId="manage_breakpoint_action_context"
+               icon="icons/full/obj16/brkp_obj.png"
+               id="org.eclipse.jdt.debug.ui.actions.RulerToggleLambdaEntryBreakpointAction"
+               definitionId="org.eclipse.jdt.debug.ui.commands.ToggleLambdaEntryBreakpoint"
+               label="%AddLambdaEntryBreakpoint.label"
+               menubarPath="debug">
+         </action>
+         <action
                label="%EnableBreakpoint.label"
                helpContextId="enable_disable_breakpoint_action_context"
                class="org.eclipse.debug.ui.actions.RulerEnableDisableBreakpointActionDelegate"
@@ -2688,6 +2716,12 @@
             id="org.eclipse.jdt.debug.ui.commands.ToggleTracepoint">
       </command>
       <command
+            name="%ToggleLambdaEntryBreakpointCommand.label"
+            description="%ToggleLambdaEntryBreakpointCommand.description"
+            categoryId="org.eclipse.debug.ui.category.run"
+            id="org.eclipse.jdt.debug.ui.commands.ToggleLambdaEntryBreakpoint">
+      </command>
+      <command
             name="%ActionDefinition.addException.name"
             description="%ActionDefinition.addException.description"
             categoryId="org.eclipse.debug.ui.category.run"
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/BreakpointMarkerUpdater.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/BreakpointMarkerUpdater.java
index 53a7b46..1bf6647 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/BreakpointMarkerUpdater.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/BreakpointMarkerUpdater.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2015 IBM Corporation and others.
+ * Copyright (c) 2006, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -25,6 +25,7 @@
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
 import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaMethodBreakpoint;
 import org.eclipse.jdt.debug.core.IJavaPatternBreakpoint;
 import org.eclipse.jdt.debug.core.IJavaStratumLineBreakpoint;
 import org.eclipse.jdt.debug.core.IJavaWatchpoint;
@@ -67,6 +68,7 @@
  *
  * @since 3.3
  */
+@SuppressWarnings("deprecation")
 public class BreakpointMarkerUpdater implements IMarkerUpdater {
 
 	public BreakpointMarkerUpdater() {}
@@ -113,7 +115,22 @@
 			return false;
 		}
 		try {
-			ValidBreakpointLocationLocator loc = new ValidBreakpointLocationLocator(unit, document.getLineOfOffset(position.getOffset())+1, true, true);
+			ValidBreakpointLocationLocator loc;
+			if (breakpoint instanceof IJavaMethodBreakpoint && ((IJavaMethodBreakpoint) breakpoint).isEntry()) {
+				IMarker m = breakpoint.getMarker();
+				if (m != null) {
+					int charStart = m.getAttribute(IMarker.CHAR_START, -1);
+					int charEnd = m.getAttribute(IMarker.CHAR_END, -1);
+					int length = charEnd - charStart + 1;
+					loc = new ValidBreakpointLocationLocator(unit, document.getLineOfOffset(position.getOffset())
+							+ 1, true, true, position.getOffset(), length);
+
+				} else {
+					loc = new ValidBreakpointLocationLocator(unit, document.getLineOfOffset(position.getOffset()) + 1, true, true);
+				}
+			} else {
+				loc = new ValidBreakpointLocationLocator(unit, document.getLineOfOffset(position.getOffset()) + 1, true, true);
+			}
 			unit.accept(loc);
 			if(loc.getLocationType() == ValidBreakpointLocationLocator.LOCATION_NOT_FOUND) {
 				return false;
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.java
index 351f3c9..61fe1da 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2021 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -168,6 +168,8 @@
 
 	public static String TracepointToggleAction_Unavailable;
 
+	public static String LambdaEntryBreakpointToggleAction_Unavailable;
+
 	public static String Override_Dependencies_title;
 	public static String Override_Dependencies_button;
 	public static String Override_Dependencies_button1;
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.properties
index 425b5f7..d1ad471 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.properties
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ActionMessages.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2000, 2021 IBM Corporation and others.
+# Copyright (c) 2000, 2022 IBM Corporation and others.
 #
 # This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License 2.0
@@ -149,6 +149,7 @@
 EditClasspathEntryAction_0=Ed&it...
 ProjectSelectionDialog_0=Choose &project(s) to add:
 TracepointToggleAction_Unavailable=The operation is unavailable on the current selection.
+LambdaEntryBreakpointToggleAction_Unavailable=The operation is unavailable on the current selection.
 Override_Dependencies_title=Override Dependencies
 Override_Dependencies_button=&Override
 Override_Dependencies_button1=&Override Dependencies...
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/BreakpointToggleUtils.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/BreakpointToggleUtils.java
index eeb3d97..903d26e 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/BreakpointToggleUtils.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/BreakpointToggleUtils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016, 2017 IBM Corporation and others.
+ * Copyright (c) 2016, 2022 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -25,6 +25,7 @@
 public class BreakpointToggleUtils {
 
 	private static boolean isTracepoint = false;
+	private static boolean isLambdaEntryBreakpoint = false;
 
 
 	public static void setUnsetTracepoints(boolean tracePoint) {
@@ -35,6 +36,14 @@
 		return isTracepoint;
 	}
 
+	public static void setUnsetLambdaEntryBreakpoint(boolean lambdaEntryBreakpoint) {
+		isLambdaEntryBreakpoint = lambdaEntryBreakpoint;
+	}
+
+	public static boolean isToggleLambdaEntryBreakpoint() {
+		return isLambdaEntryBreakpoint;
+	}
+
 	/**
 	 * Convenience method for printing messages to the status line
 	 *
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/RetargetToggleLambdaEntryBreakpointAction.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/RetargetToggleLambdaEntryBreakpointAction.java
new file mode 100644
index 0000000..e24b19a
--- /dev/null
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/RetargetToggleLambdaEntryBreakpointAction.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 20227 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.ui.actions;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.internal.ui.actions.breakpoints.RetargetToggleBreakpointAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Global retargettable toggle Line Entry action.
+ *
+ * @since 3.12
+ *
+ */
+public class RetargetToggleLambdaEntryBreakpointAction extends RetargetToggleBreakpointAction {
+
+	@Override
+	protected void performAction(Object target, ISelection selection, IWorkbenchPart part) throws CoreException {
+		BreakpointToggleUtils.setUnsetLambdaEntryBreakpoint(true);
+		super.performAction(target, selection, part);
+	}
+
+	@Override
+	protected boolean canPerformAction(Object target, ISelection selection, IWorkbenchPart part) {
+		return super.canPerformAction(target, selection, part);
+	}
+
+	@Override
+	protected String getOperationUnavailableMessage() {
+		return ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/RulerToggleLambdaEntryBreakpointActionDelegate.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/RulerToggleLambdaEntryBreakpointActionDelegate.java
new file mode 100644
index 0000000..df131df
--- /dev/null
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/RulerToggleLambdaEntryBreakpointActionDelegate.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2022 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.ui.actions;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.internal.ui.DebugUIPlugin;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.text.source.IVerticalRulerInfo;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.IActionDelegate2;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.texteditor.AbstractRulerActionDelegate;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+public class RulerToggleLambdaEntryBreakpointActionDelegate extends AbstractRulerActionDelegate implements IActionDelegate2 {
+
+	private IEditorPart currentEditor;
+	private IAction dummyAction;
+
+	@Override
+	protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {
+		dummyAction = new Action() {
+			// empty implementation to make compiler happy
+		};
+		return dummyAction;
+	}
+
+	@Override
+	public void setActiveEditor(IAction callerAction, IEditorPart targetEditor) {
+		currentEditor = targetEditor;
+	}
+
+	@Override
+	public void init(IAction action) {
+	}
+
+	@Override
+	public void dispose() {
+		currentEditor = null;
+		dummyAction = null;
+		super.dispose();
+	}
+
+	@Override
+	public void runWithEvent(IAction action, Event event) {
+		if (!(currentEditor instanceof ITextEditor)) {
+			return;
+		}
+		IVerticalRulerInfo rulerInfo = currentEditor.getAdapter(IVerticalRulerInfo.class);
+		if (rulerInfo == null) {
+			return;
+		}
+		int lineOfLastMouseButtonActivity = rulerInfo.getLineOfLastMouseButtonActivity();
+		if (lineOfLastMouseButtonActivity < 0) {
+			return;
+		}
+		IDocument document = getDocument((ITextEditor) currentEditor);
+		if (document == null) {
+			return;
+		}
+		ToggleBreakpointAdapter toggle = new ToggleBreakpointAdapter();
+		try {
+			ITextSelection selection = getTextSelection(currentEditor, document, lineOfLastMouseButtonActivity);
+			if (toggle.canToggleLineBreakpoints(currentEditor, selection)) {
+				BreakpointToggleUtils.setUnsetLambdaEntryBreakpoint(true);
+				toggle.toggleBreakpoints(currentEditor, selection);
+			}
+		} catch (BadLocationException | CoreException e) {
+			DebugUIPlugin.log(e);
+		}
+	}
+
+	private static IDocument getDocument(ITextEditor editor) {
+		IDocumentProvider provider = editor.getDocumentProvider();
+		if (provider != null) {
+			return provider.getDocument(editor.getEditorInput());
+		}
+		IDocument doc = editor.getAdapter(IDocument.class);
+		if (doc != null) {
+			return doc;
+		}
+		return null;
+	}
+
+	private static ITextSelection getTextSelection(IEditorPart editor, IDocument document, int line) throws BadLocationException {
+		IRegion region = document.getLineInformation(line);
+		ITextSelection textSelection = new TextSelection(document, region.getOffset(), 0);
+		ISelectionProvider provider = editor.getSite().getSelectionProvider();
+		if (provider != null) {
+			ISelection selection = provider.getSelection();
+			if (selection instanceof ITextSelection && ((ITextSelection) selection).getStartLine() <= line
+					&& ((ITextSelection) selection).getEndLine() >= line) {
+				textSelection = (ITextSelection) selection;
+			}
+		}
+		return textSelection;
+	}
+}
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java
index 8b131a6..4e34313 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java
@@ -19,6 +19,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.core.resources.IFile;
@@ -81,6 +82,7 @@
 import org.eclipse.jdt.internal.corext.template.java.CompilationUnitContextType;
 import org.eclipse.jdt.internal.corext.template.java.JavaContextType;
 import org.eclipse.jdt.internal.debug.core.JavaDebugUtils;
+import org.eclipse.jdt.internal.debug.core.breakpoints.FirstLambdaLocationLocator;
 import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator;
 import org.eclipse.jdt.internal.debug.ui.BreakpointUtils;
 import org.eclipse.jdt.internal.debug.ui.DebugWorkingCopyManager;
@@ -101,6 +103,7 @@
 import org.eclipse.jface.text.ITextSelection;
 import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.TextSelection;
 import org.eclipse.jface.text.source.Annotation;
 import org.eclipse.jface.text.source.IAnnotationModel;
 import org.eclipse.jface.text.source.IVerticalRulerInfo;
@@ -245,6 +248,123 @@
         job.schedule();
     }
 
+	public void toggleLambdaMethodBreakpoints(final IWorkbenchPart part, final ISelection finalSelection, final ValidBreakpointLocationLocator loc) {
+		Job job = new Job("Toggle Lambda Method Breakpoints") { //$NON-NLS-1$
+			@Override
+			protected IStatus run(IProgressMonitor monitor) {
+				if (monitor.isCanceled()) {
+					return Status.CANCEL_STATUS;
+				}
+				try {
+					return doToggleLambdaMethodBreakpoints(part, finalSelection, loc, monitor);
+				} catch (CoreException e) {
+					return e.getStatus();
+				} finally {
+					BreakpointToggleUtils.setUnsetTracepoints(false);
+				}
+			}
+		};
+		job.setPriority(Job.INTERACTIVE);
+		job.setSystem(true);
+		job.schedule();
+	}
+
+	public void toggleLambdaEntryMethodBreakpoints(final IWorkbenchPart part, final ISelection finalSelection, final String lambdaMethodName) {
+		Job job = new Job("Toggle Lambda Entry Method Breakpoints") { //$NON-NLS-1$
+			@Override
+			protected IStatus run(IProgressMonitor monitor) {
+				if (monitor.isCanceled()) {
+					return Status.CANCEL_STATUS;
+				}
+				try {
+					return doToggleLambdaEntryMethodBreakpoints(part, finalSelection, lambdaMethodName, monitor);
+				} catch (CoreException e) {
+					return e.getStatus();
+				} finally {
+					BreakpointToggleUtils.setUnsetTracepoints(false);
+				}
+			}
+		};
+		job.setPriority(Job.INTERACTIVE);
+		job.setSystem(true);
+		job.schedule();
+	}
+
+	static IStatus doToggleLambdaEntryMethodBreakpoints(IWorkbenchPart part, ISelection selection, String lambdaMethodName, IProgressMonitor monitor) throws CoreException {
+		ITextEditor textEditor = getTextEditor(part);
+		if (textEditor == null || !(selection instanceof ITextSelection)) {
+			return Status.OK_STATUS;
+		}
+		ITextSelection textSelection = (ITextSelection) selection;
+		IEditorInput editorInput = textEditor.getEditorInput();
+		ITypeRoot root = getTypeRoot(editorInput);
+		if (root instanceof ICompilationUnit) {
+			ICompilationUnit unit = (ICompilationUnit) root;
+			synchronized (unit) {
+				unit.reconcile(ICompilationUnit.NO_AST, false, null, null);
+			}
+			IJavaElement[] elements = unit.codeSelect(textSelection.getOffset(), textSelection.getLength());
+			if (elements == null || elements.length == 0) {
+				BreakpointToggleUtils.report(ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable, part);
+				return Status.OK_STATUS;
+			}
+			IMethod method = null;
+			if (elements[0] instanceof IMethod) {
+				method = (IMethod) elements[0];
+			} else if (elements[0].getParent() instanceof IMethod) {
+				method = (IMethod) elements[0].getParent();
+			}
+
+			if (method != null) {
+				doToggleMethodBreakpoint(method, lambdaMethodName, part, selection, monitor);
+			} else {
+				BreakpointToggleUtils.report(ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable, part);
+			}
+		}
+		return Status.OK_STATUS;
+	}
+
+
+	static IStatus doToggleLambdaMethodBreakpoints(IWorkbenchPart part, ISelection selection, ValidBreakpointLocationLocator loc, IProgressMonitor monitor) throws CoreException {
+		ITextEditor textEditor = getTextEditor(part);
+		if (textEditor == null || !(selection instanceof ITextSelection)) {
+			return Status.OK_STATUS;
+		}
+		ITextSelection textSelection = (ITextSelection) selection;
+		IEditorInput editorInput = textEditor.getEditorInput();
+		ITypeRoot root = getTypeRoot(editorInput);
+		if (root instanceof ICompilationUnit) {
+			ICompilationUnit unit = (ICompilationUnit) root;
+			synchronized (unit) {
+				unit.reconcile(ICompilationUnit.NO_AST, false, null, null);
+			}
+			IJavaElement[] elements = unit.codeSelect(textSelection.getOffset(), textSelection.getLength());
+			if (elements == null || elements.length == 0) {
+				ValidBreakpointLocationLocator locNew = new ValidBreakpointLocationLocator(loc.getCompilationUnit(), textSelection.getStartLine()
+						+ 1, true, true);
+				locNew.getCompilationUnit().accept(locNew);
+				toggleLineBreakpoints(part, selection, false, locNew);
+				return Status.OK_STATUS;
+			}
+			IMethod method = null;
+			if (elements[0] instanceof IMethod) {
+				method = (IMethod) elements[0];
+			} else if (elements[0].getParent() instanceof IMethod) {
+				method = (IMethod) elements[0].getParent();
+			}
+
+			if (method != null) {
+				doToggleMethodBreakpoint(method, loc.getLambdaMethodName(), part, selection, monitor);
+			} else {
+				ValidBreakpointLocationLocator locNew = new ValidBreakpointLocationLocator(loc.getCompilationUnit(), textSelection.getStartLine()
+						+ 1, true, true);
+				locNew.getCompilationUnit().accept(locNew);
+				toggleLineBreakpoints(part, selection, false, locNew);
+			}
+
+		}
+		return Status.OK_STATUS;
+	}
 	static IStatus doToggleMethodBreakpoints(IWorkbenchPart part, ISelection finalSelection, IProgressMonitor monitor) throws CoreException {
 		BreakpointToggleUtils.report(null, part);
 		ISelection selection = finalSelection;
@@ -272,6 +392,10 @@
 	}
 
 	private static void doToggleMethodBreakpoint(IMethod member, IWorkbenchPart part, ISelection finalSelection, IProgressMonitor monitor) throws CoreException {
+		doToggleMethodBreakpoint(member, null, part, finalSelection, monitor);
+	}
+
+	private static void doToggleMethodBreakpoint(IMethod member, String lambdaMethodName, IWorkbenchPart part, ISelection finalSelection, IProgressMonitor monitor) throws CoreException {
 		IJavaBreakpoint breakpoint = getMethodBreakpoint(member);
 		if (breakpoint != null) {
 			if (BreakpointToggleUtils.isToggleTracepoints()) {
@@ -293,7 +417,7 @@
 		BreakpointUtils.addJavaBreakpointAttributes(attributes, member);
 		IType type = member.getDeclaringType();
 		String signature = member.getSignature();
-		String mname = member.getElementName();
+		String mname = Optional.ofNullable(lambdaMethodName).orElse(member.getElementName());
 		if (member.isConstructor()) {
 			mname = "<init>"; //$NON-NLS-1$
 			if (type.isEnum()) {
@@ -1432,9 +1556,21 @@
 			JDIDebugUIPlugin.log("Failed to parse CU for: " + editor.getTitle(), new IllegalStateException()); //$NON-NLS-1$
 			return;
 		}
-		ValidBreakpointLocationLocator loc = new ValidBreakpointLocationLocator(unit, ts.getStartLine() + 1, true, true);
+		ValidBreakpointLocationLocator loc = null;
+		boolean lambdaEntryBP = false;
+		if (BreakpointToggleUtils.isToggleLambdaEntryBreakpoint()) {
+			loc = new ValidBreakpointLocationLocator(unit, ts.getStartLine()
+				+ 1, true, true, ts.getOffset(), ts.getLength());
+			lambdaEntryBP = true;
+			BreakpointToggleUtils.setUnsetLambdaEntryBreakpoint(false);
+		} else {
+			loc = new ValidBreakpointLocationLocator(unit, ts.getStartLine() + 1, true, true);
+		}
 		unit.accept(loc);
-		if (loc.getLocationType() == ValidBreakpointLocationLocator.LOCATION_METHOD) {
+
+		if (loc.getLocationType() == ValidBreakpointLocationLocator.LOCATION_LAMBDA_METHOD) {
+			toggleLambdaMethodBreakpoints(part, ts, loc);
+		} else if (loc.getLocationType() == ValidBreakpointLocationLocator.LOCATION_METHOD) {
 			toggleMethodBreakpoints(part, ts);
 		} else if (loc.getLocationType() == ValidBreakpointLocationLocator.LOCATION_FIELD) {
 			if (BreakpointToggleUtils.isToggleTracepoints()) {
@@ -1444,7 +1580,31 @@
 			}
 			toggleWatchpoints(part, ts);
 		} else if (loc.getLocationType() == ValidBreakpointLocationLocator.LOCATION_LINE) {
-			toggleLineBreakpoints(part, ts, false, loc);
+			if (lambdaEntryBP) {
+				IEditorInput editorInput = editor.getEditorInput();
+				IDocumentProvider documentProvider = editor.getDocumentProvider();
+				if (documentProvider == null) {
+					BreakpointToggleUtils.report(ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable, part);
+					throw new CoreException(Status.CANCEL_STATUS);
+				}
+				IDocument document = documentProvider.getDocument(editorInput);
+				try {
+					IRegion region = document.getLineInformation(ts.getStartLine());
+					FirstLambdaLocationLocator firstLambda = new FirstLambdaLocationLocator(region.getOffset(), region.getOffset() + region.getLength());
+					unit.accept(firstLambda);
+					if (firstLambda.getNodeLength() == -1 || firstLambda.getNodeOffset() == -1) {
+						BreakpointToggleUtils.report(ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable, part);
+						return;
+					}
+					ITextSelection textSelection = new TextSelection(document, firstLambda.getNodeOffset(), firstLambda.getNodeLength());
+					toggleLambdaEntryMethodBreakpoints(part, textSelection, firstLambda.getLambdaMethodName());
+				} catch (BadLocationException e) {
+					BreakpointToggleUtils.report(ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable, part);
+				}
+
+			} else {
+				toggleLineBreakpoints(part, ts, false, loc);
+			}
 		}
 	}
 
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/FirstLambdaLocationLocator.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/FirstLambdaLocationLocator.java
new file mode 100644
index 0000000..9f743a2
--- /dev/null
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/FirstLambdaLocationLocator.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2022 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.core.breakpoints;
+
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.LambdaExpression;
+
+public class FirstLambdaLocationLocator extends ASTVisitor {
+	private int fNodeLength = -1;
+	private int fNodeOffset = -1;
+	private int fLineOffset = -1;
+	private int fLineEndPosition = -1;
+	private String fLambdaMethodName;
+	private boolean fLocationFound = false;
+
+	public FirstLambdaLocationLocator(int lineOffset, int lineEndPosition) {
+		fLineOffset = lineOffset;
+		fLineEndPosition = lineEndPosition;
+	}
+
+	/**
+	 * Return of the name of the lambda method where the valid location is.
+	 */
+	public String getLambdaMethodName() {
+		return fLambdaMethodName;
+	}
+
+	public int getNodeLength() {
+		return fNodeLength;
+	}
+
+	public int getNodeOffset() {
+		return fNodeOffset;
+	}
+
+	@Override
+	public boolean visit(LambdaExpression node) {
+		if (fLocationFound) {
+			return false;
+		}
+		if (node.getStartPosition() < fLineOffset || node.getStartPosition() > fLineEndPosition) {
+			return false;
+		}
+		fNodeLength = node.getLength();
+		fNodeOffset = node.getStartPosition();
+		IMethodBinding methodBinding = node.resolveMethodBinding();
+		if (methodBinding != null) {
+			fLambdaMethodName = toMethodName(methodBinding);
+			fLocationFound = true;
+		}
+		return false;
+	}
+
+	private String toMethodName(IMethodBinding methodBinding) {
+		String key = methodBinding.getKey();
+		return key.substring(key.indexOf('.') + 1, key.indexOf('('));
+	}
+
+}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaMethodBreakpoint.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaMethodBreakpoint.java
index 42f014c..206894a 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaMethodBreakpoint.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaMethodBreakpoint.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -30,6 +30,7 @@
 import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
 import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
 import org.eclipse.jdt.internal.debug.core.model.JDIThread;
+import org.eclipse.jdt.internal.debug.core.model.LambdaUtils;
 import org.eclipse.jdt.internal.debug.core.model.MethodResult;
 import org.eclipse.jdt.internal.debug.core.model.MethodResult.ResultType;
 
@@ -464,7 +465,7 @@
 				}
 			}
 
-			if (getMethodName() != null) {
+			if (getMethodName() != null && !LambdaUtils.isLambdaMethod(method)) {
 				if (!method.name().equals(getMethodName())) {
 					return true;
 				}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java
index 76cdc9a..91d3864 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2003, 2019 IBM Corporation and others.
+ * Copyright (c) 2003, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -54,6 +54,7 @@
 import org.eclipse.jdt.core.dom.FieldDeclaration;
 import org.eclipse.jdt.core.dom.ForStatement;
 import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
 import org.eclipse.jdt.core.dom.ITypeBinding;
 import org.eclipse.jdt.core.dom.IVariableBinding;
 import org.eclipse.jdt.core.dom.IfStatement;
@@ -63,6 +64,7 @@
 import org.eclipse.jdt.core.dom.InstanceofExpression;
 import org.eclipse.jdt.core.dom.Javadoc;
 import org.eclipse.jdt.core.dom.LabeledStatement;
+import org.eclipse.jdt.core.dom.LambdaExpression;
 import org.eclipse.jdt.core.dom.LineComment;
 import org.eclipse.jdt.core.dom.MarkerAnnotation;
 import org.eclipse.jdt.core.dom.MemberRef;
@@ -125,6 +127,8 @@
 	public static final int LOCATION_LINE = 1;
 	public static final int LOCATION_METHOD = 2;
 	public static final int LOCATION_FIELD = 3;
+	public static final int LOCATION_LAMBDA_METHOD = 4;
+
 
 	private CompilationUnit fCompilationUnit;
 	private int fLineNumber;
@@ -134,10 +138,15 @@
 
 	private int fLocationType;
 	private boolean fLocationFound;
+	private boolean fLambdaVisited;
+	private String fLambdaMethodName;
 	private String fTypeName;
 	private int fLineLocation;
 	private int fMemberOffset;
+	private int fNodeLength;
 	private List<String> fLabels;
+	private int fInputOffset;
+	private int fInputLength;
 
 	/**
 	 * @param compilationUnit
@@ -156,6 +165,30 @@
 		fBindingsResolved = bindingsResolved;
 		fBestMatch = bestMatch;
 		fLocationFound = false;
+		fInputOffset = -1;
+		fInputLength = -1;
+	}
+
+	/**
+	 * @param compilationUnit
+	 *            the JDOM CompilationUnit of the source code.
+	 * @param lineNumber
+	 *            the line number in the source code where to put the breakpoint.
+	 * @param bestMatch
+	 *            if <code>true</code> look for the best match, otherwise look only for a valid line
+	 * @param offset
+	 *            selection offset on which breakpoints should be toggled
+	 * @param end
+	 *            selection end on which breakpoints should be toggled
+	 */
+	public ValidBreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved, boolean bestMatch, int offset, int end) {
+		fCompilationUnit = compilationUnit;
+		fLineNumber = lineNumber;
+		fBindingsResolved = bindingsResolved;
+		fBestMatch = bestMatch;
+		fLocationFound = false;
+		fInputOffset = offset;
+		fInputLength = end;
 	}
 
 	/**
@@ -189,6 +222,12 @@
 	}
 
 	/**
+	 * Return of the name of the lambda method where the valid location is.
+	 */
+	public String getLambdaMethodName() {
+		return fLambdaMethodName;
+	}
+	/**
 	 * Return the line number of the computed valid location
 	 */
 	public int getLineLocation() {
@@ -206,6 +245,13 @@
 		return fMemberOffset;
 	}
 
+	public int getNodeLength() {
+		return fNodeLength;
+	}
+
+	public CompilationUnit getCompilationUnit() {
+		return fCompilationUnit;
+	}
 	/**
 	 * Compute the name of the type which contains this node. <br>
 	 * <br>
@@ -281,13 +327,17 @@
 	 *            lines.
 	 */
 	private boolean visit(ASTNode node, boolean isCode) {
+		int startPosition = node.getStartPosition();
+		int startLine = lineNumber(startPosition);
+		int endLine = lineNumber(startPosition + node.getLength() - 1);
+
 		// if we already found a correct location
-		// no need to check the element inside.
-		if (fLocationFound) {
+		// no need to check the element inside if the line number doesn't match.
+		// if line numbers match we still need to visit the whole expression in this line
+		// to make sure we have any lambda's we were looking for which we didn't visited yet.
+		if (fLambdaVisited || (fLocationFound && fLineNumber != startLine)) {
 			return false;
 		}
-		int startPosition = node.getStartPosition();
-		int endLine = lineNumber(startPosition + node.getLength() - 1);
 		// if the position is not in this part of the code
 		// no need to check the element inside.
 		if (endLine < fLineNumber) {
@@ -298,7 +348,6 @@
 		// breakpoint is requested on this line or on a previous line, this is a
 		// valid
 		// location
-		int startLine = lineNumber(startPosition);
 		if (isCode && (fLineNumber <= startLine)) {
 			fLineLocation = startLine;
 			fLocationFound = true;
@@ -595,6 +644,15 @@
 	 */
 	@Override
 	public boolean visit(ClassInstanceCreation node) {
+		if (visit(node, false)) {
+			List<? extends ASTNode> arguments = node.arguments();
+			for (ASTNode astNode : arguments) {
+				if (astNode instanceof LambdaExpression) {
+					astNode.accept(this);
+					return false;
+				}
+			}
+		}
 		return visit(node, true);
 	}
 
@@ -708,7 +766,16 @@
 	 */
 	@Override
 	public boolean visit(ExpressionStatement node) {
-		return visit(node, false);
+		if (fLocationFound && fLambdaVisited) {
+			return false;
+		}
+		if (visit(node, false)) {
+			if (node.getExpression() instanceof MethodInvocation) {
+				node.getExpression().accept(this);
+				return false;
+			}
+		}
+		return visit(node, true);
 	}
 
 	/**
@@ -948,6 +1015,56 @@
 		fLabels.remove(fLabels.size() - 1);
 	}
 
+	@Override
+	public boolean visit(LambdaExpression node) {
+		fMemberOffset = node.getStartPosition();
+		fNodeLength = node.getLength();
+		if (fInputOffset != -1 && fInputLength > 0 && fInputOffset >= fMemberOffset && fInputOffset + fInputLength <= fMemberOffset + fNodeLength) {
+			IMethodBinding methodBinding = node.resolveMethodBinding();
+			if (methodBinding != null) {
+				fLambdaVisited = true;
+				fLocationType = LOCATION_LAMBDA_METHOD;
+				fLambdaMethodName = toMethodName(methodBinding);
+				fLocationFound = true;
+				return false;
+			}
+		} else if (fLocationType != LOCATION_LAMBDA_METHOD) {
+			fLocationType = LOCATION_LINE;
+			fTypeName = computeTypeName(node);
+			int startLine = lineNumber(fMemberOffset);
+			if (fLineNumber <= startLine) {
+				if (fInputOffset != -1) {
+					fLineLocation = lineNumber(fInputOffset);
+				} else {
+					fLineLocation = startLine;
+				}
+			} else {
+				// visit the body
+				ASTNode body = node.getBody();
+				if (body instanceof Block) { // body is null for abstract methods
+					fLocationFound = false;
+					Block block1 = (Block) body;
+					if (visit(block1)) {
+						for (Object object : block1.statements()) {
+							if (object instanceof ASTNode) {
+								ASTNode node1 = (ASTNode) object;
+								node1.accept(this);
+							}
+
+						}
+					}
+				}
+			}
+			return false;
+		}
+		return visit(node, true);
+	}
+
+	private String toMethodName(IMethodBinding methodBinding) {
+		String key = methodBinding.getKey();
+		return key.substring(key.indexOf('.') + 1, key.indexOf('('));
+	}
+	
 	/*
 	 * (non-Javadoc)
 	 *
@@ -1028,6 +1145,27 @@
 	 */
 	@Override
 	public boolean visit(MethodInvocation node) {
+		if (fLocationFound && fLambdaVisited) {
+			return false;
+		}
+		if (visit(node, false)) {
+			// first run through arguments to avoid pre-mature line breakpoint identification
+			List<? extends ASTNode> arguments = node.arguments();
+			for (ASTNode astNode : arguments) {
+				// arguments needs to be accepted to handle stream operation where the lambda debug point
+				// is expected on a lambda expression which is a parameter on a nested method invocation like
+				// strings.stream().collect(Collectors.toMap(s -> s.substring(0), s -> s))
+				astNode.accept(this);
+				if (astNode instanceof LambdaExpression && fLambdaVisited) {
+					return false;
+				}
+			}
+
+			Expression expression = node.getExpression();
+			if (expression instanceof ClassInstanceCreation || expression instanceof MethodInvocation) {
+				expression.accept(this);
+			}
+		}
 		return visit(node, true);
 	}
 
@@ -1403,6 +1541,9 @@
 						fLocationFound = true;
 						fLocationType = LOCATION_LINE;
 						fTypeName = computeTypeName(node);
+						if (initializer instanceof MethodInvocation || initializer instanceof LambdaExpression) {
+							initializer.accept(this);
+						}
 					return false;
 				}
 				initializer.accept(this);
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/LambdaUtils.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/LambdaUtils.java
index 272b486..aee1c97 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/LambdaUtils.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/LambdaUtils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 IBM Corporation and others.
+ * Copyright (c) 2018, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0