Bug 341232 - Eclipse is not able to set the simplest of conditional
breakpoints and reports that the condition has compilation errors when
it doesn't 
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
index fc3c7d6..ef43203 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
  * 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Jesper Steen Moller - bug 341232
  *******************************************************************************/
 package org.eclipse.jdt.debug.tests;
 
@@ -360,6 +361,7 @@
 				cfgs.add(createLaunchConfiguration(jp, "a.b.c.MethodBreakpoints"));
 				cfgs.add(createLaunchConfiguration(jp, "a.b.c.IntegerAccess"));
 				cfgs.add(createLaunchConfiguration(jp, "a.b.c.StepIntoSelectionWithGenerics"));
+				cfgs.add(createLaunchConfiguration(jp, "a.b.c.ConditionalsNearGenerics"));
 				loaded15 = true;
 				waitForBuild();
 	        }
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
index af076ff..1c336f5 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
@@ -22,6 +22,7 @@
 import org.eclipse.jdt.debug.tests.breakpoints.BreakpointListenerTests;
 import org.eclipse.jdt.debug.tests.breakpoints.BreakpointLocationVerificationTests;
 import org.eclipse.jdt.debug.tests.breakpoints.BreakpointWorkingSetTests;
+import org.eclipse.jdt.debug.tests.breakpoints.ConditionalBreakpoints15Tests;
 import org.eclipse.jdt.debug.tests.breakpoints.ConditionalBreakpointsTests;
 import org.eclipse.jdt.debug.tests.breakpoints.DeferredBreakpointTests;
 import org.eclipse.jdt.debug.tests.breakpoints.ExceptionBreakpointTests;
@@ -182,6 +183,7 @@
 			addTest(new TestSuite(MethodBreakpointTests15.class));
 			addTest(new TestSuite(TestIntegerAccessUnboxing15.class));
 			addTest(new TestSuite(StepIntoSelectionWithGenerics.class));
+			addTest(new TestSuite(ConditionalBreakpoints15Tests.class));
 		}
 		
 	//Sourcelookup tests
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/ConditionalBreakpoints15Tests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/ConditionalBreakpoints15Tests.java
new file mode 100644
index 0000000..af41b34
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/ConditionalBreakpoints15Tests.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Jesper Steen Moller and others.
+ * 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:
+ *     Jesper Steen Moller - initial API and implementation, adapted from
+ *     Stefan Mandels contribution in bug 341232, and existing debug tests
+ *******************************************************************************/
+package org.eclipse.jdt.debug.tests.breakpoints;
+
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.debug.tests.AbstractDebugTest;
+
+/**
+ * Tests conditional breakpoints.
+ */
+public class ConditionalBreakpoints15Tests extends AbstractDebugTest {
+	
+	/**
+	 * Constructor
+	 * @param name
+	 */
+	public ConditionalBreakpoints15Tests(String name) {
+		super(name);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.debug.tests.AbstractDebugTest#getProjectContext()
+	 */
+	@Override
+	protected IJavaProject getProjectContext() {
+		return get15Project();
+	}
+
+	/**
+	 * Tests a breakpoint with a simple condition
+	 * @throws Exception
+	 */
+	public void testSimpleConditionalBreakpointOnParameterizedType() throws Exception {
+		String typeName = "a.b.c.ConditionalsNearGenerics";
+		String innerTypeName = "a.b.c.ConditionalsNearGenerics.ItemIterator";
+		IJavaLineBreakpoint bp1 = createConditionalLineBreakpoint(33, typeName, "false", true);
+		IJavaLineBreakpoint bp2 = createConditionalLineBreakpoint(39, typeName, "false", true);
+		IJavaLineBreakpoint bp3 = createConditionalLineBreakpoint(52, innerTypeName, "false", true);
+		IJavaLineBreakpoint bp4 = createConditionalLineBreakpoint(53, innerTypeName, "true", true);
+		
+		IJavaThread thread= null;
+		try {
+			thread= launchToLineBreakpoint(typeName, bp4); // If compiled correctly, this will jump over bp1-bp3 !!
+
+			bp1.delete();
+			bp2.delete();
+			bp3.delete();
+			bp4.delete();
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}		
+	}
+	
+	}
diff --git a/org.eclipse.jdt.debug.tests/testsource-j2se-1.5/a/b/c/ConditionalsNearGenerics.java b/org.eclipse.jdt.debug.tests/testsource-j2se-1.5/a/b/c/ConditionalsNearGenerics.java
new file mode 100644
index 0000000..93565a0
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/testsource-j2se-1.5/a/b/c/ConditionalsNearGenerics.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Jesper Steen Moller and others.
+ * 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:
+ *     Jesper Steen Moller - initial API and implementation, adapted from
+ *     Stefan Mandels contribution in bug 341232
+ *******************************************************************************/
+package a.b.c;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+public class ConditionalsNearGenerics {
+
+	private String name;
+
+	public ConditionalsNearGenerics() {
+		// set a conditional breakpoint in next line: use true as expression
+		this.name = "bug";
+	}
+
+	public static void main(String[] args) throws Exception {
+		new ConditionalsNearGenerics().bug();
+	}
+
+	public void bug() throws Exception {
+		char[] chars = name.toCharArray();
+		System.out.println(chars);
+		tokenize(Arrays.asList(1,2,3), name);
+	}
+
+	//FIXME delete following method, then the breakpoint shall work
+	public <T extends Number> Iterator<T> tokenize(List<T> list, String input) {
+		new ItemIterator<Item>(input);
+		return list.iterator();
+	}
+
+	public interface Item {
+
+	}
+
+	private class ItemIterator<T extends Item> implements Iterator<T> {
+
+		private String input;
+
+		public ItemIterator(String input) {
+			this.input = input;
+			System.out.println("From ItemIterator!");
+		}
+
+		@Override
+		public boolean hasNext() {
+			return false;
+		}
+
+		@Override
+		public T next() {
+			return null;
+		}
+
+		@Override
+		public void remove() {
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
index 165fb7a..f35e2da 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
  * 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Jesper Steen Moller - bug 341232
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.eval.ast.engine;
 
@@ -336,7 +337,7 @@
 			// }
 
 			unit = parseCompilationUnit(
-					mapper.getSource(receivingType, javaProject,
+					mapper.getSource(receivingType, frame.getLineNumber(), javaProject,
 							frame.isStatic()).toCharArray(),
 					mapper.getCompilationUnitName(), javaProject);
 		} catch (CoreException e) {
@@ -428,7 +429,7 @@
 			if (javaTypes.length > 0) {
 				IJavaReferenceType recType = (IJavaReferenceType) javaTypes[0];
 				unit = parseCompilationUnit(
-						mapper.getSource(recType, getJavaProject(), false)
+						mapper.getSource(recType, -1, getJavaProject(), false)
 								.toCharArray(),
 						mapper.getCompilationUnitName(), javaProject);
 			} else {
@@ -493,7 +494,7 @@
 
 		try {
 			unit = parseCompilationUnit(
-					mapper.getSource(type, javaProject, false).toCharArray(),
+					mapper.getSource(type, -1, javaProject, false).toCharArray(),
 					mapper.getCompilationUnitName(), javaProject);
 		} catch (CoreException e) {
 			InstructionSequence expression = new InstructionSequence(snippet);
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
index f21a3b9..d752a0f 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
  * 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
@@ -7,6 +7,7 @@
  * 
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Jesper Steen Moller - bug 341232
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.eval.ast.engine;
 
@@ -137,7 +138,7 @@
 	}
 
 	private void createEvaluationSourceFromSource(String source, IType type,
-			boolean createInAStaticMethod, IJavaProject project)
+			int line, boolean createInAStaticMethod, IJavaProject project)
 			throws DebugException {
 		ASTParser parser = ASTParser.newParser(AST.JLS4);
 		parser.setSource(source.toCharArray());
@@ -146,7 +147,7 @@
 		parser.setCompilerOptions(options);
 		CompilationUnit unit = (CompilationUnit) parser.createAST(null);
 		SourceBasedSourceGenerator visitor = new SourceBasedSourceGenerator(
-				type, createInAStaticMethod, fLocalVariableTypeNames,
+				type, line, createInAStaticMethod, fLocalVariableTypeNames,
 				fLocalVariableNames, fCodeSnippet, sourceLevel);
 		unit.accept(visitor);
 
@@ -217,7 +218,7 @@
 		return objectToEvaluationSourceMapper;
 	}
 
-	public String getSource(IJavaReferenceType type, IJavaProject javaProject,
+	public String getSource(IJavaReferenceType type, int line, IJavaProject javaProject,
 			boolean isStatic) throws CoreException {
 		if (fSource == null) {
 			IType iType = JavaDebugUtils.resolveType(type);
@@ -230,7 +231,7 @@
 				}
 				if (baseSource != null) {
 					createEvaluationSourceFromSource(baseSource, iType,
-							isStatic, javaProject);
+							line, isStatic, javaProject);
 				}
 			}
 			if (fSource == null) {
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
index dbbd7d9..837bac0 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
  * 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
@@ -7,13 +7,17 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Jesper Steen Moller - bug 341232
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.eval.ast.engine;
 
-import java.util.HashSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
+import java.util.Stack;
 
 import org.eclipse.jdt.core.Flags;
 import org.eclipse.jdt.core.IType;
@@ -131,6 +135,7 @@
 	private String fError;
 
 	private IType fType;
+	private int fLine;
 
 	private StringBuffer fSource;
 
@@ -149,7 +154,12 @@
 	private int fSourceMajorLevel;
 	private int fSourceMinorLevel;
 
-	private Set<String> fTypeParameters = new HashSet<String>();
+	private Stack<Map<String, String>> fTypeParameterStack = new Stack<Map<String, String>>();
+	private Map<String, String> fMatchingTypeParameters = null;
+	private CompilationUnit fCompilationUnit;
+	{
+		fTypeParameterStack.push(Collections.<String,String>emptyMap());
+	}
 
 	/**
 	 * if the <code>createInAnInstanceMethod</code> flag is set, the method
@@ -158,6 +168,8 @@
 	 * 
 	 * @param type
 	 *            the root {@link IType}
+	 * @param sourcePosition
+	 *            the reference position in the type's source
 	 * @param createInAStaticMethod
 	 *            if the source should be generated
 	 * @param localTypesNames
@@ -170,10 +182,11 @@
 	 *            the desired source level
 	 */
 	public SourceBasedSourceGenerator(IType type,
-			boolean createInAStaticMethod, String[] localTypesNames,
+			int line, boolean createInAStaticMethod, String[] localTypesNames,
 			String[] localVariables, String codeSnippet, String sourceLevel) {
 		fRightTypeFound = false;
 		fType = type;
+		fLine = line;
 		fLocalVariableTypeNames = localTypesNames;
 		fLocalVariableNames = localVariables;
 		fCodeSnippet = codeSnippet;
@@ -243,8 +256,9 @@
 
 		// add type parameters as required
 		if (isSourceLevelGreaterOrEqual(1, 5)) {
-			if (!fTypeParameters.isEmpty()) {
-				Iterator<String> iterator = fTypeParameters.iterator();
+			Collection<String> activeTypeParameters = (fMatchingTypeParameters != null ? fMatchingTypeParameters : fTypeParameterStack.peek()).values();
+			if (!activeTypeParameters.isEmpty()) {
+				Iterator<String> iterator = activeTypeParameters.iterator();
 				buffer.append(Signature.C_GENERIC_START);
 				while (iterator.hasNext()) {
 					String name = iterator.next();
@@ -740,7 +754,7 @@
 		if (type.isSimpleType()) {
 			String name = getQualifiedIdentifier(((SimpleType) type).getName());
 			if (!isSourceLevelGreaterOrEqual(1, 5)
-					&& fTypeParameters.contains(name)) {
+					&& fTypeParameterStack.peek().containsKey(name)) {
 				return "Object"; //$NON-NLS-1$
 			}
 			return name;
@@ -1008,6 +1022,13 @@
 		}
 	}
 
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.MethodDeclaration)
+	 */
+	public void endVisit(MethodDeclaration node) {
+		fTypeParameterStack.pop();
+	}
+
 	/**
 	 * @see ASTVisitor#endVisit(TypeDeclaration)
 	 */
@@ -1015,6 +1036,7 @@
 	public void endVisit(TypeDeclaration node) {
 
 		if (hasError()) {
+			fTypeParameterStack.pop();
 			return;
 		}
 
@@ -1027,6 +1049,7 @@
 
 		if (!fEvaluateNextEndTypeDeclaration) {
 			fEvaluateNextEndTypeDeclaration = true;
+			fTypeParameterStack.pop();
 			return;
 		}
 
@@ -1060,6 +1083,7 @@
 				fLastTypeName = node.getName().getIdentifier();
 			}
 		}
+		fTypeParameterStack.pop();
 	}
 
 	/*
@@ -1254,6 +1278,7 @@
 	 */
 	@Override
 	public boolean visit(CompilationUnit node) {
+		fCompilationUnit = node;
 		if (rightTypeFound()) {
 			return false;
 		}
@@ -1532,13 +1557,15 @@
 	 */
 	@Override
 	public boolean visit(MethodDeclaration node) {
+		String name = node.getName().toString();
+		System.out.println(name);
+		int firstLine = fCompilationUnit.getLineNumber(node.getStartPosition());
+		int lastLine = fCompilationUnit.getLineNumber(node.getStartPosition() + node.getLength());
+		
 		List<TypeParameter> typeParameters = node.typeParameters();
-		if (!typeParameters.isEmpty()) {
-			Iterator<TypeParameter> iterator = typeParameters.iterator();
-			while (iterator.hasNext()) {
-				TypeParameter typeParameter = iterator.next();
-				fTypeParameters.add(typeParameter.toString());
-			}
+		pushTypeParameters(typeParameters);
+		if (isRightType(node.getParent()) && firstLine <= fLine && fLine <= lastLine) {
+				fMatchingTypeParameters  = fTypeParameterStack.peek();
 		}
 		if (rightTypeFound()) {
 			return false;
@@ -1546,6 +1573,21 @@
 		return true;
 	}
 
+	private void pushTypeParameters(List<TypeParameter> typeParameters) {
+		if (!typeParameters.isEmpty()) {
+			HashMap<String,String> newTypeParameters = new HashMap<String,String>(fTypeParameterStack.peek());
+			Iterator<TypeParameter> iterator = typeParameters.iterator();
+			while (iterator.hasNext()) {
+				TypeParameter typeParameter = iterator.next();
+				String boundName = typeParameter.getName().getIdentifier();
+				newTypeParameters.put(boundName, typeParameter.toString());
+			}
+			fTypeParameterStack.push(newTypeParameters); // Push the new "scope"
+		} else {
+			fTypeParameterStack.push(fTypeParameterStack.peek()); // Push the same
+		}
+	}
+
 	/**
 	 * @see ASTVisitor#visit(MethodInvocation)
 	 */
@@ -1925,13 +1967,7 @@
 	@Override
 	public boolean visit(TypeDeclaration node) {
 		List<TypeParameter> typeParameters = node.typeParameters();
-		if (!typeParameters.isEmpty()) {
-			Iterator<TypeParameter> iterator = typeParameters.iterator();
-			while (iterator.hasNext()) {
-				TypeParameter typeParameter = iterator.next();
-				fTypeParameters.add(typeParameter.getName().getIdentifier());
-			}
-		}
+		pushTypeParameters(typeParameters);
 		if (rightTypeFound()) {
 			fEvaluateNextEndTypeDeclaration = false;
 			return false;