[432978] Support nested at_begin vars

Signed-off-by: Gregory Amerson <gregory.amerson@liferay.com>
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
index 65f1a23..5392bf6 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2013 IBM Corporation and others.
+ * Copyright (c) 2004, 2014 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
@@ -27,6 +27,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.Stack;
@@ -250,6 +251,8 @@
 	 * the ones needed for AT_END variable support.
 	 */
 	private StackMap fTagToVariableMap = null;
+	private Map fAtBeginVariableMap = null;
+	private Stack fAtBeginScopeStack = new Stack();
 	private Stack fUseBeansStack = new Stack();
 
 	/** the final translated java document */
@@ -333,9 +336,6 @@
 	/** The model path as it was persisted */
 	private IPath fSavedModelPath = null;
 
-	/** the set of variable names that we have declared */
-	private Set fDeclSet = new HashSet();
-
 	/**
 	 * A structure for holding a region collection marker and list of variable
 	 * information. The region can be used later for positioning validation
@@ -344,10 +344,12 @@
 	static class RegionTags {
 		ITextRegionCollection region;
 		CustomTag tag;
+		Collection scopedVarNames = null;
 
-		RegionTags(ITextRegionCollection region, CustomTag tag) {
+		RegionTags(ITextRegionCollection region, CustomTag tag, Collection scopedVarNames) {
 			this.region = region;
 			this.tag = tag;
+			this.scopedVarNames = scopedVarNames;
 		}
 	}
 
@@ -644,7 +646,6 @@
 		
 		fFoundNonTranslatedCode = false;
 		fCodeTranslated = false;
-		fDeclSet.clear();
 	}
 
 	/**
@@ -953,6 +954,8 @@
 					appendToBuffer(decl, fUserCode, true, customTag);
 				}
 			}
+
+			fAtBeginVariableMap.remove( fAtBeginScopeStack.pop() );
 		}
 		else {
 			/*
@@ -975,19 +978,46 @@
 		CustomTag tag = helper.getCustomTag(tagToAdd, getStructuredDocument(), customTag, problems);
 		TaglibVariable[] taglibVars = tag.getTagVariables();
 		fTranslationProblems.addAll(problems);
+		Set scopedVarNames = new HashSet(0);
 		/*
 		 * Add AT_BEGIN variables
 		 */
 		for (int i = 0; i < taglibVars.length; i++) {
 			if (taglibVars[i].getScope() == VariableInfo.AT_BEGIN) {
-				// check to see if we have already declared this variable once, if so then just reassign it instead
-				if (fDeclSet.contains(taglibVars[i].getVarName())) {
+				scopedVarNames.add(taglibVars[i].getVarName());
+				boolean declaredInParentScope = false;
+				/*
+				 * Check to see if we have already declared this variable
+				 * once, if so then just reassign it instead. Declaring twice
+				 * in the same scope should cause an error, so we're only
+				 * checking parent scopes and the current scope.
+				 */
+				RegionTags[] parentTags = (RegionTags[]) fTagToVariableMap.values().toArray(new RegionTags[fTagToVariableMap.size()]);
+				String varName = taglibVars[i].getVarName();
+				for (int j = 0; j < parentTags.length && !declaredInParentScope; j++) {
+					declaredInParentScope |= parentTags[j].scopedVarNames.contains(varName);
+				}
+
+				Set currentAtBeginVars = (Set) fAtBeginVariableMap.get( fAtBeginScopeStack.peek() );
+
+				boolean declaredInCurrentScope = currentAtBeginVars != null && currentAtBeginVars.contains( varName );
+
+				if (declaredInParentScope || declaredInCurrentScope) {
 					decl = taglibVars[i].getDeclarationString(false, fContext, TaglibVariable.M_REASSIGN);
 				}
 				else {
 					decl = taglibVars[i].getDeclarationString(fContext);
-					fDeclSet.add( taglibVars[i].getVarName() );
+
+					if( currentAtBeginVars == null ) {
+					    currentAtBeginVars = new HashSet();
+					    currentAtBeginVars.add( varName );
+					    fAtBeginVariableMap.put( fAtBeginScopeStack.peek(), currentAtBeginVars );
+					}
+					else {
+					    currentAtBeginVars.add( varName );
+					}
 				}
+
 				appendToBuffer(decl, fUserCode, true, customTag);
 			}
 		}
@@ -1015,6 +1045,7 @@
 
 		for (int i = 0; i < taglibVars.length; i++) {
 			if (taglibVars[i].getScope() == VariableInfo.NESTED) {
+				scopedVarNames.add(taglibVars[i].getVarName());
 				decl = taglibVars[i].getDeclarationString(fContext);
 				appendToBuffer(decl, fUserCode, true, customTag);
 			}
@@ -1040,7 +1071,8 @@
 			/*
 			 * For non-empty tags, remember the variable information
 			 */
-			fTagToVariableMap.push(tagToAdd, new RegionTags(customTag, tag));
+			fTagToVariableMap.push(tagToAdd, new RegionTags(customTag, tag, scopedVarNames));
+			fAtBeginScopeStack.push( tagToAdd );
 		}
 		
 	}
@@ -1131,6 +1163,11 @@
 		if (fTagToVariableMap == null) {
 			fTagToVariableMap = new StackMap();
 		}
+        if( fAtBeginVariableMap == null ) {
+            fAtBeginVariableMap = new HashMap();
+        }
+        fAtBeginScopeStack.clear();
+		fAtBeginScopeStack.push( "__root__" ); // need to existing scope for top level customtags
 		fTranslationProblems.clear();
 
 		setCurrentNode(new ZeroStructuredDocumentRegion(fStructuredDocument, 0));
@@ -1192,6 +1229,7 @@
 			appendToBuffer(text.toString(), fUserCode, false, fStructuredDocument.getLastStructuredDocumentRegion());
 		}
 		fTagToVariableMap.clear();
+		fAtBeginVariableMap.clear();
 
 		/*
 		 * Now do the same for jsp:useBean tags, whose contents get their own