Bug 462929 - JavaClassPrepareBreakpoint does not handle wildcards

Change-Id: Ia9f0e7322b0f5a668491003929b34e9b213a2c66
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/TypeNameBreakpointTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/TypeNameBreakpointTests.java
index b36c24b..c45a187 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/TypeNameBreakpointTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/TypeNameBreakpointTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation and others.
+ * Copyright (c) 2014, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -18,11 +18,17 @@
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
 import org.eclipse.debug.core.ILaunch;
 import org.eclipse.debug.core.ILaunchConfiguration;
 import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jdt.core.dom.Message;
 import org.eclipse.jdt.debug.core.IJavaBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaBreakpointListener;
+import org.eclipse.jdt.debug.core.IJavaDebugTarget;
+import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
 import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.debug.core.IJavaType;
 import org.eclipse.jdt.debug.core.JDIDebugModel;
 import org.eclipse.jdt.debug.testplugin.DebugElementKindEventDetailWaiter;
 import org.eclipse.jdt.debug.testplugin.DebugEventWaiter;
@@ -502,4 +508,80 @@
 		assertEquals("expected exactly 1 launch but got: " + Arrays.toString(launches), 1, launches.length);
 		launchManager.removeLaunch(launches[0]);
 	}
+
+	class TestJavaBreakpointListener implements IJavaBreakpointListener {
+
+		private String breakpointTypeName;
+
+		public String getBreakpointTypeName() {
+			return breakpointTypeName;
+		}
+
+		@Override
+		public void addingBreakpoint(IJavaDebugTarget target, IJavaBreakpoint breakpoint) {
+		}
+
+		@Override
+		public int installingBreakpoint(IJavaDebugTarget target, IJavaBreakpoint breakpoint, IJavaType type) {
+			return 0;
+		}
+
+		@Override
+		public void breakpointInstalled(IJavaDebugTarget target, IJavaBreakpoint breakpoint) {
+		}
+
+		@Override
+		public int breakpointHit(IJavaThread thread, IJavaBreakpoint breakpoint) {
+			try {
+				breakpointTypeName = breakpoint.getTypeName();
+			} catch (CoreException e) {
+				e.printStackTrace();
+			}
+			return 0;
+		}
+
+		@Override
+		public void breakpointRemoved(IJavaDebugTarget target, IJavaBreakpoint breakpoint) {
+		}
+
+		@Override
+		public void breakpointHasRuntimeException(IJavaLineBreakpoint breakpoint, DebugException exception) {
+		}
+
+		@Override
+		public void breakpointHasCompilationErrors(IJavaLineBreakpoint breakpoint, Message[] errors) {
+		}
+	}
+
+	public void testWildCardClassPrepareBreakpoint() throws Exception {
+		JavaClassPrepareBreakpoint bp = (JavaClassPrepareBreakpoint) JDIDebugModel.createClassPrepareBreakpoint(getTestResource(), "Hit*", 1, -1, -1, true, null);
+		assertNotNull("The class prepare breakpoint should not be null", bp);
+		TestJavaBreakpointListener l = new TestJavaBreakpointListener();
+		IJavaThread thread = null;
+		try {
+			JDIDebugModel.addJavaBreakpointListener(l);
+			thread = launchToBreakpoint(getLaunchConfiguration("HitCountLooper"));
+			assertEquals(l.getBreakpointTypeName(), "HitCountLooper");
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+			JDIDebugModel.removeJavaBreakpointListener(l);
+		}
+	}
+
+	public void testEqualClassPrepareBreakpoint() throws Exception {
+		JavaClassPrepareBreakpoint bp = (JavaClassPrepareBreakpoint) JDIDebugModel.createClassPrepareBreakpoint(getTestResource(), "HitCountLooper", 1, -1, -1, true, null);
+		assertNotNull("The class prepare breakpoint should not be null", bp);
+		TestJavaBreakpointListener l = new TestJavaBreakpointListener();
+		IJavaThread thread = null;
+		try {
+			JDIDebugModel.addJavaBreakpointListener(l);
+			thread = launchToBreakpoint(getLaunchConfiguration("HitCountLooper"));
+			assertEquals(l.getBreakpointTypeName(), "HitCountLooper");
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+			JDIDebugModel.removeJavaBreakpointListener(l);
+		}
+	}
 }
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaClassPrepareBreakpoint.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaClassPrepareBreakpoint.java
index 09cc034..2fad0c3 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaClassPrepareBreakpoint.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaClassPrepareBreakpoint.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 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
@@ -14,6 +14,7 @@
 package org.eclipse.jdt.internal.debug.core.breakpoints;
 
 import java.util.Map;
+import java.util.regex.Pattern;
 
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IResource;
@@ -58,6 +59,8 @@
 	 */
 	protected static final String MEMBER_TYPE = "org.eclipse.jdt.debug.core.memberType"; //$NON-NLS-1$
 
+	private Pattern pattern = null;
+
 	/**
 	 * Creates and returns a Java class prepare breakpoint for the given type.
 	 *
@@ -212,13 +215,21 @@
 	public boolean handleClassPrepareEvent(ClassPrepareEvent event,
 			JDIDebugTarget target, boolean suspendVote) {
 		try {
-			if (isEnabled()
-					&& event.referenceType().name().equals(getTypeName())) {
+			if (pattern == null){
+				 String typeName = ".*";//$NON-NLS-1$
+				 String breakpointName = ensureMarker().getAttribute(TYPE_NAME, null);
+				 typeName = typeName + breakpointName.replaceAll("\\.", "\\\\.");  //$NON-NLS-1$//$NON-NLS-2$
+				 typeName = typeName.replaceAll("\\$", "\\\\\\$");  //$NON-NLS-1$//$NON-NLS-2$
+				 typeName = typeName.concat(".*"); //$NON-NLS-1$
+				 pattern = Pattern.compile(typeName);
+			}
+			if (isEnabled() && pattern.matcher(event.referenceType().name()).find()){
 				ThreadReference threadRef = event.thread();
 				JDIThread thread = target.findThread(threadRef);
 				if (thread == null || thread.isIgnoringBreakpoints()) {
 					return true;
 				}
+				fInstalledTypeName = event.referenceType().name();
 				return handleBreakpointEvent(event, thread, suspendVote);
 			}
 		} catch (CoreException e) {