Skip to main content
aboutsummaryrefslogblamecommitdiffstats
blob: e1fee7c7f045f851099f4ce5293e1a2bd8879ffd (plain) (tree)

























                                                                                 

                                                 

                                        

                                                                       
                                                

                                                                               





                                                                 
                                                   





























                                                                                                                                
                                                                                                                    



















                                                                                                     
                                                                  








































































                                                                                                                       






                                                                        
























                                                                                                             










































































































                                                                                                             
                                                           
























































































































































                                                                                                                       
                                                


                                                         











                                                                                        





                                                                                             
                                                                                        


































                                                                                           
                                          
                                                                                      



                                                                                                                    














































































                                                                                                                                                                                                                           
         
 


























                                                                                                          

























































































































































                                                                                                                                                                                                                                                                                                                            







                                                                                                                                                       








                                                                                                                                                        









                                                                                                                                                         



























































































































































































































































                                                                                                                                                                                                                                                                                  
                                                                       






                                                                                                                                        
                                                                             
                                                      
                                                                        















                                                                                                                


                                                                                                         































































































                                                                                                                                                                                   
                                                                                        










                                                                       
                                                                                         










                                                                                 


                                                                                                                                 



















































                                                                                                                                            










                                                                                                                                                         







                                                            



























































                                                                                                                                                                                                       
                



                                               


                                                             






































































































































                                                                                                                                                                          




                                                                                                                                















































































































































































































































                                                                                                                                                                                                   


















                                                          



                                                                                     
 

                                                                 





                                              

                                                               





                                                    

                                                                                                 




                                                                 
 
/*******************************************************************************
 * Copyright (c) 2004 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
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.jsp.core.internal.java;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jst.jsp.core.contentmodel.tld.TLDElementDeclaration;
import org.eclipse.jst.jsp.core.contentmodel.tld.TLDVariable;
import org.eclipse.jst.jsp.core.internal.Logger;
import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController;
import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager;
import org.eclipse.wst.common.contentmodel.CMDocument;
import org.eclipse.wst.common.contentmodel.CMNamedNodeMap;
import org.eclipse.wst.common.contentmodel.CMNode;
import org.eclipse.wst.common.contentmodel.modelquery.ModelQuery;
import org.eclipse.wst.sse.core.contentmodel.CMDocumentTracker;
import org.eclipse.wst.sse.core.contentmodel.CMNodeWrapper;
import org.eclipse.wst.sse.core.parser.BlockMarker;
import org.eclipse.wst.sse.core.text.IStructuredDocument;
import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.text.ITextRegion;
import org.eclipse.wst.sse.core.text.ITextRegionCollection;
import org.eclipse.wst.sse.core.text.ITextRegionContainer;
import org.eclipse.wst.sse.core.text.ITextRegionList;
import org.eclipse.wst.sse.core.util.StringUtils;
import org.eclipse.wst.sse.core.util.URIResolver;
import org.eclipse.wst.xml.core.document.XMLModel;
import org.eclipse.wst.xml.core.document.XMLNode;
import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts;
import org.eclipse.wst.xml.core.modelquery.ModelQueryUtil;
import org.eclipse.wst.xml.core.parser.XMLRegionContext;

/**
 * Translates a JSP document into a HttpServlet.
 * Keeps two way mapping from java translation to the original JSP source, which
 * can be obtained through getJava2JspRanges() and getJsp2JavaRanges().
 * 
 * @author pavery
 */
public class JSPTranslator {
	
	public static final String ENDL = "\n"; //$NON-NLS-1$
	
	private String fClassHeader = "public class _JSPServlet extends "; //$NON-NLS-1$
	private String fClassname = "_JSPServlet"; //$NON-NLS-1$
	
	private String fServiceHeader = "public void _jspService(javax.servlet.http.HttpServletRequest request," + //$NON-NLS-1$
				" javax.servlet.http.HttpServletResponse response)\n" + //$NON-NLS-1$
				"\t\tthrows java.io.IOException, javax.servlet.ServletException {\n" + //$NON-NLS-1$
				"javax.servlet.jsp.PageContext pageContext = null;\n" + //$NON-NLS-1$
				"javax.servlet.http.HttpSession session = null;\n" + //$NON-NLS-1$
				"javax.servlet.ServletContext application = null;\n" + //$NON-NLS-1$
				"javax.servlet.ServletConfig config = null;\n" + //$NON-NLS-1$ 
				"javax.servlet.jsp.JspWriter out = null;\n" + //$NON-NLS-1$
				"Object page = null;"; //$NON-NLS-1$

	private String fFooter = "}}"; //$NON-NLS-1$
	private String fException = "Throwable exception = null;"; //$NON-NLS-1$
	public static final String EXPRESSION_PREFIX = "out.print(\"\"+"; //$NON-NLS-1$
	public static final String EXPRESSION_SUFFIX = ");"; //$NON-NLS-1$
	private String fSuperclass = "javax.servlet.http.HttpServlet"; //$NON-NLS-1$

	/** fSourcePosition = position in JSP source*/
	private int fSourcePosition = -1;
	/** fRelativeOffest = offset in the buffer there the cursor is */
	private int fRelativeOffset = -1;
	/** fCursorPosition = offset in the translated java document */
	private int fCursorPosition = -1;
	/** some page directive attributes */
	private boolean fIsErrorPage, fCursorInExpression = false;

	/** user java code in body of the service method */
	private StringBuffer fUserCode = new StringBuffer();
	/** user defined vars declared in the beginning of the class */
	private StringBuffer fUserDeclarations = new StringBuffer();
	/** user defined imports */
	private StringBuffer fUserImports = new StringBuffer();

	private StringBuffer fImports = new StringBuffer(); // imports
	private StringBuffer fResult; // the final traslated java document string buffer
	private StringBuffer fCursorOwner = null; // the buffer where the cursor is

	private XMLModel fStructuredModel = null;
	private IStructuredDocument fStructuredDocument = null;
	private ModelQuery fModelQuery = null;
	//private XMLNode fPositionNode; // position in the DOM
	private IStructuredDocumentRegion fCurrentNode;
	private boolean fInCodeRegion = false; // flag for if cursor is in the current region being translated

	/**
	 * these constants are to keep track of whether the code in question 
	 * is embedded (JSP as an attribute or within comment tags)
	 * or is just standard JSP code, or identifies if it's an expression 
	 */
	protected final static int STANDARD_JSP = 0;
	protected final static int EMBEDDED_JSP = 1;
	protected final static int DECLARATION = 2;
	protected final static int EXPRESSION = 4;
	protected final static int SCRIPTLET = 8;

	/** used to avoid infinite looping include files */
	private Stack fIncludes = null;
	/** mostly for helper classes, so they parse correctly */
	private ArrayList fBlockMarkers = null;
	/** use only one inclue helper per file location */
	private HashMap fJSPIncludeHelperMap = null;
	/** for keeping track of offset in user buffers while document is being built*/
	private int fOffsetInUserImports = 0;
	private int fOffsetInUserDeclarations = 0;
	private int fOffsetInUserCode = 0;

	/** correlates ranges (positions) in java to ranges in jsp */
	private HashMap fJava2JspRanges = new HashMap();

	/** map of ranges in fUserImports (relative to the start of the buffer) to ranges in source JSP buffer. */
	private HashMap fImportRanges = new HashMap();
	/** map of ranges in fUserCode (relative to the start of the buffer) to ranges in source JSP buffer. */
	private HashMap fCodeRanges = new HashMap();
	/** map of ranges in fUserDeclarations (relative to the start of the buffer) to ranges in source JSP buffer. */
	private HashMap fDeclarationRanges = new HashMap();

	private HashMap fUseBeanRanges = new HashMap();
	/** ranges that don't directly map from java code to JSP code (eg. <%@include file="included.jsp"%> */
	private HashMap fIndirectRanges = new HashMap();
	
	private IProgressMonitor fProgressMonitor = null;
	
	/** 
	 * save JSP document text for later use 
	 * may just want to read this from the file or strucdtured document depending what is available
	 * */
	private StringBuffer fJspTextBuffer = new StringBuffer();
	
	/** 
	 * Bits indicates what state the translator is in
	 * use stateMask | MASK to set
	 * use (stateMask & MASK) == MASK to check
	 * USE stateMask 
	 */
	
	/** no state */
	public static final int S_NONE = 0;
	
	/**
	 * configure using an XMLNode
	 * @param node
	 * @param monitor
	 */
	private void configure(XMLNode node, IProgressMonitor monitor) {
		
		fProgressMonitor = monitor;
		fStructuredModel = node.getModel();
		//fPositionNode = node;
		
		fModelQuery = ModelQueryUtil.getModelQuery(node.getOwnerDocument());
		fStructuredDocument = fStructuredModel.getStructuredDocument();
		
		String className = createClassname(node);
		if (className.length() > 0) {
			setClassname(className);
			fClassHeader = "public class " + className + " extends "; //$NON-NLS-1$ //$NON-NLS-2$
		}
	}
	
	/**
	 * memory saving configure (no StructuredDocument in memory)
	 * currently doesn't handle included files
	 * 
	 * @param jspFile
	 * @param monitor
	 */
	private void configure(IFile jspFile, IProgressMonitor monitor) {
		// when configured on a file
		// fStructuredModel, fPositionNode, fModelQuery, fStructuredDocument are all null
		
		fProgressMonitor = monitor;
		
		String className = createClassname(jspFile);
		if (className.length() > 0) {
			setClassname(className);
			fClassHeader = "public class " + className + " extends "; //$NON-NLS-1$ //$NON-NLS-2$
		}
	}

	/**
	 * Set the jsp text from an IFile
	 * @param jspFile
	 */
	private void setJspText(IFile jspFile) {
		try {
			BufferedInputStream in = new BufferedInputStream(jspFile.getContents());
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		    String line = null; 
		    while ((line=reader.readLine()) != null){ 
		    	fJspTextBuffer.append(line);
		    	fJspTextBuffer.append(ENDL);
		    } 
		    reader.close(); 
		}
		catch (CoreException e){
			Logger.logException(e);
		}
		catch(IOException e) {
			Logger.logException(e);
		}
	}
	
	/**
	 * @param node
	 * @return
	 */
	private String createClassname(XMLNode node) {
		
		String classname = ""; //$NON-NLS-1$
		if (node != null) {
			String base = node.getModel().getBaseLocation();
			classname = JSP2ServletNameUtil.mangle(base);
		}
		return classname;
	}

	/**
	 * @param jspFile
	 * @return
	 */
	private String createClassname(IFile jspFile) {
		
		String classname = ""; //$NON-NLS-1$
		if(jspFile != null) {
			classname = JSP2ServletNameUtil.mangle(jspFile.getFullPath().toString());
		}
		return classname;
	}
	
	public void setClassname(String classname) {
		this.fClassname = classname;
	}

	public String getClassname() {
		return this.fClassname != null ? this.fClassname : "GenericJspServlet"; //$NON-NLS-1$
	}

	/**
	 * So that the JSPTranslator can be reused.
	 */
	public void reset(XMLNode node, IProgressMonitor progress) {
		
		// initialize some things on node
		configure(node, progress);
		reset();
		// set the jsp text buffer
		fJspTextBuffer.append(fStructuredDocument.get());
	}
	
	/**
	 * conservative version (no StructuredDocument/Model)
	 * @param jspFile
	 * @param progress
	 */
	public void reset(IFile jspFile, IProgressMonitor progress) {
		
		// initialize some things on node
		configure(jspFile, progress);
		reset();
		// set the jsp text buffer
		setJspText(jspFile);
	}

	/**
	 * Reinitialize some fields
	 */
	private void reset() {
		
		// reset progress monitor
		if (fProgressMonitor != null)
			fProgressMonitor.setCanceled(false);

		// reinit fields
		fSourcePosition = -1;
		fRelativeOffset = -1;
		fCursorPosition = -1;

		fIsErrorPage = fCursorInExpression = false;

		fUserCode = new StringBuffer();
		fUserDeclarations = new StringBuffer();
		fUserImports = new StringBuffer();

		fImports = new StringBuffer(); // imports
		fResult = null;
		fCursorOwner = null; // the buffer where the cursor is

		fCurrentNode = null;
		fInCodeRegion = false; // flag for if cursor is in the current region
							  // being translated

		if (fIncludes != null)
			fIncludes.clear();

		fBlockMarkers = null;

		fJSPIncludeHelperMap = null;

		fOffsetInUserImports = 0;
		fOffsetInUserDeclarations = 0;
		fOffsetInUserCode = 0;

		fJava2JspRanges.clear();
		fImportRanges.clear();
		fCodeRanges.clear();
		fUseBeanRanges.clear();
		fDeclarationRanges.clear();
		fIndirectRanges.clear();
		
		fJspTextBuffer = new StringBuffer();
	}
	
	/**
	 * @return just the "shell" of a servlet, nothing contributed from the JSP doc
	 */
	public final StringBuffer getEmptyTranslation() {
		reset();
		buildResult();
		return getTranslation();
	}
	
	/**
	 * put the final java document together
	 */
	private final void buildResult() {
		// to build the java document this is the order:
		// 
		// + default imports
		// + user imports
		// + class header
		// [+ error page]
		// + user declarations
		// + service method header
		// + user code
		// + service method footer
		fResult = new StringBuffer(fImports.length() + fUserDeclarations.length() + fUserCode.length() + 2048);
		int javaOffset = 0;

		// default imports
		append(fImports);
		javaOffset += fImports.length();
		updateRanges(fImportRanges, javaOffset);
		
		//updateRanges(fIndirectImports, javaOffset);
		// user imports
		append(fUserImports);
		javaOffset += fUserImports.length();

		// class header
		fResult.append(fClassHeader); //$NON-NLS-1$
		javaOffset += fClassHeader.length();
		fResult.append(fSuperclass + "{\n"); //$NON-NLS-1$
		javaOffset += fSuperclass.length() + 2;

		updateRanges(fDeclarationRanges, javaOffset);
		// user declarations
		append(fUserDeclarations);
		javaOffset += fUserDeclarations.length();

		fResult.append(fServiceHeader);
		javaOffset += fServiceHeader.length();
		// error page      
		if (fIsErrorPage) {
			fResult.append(fException);
			javaOffset += fException.length();
		}
		updateRanges(fCodeRanges, javaOffset);
		
		// user code
		append(fUserCode);
		javaOffset += fUserCode.length();

		// footer
		fResult.append(fFooter);
		javaOffset += fFooter.length();

		fJava2JspRanges.putAll(fImportRanges);
		fJava2JspRanges.putAll(fDeclarationRanges);
		fJava2JspRanges.putAll(fCodeRanges);
	}

	/**
	 * @param javaRanges
	 * @param offsetInJava
	 */
	private void updateRanges(HashMap rangeMap, int offsetInJava) {
		// just need to update java ranges w/ the offset we now know
		Iterator it = rangeMap.keySet().iterator();
		while (it.hasNext()) 
			((Position) it.next()).offset += offsetInJava;
	}

	/**
	 * map of ranges (positions) in java document to ranges in jsp document
	 * @return a map of java positions to jsp positions.
	 */
	public HashMap getJava2JspRanges() {
		return fJava2JspRanges;
	}

	/**
	 * map of ranges in jsp document to ranges in java document.
	 * @return a map of jsp positions to java positions, or null if no translation has occured yet
	 * (the map hasn't been built). 
	 */
	public HashMap getJsp2JavaRanges() {
		if (fJava2JspRanges == null)
			return null;
		HashMap flipFlopped = new HashMap();
		Iterator keys = fJava2JspRanges.keySet().iterator();
		Object range = null;
		while (keys.hasNext()) {
			range = keys.next();
			flipFlopped.put(fJava2JspRanges.get(range), range);
		}
		return flipFlopped;
	}
	
	public HashMap getJava2JspImportRanges() {
		return fImportRanges;
	}
	
	public HashMap getJava2JspUseBeanRanges() {
		return fUseBeanRanges;
	}
	
	public HashMap getJava2JspIndirectRanges() {
		return fIndirectRanges;
	}
	
	/**
	 * Adds to the jsp<->java map by default
	 * @param value a comma delimited list of imports
	 */
	protected void addImports(String value) {
		addImports(value, true);
	}
	
	/**
	 * Pass in a comma delimited list of import values,
	 * appends each to the final result buffer
	 * @param value a comma delimited list of imports
	 */
	protected void addImports(String value, boolean addToMap) {
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=81687
		// added the "addToMap" parameter to exclude imports originating
		// from included JSP files to be added to the jsp<->java mapping	
		StringTokenizer st = new StringTokenizer(value, ",", false); //$NON-NLS-1$
		String tok = ""; //$NON-NLS-1$
		String appendage = ""; //$NON-NLS-1$
		while (st.hasMoreTokens()) {
			tok = st.nextToken();
			appendage = "import " + tok + ";" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$
			appendToBuffer(appendage, fUserImports, addToMap, fCurrentNode);
		}
	}

	/**
	 /* keep track of cursor position inside the buffer
	 /* appends buffer to the final result buffer
	 */
	protected void append(StringBuffer buf) {
		if (getCursorOwner() == buf) {
			fCursorPosition = fResult.length() + getRelativeOffset();
		}
		fResult.append(buf.toString());
	}

	/**
	 * Only valid after a configure(...), translate(...) or translateFromFile(...) call
	 * @return the current result (java translation) buffer
	 */
	public final StringBuffer getTranslation() {
		return fResult;
	}
	
	/**
	 * Only valid after a configure(...), translate(...) or translateFromFile(...) call
	 * @return the text in the JSP file
	 */
	public final String getJspText() {
		return fJspTextBuffer.toString();
	}

	/**
	 * adds the variables for a tag in a taglib to the dummy java document
	 * @param tagToAdd is the name of the tag whose variables we want to add
	 */
	protected void addTaglibVariables(String tagToAdd) {
		if (fModelQuery != null) {
			// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5159
			TLDCMDocumentManager docMgr = TaglibController.getTLDCMDocumentManager(fStructuredDocument);
			if (docMgr == null)
				return;
			Iterator taglibs = docMgr.getCMDocumentTrackers(fCurrentNode.getStartOffset()).iterator();
			CMDocument doc = null;
			CMNamedNodeMap elements = null;
			while (taglibs.hasNext()) {
				doc = (CMDocument) taglibs.next();
				CMNode node = null;
				if ((elements = doc.getElements()) != null && (node = elements.getNamedItem(tagToAdd)) != null && node.getNodeType() == CMNode.ELEMENT_DECLARATION) {
					if (node instanceof CMNodeWrapper) {
						node = ((CMNodeWrapper) node).getOriginNode();
					}
					// future_TODO
					// FOR TAGLIB 1.1 STYLE, WE NEED TO INSTANTIATE THE 
					// TagExtraInfo class and get the variables that way			
					// use reflection to create class...
					// VariableInfo[] getVariableInfo(TagData data)
					// THIS IS ONLY FOR TAGLIB 1.2 STYLE .tld files
					List list = ((TLDElementDeclaration) node).getVariables();
					Iterator it = list.iterator();
					while (it.hasNext()) {
						TLDVariable var = (TLDVariable) it.next();
						String varName = var.getNameGiven();
						if (varName == null) {
							varName = var.getNameFromAttribute();
						}
						if (varName != null) {
							String varClass = "java.lang.String"; //$NON-NLS-1$ // the default class...
							if (var.getVariableClass() != null) {
								varClass = var.getVariableClass();
							}
							// add to declarations...
							String newDeclaration = varClass + " " + varName + " = null;" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$
							// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5159
							// not adding to map to avoid strange refactoring behavior
							appendToBuffer(newDeclaration, fUserCode, false, fCurrentNode);
//							fUserCode.append(newDeclaration);
//
//							Position javaRange = new Position(fOffsetInUserCode, newDeclaration.length());
//							// will need to incrememnt offset in user code
//							Position jspRange = new Position(fCurrentNode.getStart(), fCurrentNode.getLength());
//							fCodeRanges.put(javaRange, jspRange);
//							fOffsetInUserCode += newDeclaration.length();

						}
					}
				}
			}
		}
	}

	/*
	 * used by inner helper class (XMLJSPRegionHelper, JSPIncludeRegionHelper)
	 */
	public List getBlockMarkers() {
		if (fBlockMarkers == null)
			fBlockMarkers = new ArrayList();
		return fBlockMarkers;
	}

	/** 
	 /* the main control loop for translating the document, driven by the structuredDocument nodes
	 */
	public void translate() {
		setCurrentNode(fStructuredDocument.getFirstStructuredDocumentRegion());
		
		while (getCurrentNode() != null && !isCanceled()) {
			
			// intercept HTML comment flat node
			// also handles UNDEFINED (which is what CDATA comes in as)
			// basically this part will handle any "embedded" JSP containers
			if (getCurrentNode().getType() == XMLRegionContext.XML_COMMENT_TEXT || getCurrentNode().getType() == XMLRegionContext.XML_CDATA_TEXT || getCurrentNode().getType() == XMLRegionContext.UNDEFINED) {
				translateXMLCommentNode(getCurrentNode());
			}
			else // iterate through each region in the flat node
			{
				translateRegionContainer(getCurrentNode(), STANDARD_JSP);
			}
			if (getCurrentNode() != null)
				advanceNextNode();
		}
		buildResult();
	}

	protected void setDocumentContent(IDocument document, InputStream contentStream, String charset) {
		Reader in = null;
		try {
			in = new BufferedReader(new InputStreamReader(contentStream, charset), 2048);
			StringBuffer buffer = new StringBuffer(2048);
			char[] readBuffer = new char[2048];
			int n = in.read(readBuffer);
			while (n > 0) {
				buffer.append(readBuffer, 0, n);
				n = in.read(readBuffer);
			}
			document.set(buffer.toString());
		}
		catch (IOException x) {
			// ignore
		}
		finally {
			if (in != null) {
				try {
					in.close();
				}
				catch (IOException x) {
					// ignore
				}
			}
		}
	}

	/**
	 * 
	 * @return the status of the translator's progrss monitor, false if the monitor is null
	 */
	private boolean isCanceled() {
		return (fProgressMonitor == null) ? false : fProgressMonitor.isCanceled();
	}

	private void advanceNextNode() {
		setCurrentNode(getCurrentNode().getNext());
		if (getCurrentNode() != null)
			setSourceReferencePoint();
	}

	private void setSourceReferencePoint() {
		if (isJSP(getCurrentNode().getFirstRegion().getType())) {
			Iterator it = getCurrentNode().getRegions().iterator();
			ITextRegion r = null;
			while (it.hasNext()) {
				r = (ITextRegion) it.next();
				if (r.getType() == XMLJSPRegionContexts.JSP_CONTENT || r.getType() == XMLRegionContext.XML_CONTENT)
					break;
				else if (r.getType() == XMLJSPRegionContexts.JSP_DIRECTIVE_NAME)
					break;
				else if (r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE && getCurrentNode().getFullText(r).trim().equals("import")) //$NON-NLS-1$
					break;
			}
		}
	}

	/**
	 * translates a region container (and XML JSP container, or <% JSP container)
	 */
	protected void translateRegionContainer(ITextRegionCollection container, int JSPType) {
		
		ITextRegionCollection containerRegion = container;
		Iterator regions = containerRegion.getRegions().iterator();
		ITextRegion region = null;
		while (regions.hasNext()) {
			region = (ITextRegion) regions.next();
			String type = region.getType();
			// PMR 91930
			// CMVC 241869
			// content assist was not showing up in JSP inside a javascript region
			if (type == XMLRegionContext.BLOCK_TEXT) {
				// check if it's nested jsp in  a script tag...
				if (region instanceof ITextRegionContainer) {
					translateJSPNode(region, regions, type, EMBEDDED_JSP);
				}
				else {
					//////////////////////////////////////////////////////////////////////////////////
					// THIS EMBEDDED JSP TEXT WILL COME OUT LATER WHEN PARTITIONING HAS 
					// SUPPORT FOR NESTED XML-JSP
					// CMVC 241882
					decodeScriptBlock(containerRegion.getFullText(region), containerRegion.getStartOffset());
					//////////////////////////////////////////////////////////////////////////////////
				}
			}
			if (type != null && isJSP(type)) // <%, <%=, <%!, <%@
			{
				translateJSPNode(region, regions, type, JSPType);
			}
			else if (type != null && type == XMLRegionContext.XML_TAG_OPEN) {
				translateXMLNode(containerRegion, regions);
			}
		}
		//}
	}

	/*//////////////////////////////////////////////////////////////////////////////////
	 * ** TEMP WORKAROUND FOR CMVC 241882
	 * Takes a String and blocks out jsp:scriptlet, jsp:expression, and jsp:declaration
	 * @param blockText
	 * @return
	 */
	private void decodeScriptBlock(String blockText, int startOfBlock) {
		XMLJSPRegionHelper helper = new XMLJSPRegionHelper(this);
		helper.addBlockMarker(new BlockMarker("jsp:scriptlet", null, XMLJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
		helper.addBlockMarker(new BlockMarker("jsp:expression", null, XMLJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
		helper.addBlockMarker(new BlockMarker("jsp:declaration", null, XMLJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
		helper.addBlockMarker(new BlockMarker("jsp:directive.include", null, XMLJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
		helper.addBlockMarker(new BlockMarker("jsp:directive.taglib", null, XMLJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
		helper.reset(blockText, startOfBlock);
		// force parse
		helper.forceParse();
		helper.writeToBuffers();
	}

	/*
	 * returns string minus CDATA open and close text
	 */
	final public String stripCDATA(String text) {
		String resultText = ""; //$NON-NLS-1$
		String CDATA_OPEN = "<![CDATA["; //$NON-NLS-1$
		String CDATA_CLOSE = "]]>"; //$NON-NLS-1$
		int start = 0;
		int end = text.length();
		while (start < text.length()) {
			if (text.indexOf(CDATA_OPEN, start) > -1) {
				end = text.indexOf(CDATA_OPEN, start);
				resultText += text.substring(start, end);
				start = end + CDATA_OPEN.length();
			}
			else if (text.indexOf(CDATA_CLOSE, start) > -1) {
				end = text.indexOf(CDATA_CLOSE, start);
				resultText += text.substring(start, end);
				start = end + CDATA_CLOSE.length();
			}
			else {
				end = text.length();
				resultText += text.substring(start, end);
				break;
			}
		}
		return resultText;
	}

	// END OF WORKAROUND CODE...
	/////////////////////////////////////////////////////////////////////////////////////// 
	/**
	 * determines if the type is a pure JSP type (not XML)
	 */
	protected boolean isJSP(String type) {
		return ((type == XMLJSPRegionContexts.JSP_DIRECTIVE_OPEN || type == XMLJSPRegionContexts.JSP_EXPRESSION_OPEN || type == XMLJSPRegionContexts.JSP_DECLARATION_OPEN || type == XMLJSPRegionContexts.JSP_SCRIPTLET_OPEN || type == XMLJSPRegionContexts.JSP_CONTENT) && type != XMLRegionContext.XML_TAG_OPEN);
		// checking XML_TAG_OPEN so <jsp:directive.xxx/> gets treated like other XML jsp tags
	}

	/**
	 * translates the various XMLJSP type nodes
	 * @param regions the regions of the XMLNode
	 */
	protected void translateXMLNode(ITextRegionCollection container, Iterator regions) {
		// contents must be valid XHTML, translate escaped CDATA into what it really is...
		ITextRegion r = null;
 		if (regions.hasNext()) {
			r = (ITextRegion) regions.next();
			if (r.getType() == XMLRegionContext.XML_TAG_NAME || r.getType() == XMLJSPRegionContexts.JSP_DIRECTIVE_NAME) // <jsp:directive.xxx comes in as this
			{
				String fullTagName = container.getFullText(r).trim();
				if (fullTagName.indexOf(':') > -1) {
					addTaglibVariables(fullTagName); // it may be a taglib
				}
				StringTokenizer st = new StringTokenizer(fullTagName, ":.", false); //$NON-NLS-1$
				if (st.hasMoreTokens() && st.nextToken().equals("jsp")) //$NON-NLS-1$
				{
					if (st.hasMoreTokens()) {
						String jspTagName = st.nextToken();
						if (jspTagName.equals("useBean")) //$NON-NLS-1$
						{
							advanceNextNode(); // get the content
							if (getCurrentNode() != null) {
								translateUseBean(container); // 'regions' should be all the useBean attributes
							}
						}
						else if (jspTagName.equals("scriptlet")) //$NON-NLS-1$
						{	
							// <jsp:scriptlet>scriptlet content...</jsp:scriptlet>
							IStructuredDocumentRegion sdr = getCurrentNode().getNext();
							if(sdr != null) {
								translateScriptletString(sdr.getText(), sdr, sdr.getStartOffset(), sdr.getEndOffset());
							}
							advanceNextNode();
						}
						else if (jspTagName.equals("expression")) //$NON-NLS-1$
						{
							// <jsp:expression>expression content...</jsp:expression>
							IStructuredDocumentRegion sdr = getCurrentNode().getNext();
							if(sdr != null) {
								translateExpressionString(sdr.getText(), sdr, sdr.getStartOffset(), sdr.getEndOffset());
							}
							advanceNextNode();
						}
						else if (jspTagName.equals("declaration")) //$NON-NLS-1$
						{
							// <jsp:declaration>declaration content...</jsp:declaration>
							IStructuredDocumentRegion sdr = getCurrentNode().getNext();
							if(sdr != null) {
								translateDeclarationString(sdr.getText(), sdr, sdr.getStartOffset(), sdr.getEndOffset());
								
							}
							advanceNextNode();
						}
						else if (jspTagName.equals("directive")) //$NON-NLS-1$
						{
							if (st.hasMoreTokens()) {
								String directiveName = st.nextToken();
								if (directiveName.equals("taglib")) { //$NON-NLS-1$
									handleTaglib();
									return;
								}
								else if (directiveName.equals("include")) { //$NON-NLS-1$
									
									String fileLocation = ""; //$NON-NLS-1$
									String attrValue = ""; //$NON-NLS-1$
									// CMVC 258311
									// PMR 18368, B663
									// skip to required "file" attribute, should be safe because
									// "file" is the only attribute for the include directive
									while (r != null && regions.hasNext() && !r.getType().equals(XMLRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
										r = (ITextRegion) regions.next();
									}
									attrValue = getAttributeValue(r, regions);
									if (attrValue != null)
										handleIncludeFile(fileLocation);
								}
								else if (directiveName.equals("page")) { //$NON-NLS-1$
									
									// 20040702 commenting this out
									// bad if currentNode is referenced after here w/ the current list
									// see: https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3035
									// setCurrentNode(getCurrentNode().getNext());
									if (getCurrentNode() != null) {
										translatePageDirectiveAttributes(regions); // 'regions' are attributes for the directive
									}
								}
							}
						}
						else if(jspTagName.equals("include")) { //$NON-NLS-1$
							
							// <jsp:include page="filename") />
							while(regions.hasNext()) {
								r = (ITextRegion)regions.next();
								if(r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME && getCurrentNode().getText(r).equals("page")) { //$NON-NLS-1$
									String filename = getAttributeValue(r, regions);
									handleIncludeFile(filename);
									break;
								}
							}		
						}
					}
				}
				else {
					// tag name is not jsp
					// handle embedded jsp attributes...
					ITextRegion embedded = null;
					Iterator attrRegions = null;
					ITextRegion attrChunk = null;
					while (regions.hasNext()) {
						embedded = (ITextRegion) regions.next();
						if (embedded instanceof ITextRegionContainer) {
							// parse out container
							attrRegions = ((ITextRegionContainer) embedded).getRegions().iterator();
							while (attrRegions.hasNext()) {
								attrChunk = (ITextRegion) attrRegions.next();
								String type = attrChunk.getType();
								// CMVC 263661, embedded JSP in attribute support
								// only want to translate one time per embedded region
								// so we only translate on the JSP open tags (not content)
								if (type == XMLJSPRegionContexts.JSP_EXPRESSION_OPEN || type == XMLJSPRegionContexts.JSP_SCRIPTLET_OPEN || type == XMLJSPRegionContexts.JSP_DECLARATION_OPEN || type == XMLJSPRegionContexts.JSP_DIRECTIVE_OPEN) {
									// now call jsptranslate
									//System.out.println("embedded jsp OPEN >>>> " + ((ITextRegionContainer)embedded).getText(attrChunk));
									translateEmbeddedJSPInAttribute((ITextRegionContainer) embedded);
								}
							}
						}
					}
				}
			}
		}
	}

	/**
	 * goes through comment regions, checks if any are an embedded JSP container
	 * if it finds one, it's sends the container into the translation routine
	 */
	protected void translateXMLCommentNode(IStructuredDocumentRegion node) {
		Iterator it = node.getRegions().iterator();
		ITextRegion commentRegion = null;
		while (it != null && it.hasNext()) {
			commentRegion = (ITextRegion) it.next();
			if (commentRegion instanceof ITextRegionContainer) {
				translateRegionContainer((ITextRegionContainer) commentRegion, EMBEDDED_JSP); // it's embedded jsp...iterate regions...
			}
		}
	}

	/**
	 * determines which type of JSP node to translate
	 */
	protected void translateJSPNode(ITextRegion region, Iterator regions, String type, int JSPType) {
		if (type == XMLJSPRegionContexts.JSP_DIRECTIVE_OPEN && regions != null) {
			translateDirective(regions);
		}
		else {
			ITextRegionCollection contentRegion = null;
			if (JSPType == STANDARD_JSP && (setCurrentNode(getCurrentNode().getNext())) != null) {
				contentRegion = getCurrentNode();
			}
			else if (JSPType == EMBEDDED_JSP && region instanceof ITextRegionCollection) {
				// CMVC 263661
				translateEmbeddedJSPInBlock((ITextRegionCollection) region);
				// ensure the rest of this method won't be called
				contentRegion = null;
			}
			if (contentRegion != null) {
				if (type == XMLJSPRegionContexts.JSP_EXPRESSION_OPEN) {
					translateExpression(contentRegion);
				}
				else if (type == XMLJSPRegionContexts.JSP_DECLARATION_OPEN) {
					translateDeclaration(contentRegion);
				}
				else if (type == XMLJSPRegionContexts.JSP_CONTENT || type == XMLJSPRegionContexts.JSP_SCRIPTLET_OPEN) {
					translateScriptlet(contentRegion);
				}
			}
			else {
				// this is the case of an attribute w/ no region <p align="<%%>">
				setCursorOwner(getJSPTypeForRegion(region));
			}
		}
	}

	/**
	 * Pass the ITextRegionCollection which is the embedded region
	 * @param iterator
	 */
	private void translateEmbeddedJSPInBlock(ITextRegionCollection collection) {
		Iterator regions = collection.getRegions().iterator();
		ITextRegion region = null;
		while (regions.hasNext()) {
			region = (ITextRegion) regions.next();
			if (isJSP(region.getType()))
				break;
			region = null;
		}
		if (region != null) {
			translateEmbeddedJSPInAttribute(collection);
		}
	}

	/*
	 * for example: 
	 * <a href="index.jsp?p=<%=abc%>b=<%=xyz%>">abc</a>
	 */
	private void translateEmbeddedJSPInAttribute(ITextRegionCollection embeddedContainer) {
		// THIS METHOD IS A FIX FOR CMVC 263661 (jsp embedded in attribute regions)
		
		// loop all regions
		ITextRegionList embeddedRegions = embeddedContainer.getRegions();
		ITextRegion delim = null;
		ITextRegion content = null;
		String type = null;
		for(int i=0; i<embeddedRegions.size(); i++) {
			
			// possible delimiter, check later
			delim = embeddedRegions.get(i);
			type = delim.getType();
			
			// check next region to see if it's content
			if(i+1<embeddedRegions.size()) {
				if(embeddedRegions.get(i+1).getType() == XMLJSPRegionContexts.JSP_CONTENT)
					content = embeddedRegions.get(i+1);
			}
			
			if(content != null) {
				int contentStart =  embeddedContainer.getStartOffset(content);
				int rStart = fCurrentNode.getStartOffset() + contentStart;
				int rEnd = fCurrentNode.getStartOffset() + embeddedContainer.getEndOffset(content);
				
				boolean inThisRegion = rStart <= fSourcePosition && rEnd >= fSourcePosition;
				//int jspPositionStart = fCurrentNode.getStartOffset() + contentStart;
				
				if(type == XMLJSPRegionContexts.JSP_EXPRESSION_OPEN) {
					fLastJSPType = EXPRESSION;
					translateExpressionString(embeddedContainer.getText(content), fCurrentNode, contentStart, content.getLength());
				}
				else if(type == XMLJSPRegionContexts.JSP_SCRIPTLET_OPEN) {
					fLastJSPType = SCRIPTLET;
					translateScriptletString(embeddedContainer.getText(content), fCurrentNode, contentStart, content.getLength());
				}
				else if(type == XMLJSPRegionContexts.JSP_DECLARATION_OPEN) {
					fLastJSPType = DECLARATION;
					translateDeclarationString(embeddedContainer.getText(content), fCurrentNode, contentStart, content.getLength());
				}
				
				// calculate relative offset in buffer
				if (inThisRegion) {
					setCursorOwner(fLastJSPType);
					int currentBufferLength = getCursorOwner().length();
					setRelativeOffset((fSourcePosition - contentStart) + currentBufferLength);
					if (fLastJSPType == EXPRESSION) {
						// if an expression, add then length of the enclosing paren..
						setCursorInExpression(true);
						setRelativeOffset(getRelativeOffset() + EXPRESSION_PREFIX.length());
					}
				}
			}
			else {
				type = null;
				content = null;
			}
		}
	}

	private int fLastJSPType = SCRIPTLET;

	/**
	 * JSPType is only used internally in this class to describe tye type of region to be translated
	 * @param region
	 * @return int
	 */
	private int getJSPTypeForRegion(ITextRegion region) {
		String regionType = region.getType();
		int type = SCRIPTLET;
		if (regionType == XMLJSPRegionContexts.JSP_SCRIPTLET_OPEN)
			type = SCRIPTLET;
		else if (regionType == XMLJSPRegionContexts.JSP_EXPRESSION_OPEN)
			type = EXPRESSION;
		else if (regionType == XMLJSPRegionContexts.JSP_DECLARATION_OPEN)
			type = DECLARATION;
		else if (regionType == XMLJSPRegionContexts.JSP_CONTENT)
			type = fLastJSPType;
		// remember the last type, in case the next type that comes in is JSP_CONTENT
		fLastJSPType = type;
		return type;
	}

	/**
	 /* <%@ %>
	 /* need to pass in the directive tag region
	 */
	protected void translateDirective(Iterator regions) {
		ITextRegion r = null;
		String regionText, attrValue = ""; //$NON-NLS-1$
		while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == XMLJSPRegionContexts.JSP_DIRECTIVE_NAME) { // could be XML_CONTENT = "", skips attrs?
			regionText = getCurrentNode().getText(r);
			if (regionText.indexOf("taglib") > -1) { //$NON-NLS-1$
				// add custom tag block markers here
				handleTaglib();
				return;
			}
			else if (regionText.equals("include")) { //$NON-NLS-1$
				String fileLocation = ""; //$NON-NLS-1$
				// CMVC 258311
				// PMR 18368, B663
				// skip to required "file" attribute, should be safe because
				// "file" is the only attribute for the include directive
				while (r != null && regions.hasNext() && !r.getType().equals(XMLRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
					r = (ITextRegion) regions.next();
				}
				fileLocation = getAttributeValue(r, regions);
				if (attrValue != null)
					handleIncludeFile(fileLocation);
			}
			else if (regionText.indexOf("page") > -1) { //$NON-NLS-1$
				translatePageDirectiveAttributes(regions);
			}
		}
	}

	/*
	 * This method should ideally only be called once per run through JSPTranslator
	 * This is intended for use by inner helper classes that need to add block markers to their own parsers.
	 * This method only adds markers that came from <@taglib> directives, (not <@include>),
	 * since include file taglibs are handled on the fly when they are encountered.
	 * * @param regions
	 */
	protected void handleTaglib() {
		// get/create TLDCMDocument
		TLDCMDocumentManager mgr = TaglibController.getTLDCMDocumentManager(fStructuredDocument);
		if (mgr != null) {
			List trackers = mgr.getCMDocumentTrackers(getCurrentNode().getEnd());
			Iterator it = trackers.iterator();
			CMDocumentTracker tracker = null;
			Iterator taglibRegions = null;
			IStructuredDocumentRegion sdRegion = null;
			ITextRegion r = null;
			while (it.hasNext()) {
				tracker = (CMDocumentTracker) it.next();
				sdRegion = tracker.getStructuredDocumentRegion();
				taglibRegions = sdRegion.getRegions().iterator();
				while (taglibRegions.hasNext()) {
					r = (ITextRegion) taglibRegions.next();
					if (r.getType().equals(XMLJSPRegionContexts.JSP_DIRECTIVE_NAME)) {
						if (sdRegion.getText(r).equals("taglib")) { //$NON-NLS-1$
							addBlockMarkers(tracker.getDocument());
						}
					}
				}
			}
		}
	}

	/*
	 * adds block markers to JSPTranslator's block marker list for all elements in doc
	 * @param doc
	 */
	protected void addBlockMarkers(CMDocument doc) {
		if (doc.getElements().getLength() > 0) {
			Iterator elements = doc.getElements().iterator();
			CMNode node = null;
			while (elements.hasNext()) {
				node = (CMNode) elements.next();
				getBlockMarkers().add(new BlockMarker(node.getNodeName(), null, XMLJSPRegionContexts.JSP_CONTENT, true));
			}
		}
	}

	/**
	 * If r is an attribute name region, this method will safely return the value for that attribute.
	 * @param r
	 * @param remainingRegions
	 * @return the value for the attribute name (r), or null if isn't one
	 */
	protected String getAttributeValue(ITextRegion r, Iterator remainingRegions) {
		if (r.getType().equals(XMLRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
			if (remainingRegions.hasNext() && (r = (ITextRegion) remainingRegions.next()) != null && r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
				if (remainingRegions.hasNext() && (r = (ITextRegion) remainingRegions.next()) != null && r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
					// handle include for the filename
					return StringUtils.stripQuotes(getCurrentNode().getText(r));
				}
			}
		}
		return null;
	}

	/**
	 * takes an emnumeration of the attributes of a directive tag
	 */
	protected void translatePageDirectiveAttributes(Iterator regions) {
		ITextRegion r = null;
		String attrName, attrValue;
		// iterate all attributes
		while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() != XMLJSPRegionContexts.JSP_CLOSE) {
			attrName = attrValue = null;
			if (r.getType().equals(XMLRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
				
				attrName = getCurrentNode().getText(r).trim();
				if(attrName.length() > 0) {
					if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
						if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
							
							attrValue = StringUtils.strip(getCurrentNode().getText(r));
						}
						// has equals, but no value?
					}
					setDirectiveAttribute(attrName, attrValue);
				}
			}
		}
	}

	/**
	 * sets the appropriate page directive attribute
	 */
	protected void setDirectiveAttribute(String attrName, String attrValue) {
		if (attrValue == null)
			return; // uses default (if there was one)
		if (attrName.equals("extends")) //$NON-NLS-1$
		{
			fSuperclass = attrValue;
		}
		else if (attrName.equals("import")) //$NON-NLS-1$
		{
			addImports(attrValue);
		}
		else if (attrName.equals("session")) //$NON-NLS-1$
		{
			//fSession = ("true".equalsIgnoreCase(attrValue)); //$NON-NLS-1$
		}
		else if (attrName.equals("buffer")) //$NON-NLS-1$
		{
			// ignore for now
		}
		else if (attrName.equals("autoFlush")) //$NON-NLS-1$
		{
			// ignore for now
		}
		else if (attrName.equals("isThreadSafe")) //$NON-NLS-1$
		{
			//fThreadSafe = "true".equalsIgnoreCase(attrValue); //$NON-NLS-1$
		}
		else if (attrName.equals("isErrorPage")) //$NON-NLS-1$
		{
			fIsErrorPage = Boolean.valueOf(attrValue).booleanValue();
		}
	}

	protected void handleIncludeFile(String filename) {
		if (filename != null) {
			String fileLocation = null;
			if (getResolver() != null) {
				fileLocation = (getIncludes().empty()) 
                                    ? getResolver().getLocationByURI(StringUtils.strip(filename)) 
                                    : getResolver().getLocationByURI(StringUtils.strip(filename), (String) getIncludes().peek());
			}
			else {
				// shouldn't happen
				fileLocation = StringUtils.strip(filename);
			}
			// hopefully, a resolver is present and has returned a canonical file path
			if (!getIncludes().contains(fileLocation) && getBaseLocation() != null && !fileLocation.equals(getBaseLocation())) {
				getIncludes().push(fileLocation);
				JSPIncludeRegionHelper helper = getIncludesHelper(fileLocation);
				helper.parse(fileLocation);
				helper.writeToBuffers();
				getIncludes().pop();
			}
		}
	}

	/*
	 * one helper per fileLocation
	 */
	protected JSPIncludeRegionHelper getIncludesHelper(String fileLocation) {
		// lazy creation
		if (fJSPIncludeHelperMap == null) {
			fJSPIncludeHelperMap = new HashMap();
		}
		JSPIncludeRegionHelper helper = (JSPIncludeRegionHelper) fJSPIncludeHelperMap.get(fileLocation);
		if (helper == null) {
			helper = new JSPIncludeRegionHelper(this);
			fJSPIncludeHelperMap.put(fileLocation, helper);
		}
		return helper;
	}

	private URIResolver getResolver() {
		return (fStructuredModel != null) ? fStructuredModel.getResolver() : null;
	}

	/**
	 * 
	 * @return java.lang.String
	 */
	private String getBaseLocation() {
		if (getResolver() == null)
			return null;
		return getResolver().getFileBaseLocation();
	}

	private Stack getIncludes() {
		if (fIncludes == null)
			fIncludes = new Stack();
		return fIncludes;
	}

	protected void translateExpressionString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength) {
		appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, embeddedContainer);
		appendToBuffer(newText, fUserCode, true, embeddedContainer, jspPositionStart, jspPositionLength);
		appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, embeddedContainer);
	}

	protected void translateDeclarationString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength) {
		appendToBuffer(newText, fUserDeclarations, true, embeddedContainer, jspPositionStart, jspPositionLength);
		appendToBuffer(ENDL, fUserDeclarations, false, embeddedContainer);
	}

	/**
	 * used by XMLJSPRegionHelper for included JSP files
	 * 
	 * @param newText
	 * @param embeddedContainer
	 * @param jspPositionStart
	 * @param jspPositionLength
	 */
	protected void translateScriptletString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength) {
		appendToBuffer(newText, fUserCode, true, embeddedContainer, jspPositionStart, jspPositionLength);
	}

	// the following 3 methods determine the cursor position
	// <%= %>
	protected void translateExpression(ITextRegionCollection region) {
		String newText = getUnescapedRegionText(region, EXPRESSION);
		appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, fCurrentNode);
		appendToBuffer(newText, fUserCode, true, fCurrentNode);
		appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, fCurrentNode);
	}

	//
	// <%! %>
	protected void translateDeclaration(ITextRegionCollection region) {
		String newText = getUnescapedRegionText(region, DECLARATION);
		appendToBuffer(newText, fUserDeclarations, true, fCurrentNode);
		appendToBuffer(ENDL, fUserDeclarations, false, fCurrentNode);
	}

	//
	// <% %>
	protected void translateScriptlet(ITextRegionCollection region) {
		String newText = getUnescapedRegionText(region, SCRIPTLET);
		appendToBuffer(newText, fUserCode, true, fCurrentNode);
	}

	/**
	 * Append using a region, probably indirect mapping (eg. <%@page include=""%>)
	 * @param newText
	 * @param buffer
	 * @param addToMap
	 * @param jspReferenceRegion
	 */
	private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion) {
	 	int start = 0, length = 0;
	 	if(jspReferenceRegion != null) {
	 		start = jspReferenceRegion.getStartOffset();
	 		length = jspReferenceRegion.getLength();
	 	}
	 	appendToBuffer(newText, buffer, addToMap, jspReferenceRegion, start, length, false);
	}
	
	private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion, int jspPositionStart, int jspPositionLength) {
		appendToBuffer(newText, buffer, addToMap, jspReferenceRegion, jspPositionStart, jspPositionLength, true);
	}
	 
	/**
	 * Adds newText to the buffer passed in, and adds to translation mapping as specified by the addToMap flag.
	 * some special cases to consider (that may be affected by changes to this method): 
	 * included files
	 * scriplets in an attribute value
	 * refactoring
	 * 
	 * @param newText
	 * @param buffer
	 * @param addToMap
	 */
	private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
		
		// nothing to append
		if (jspReferenceRegion == null)
			return;
		
		// add a newline so translation looks cleaner
		newText += ENDL;
		
		if (buffer == fUserCode) {
			buffer.append(newText);
			if (addToMap) {
				if (isUsebeanTag(jspReferenceRegion)) {
					try {
						// requires special mapping
						appendUseBeanToBuffer(newText, jspReferenceRegion, isIndirect);
					}
					catch (Exception e){
						// still working out kinks
						Logger.logException(e);
					}
				}
				else {
					// all other cases
					Position javaRange = new Position(fOffsetInUserCode, newText.length());
					Position jspRange = new Position(jspPositionStart, jspPositionLength);
					
					fCodeRanges.put(javaRange, jspRange);
					if(isIndirect)
						fIndirectRanges.put(javaRange, jspRange);
				}
			}
			fOffsetInUserCode += newText.length();
		}
		else if (buffer == fUserDeclarations) {
			buffer.append(newText);
			if (addToMap) {
				Position javaRange = new Position(fOffsetInUserDeclarations, newText.length());
				Position jspRange = new Position(jspPositionStart, jspPositionLength);
				
				fDeclarationRanges.put(javaRange, jspRange);
				if(isIndirect)
					fIndirectRanges.put(javaRange, jspRange);
			}
			fOffsetInUserDeclarations += newText.length();
		}
		else if (buffer == fUserImports) {
			buffer.append(newText);
			if (addToMap) {
				appendImportToBuffer(jspReferenceRegion, isIndirect);
			}
			fOffsetInUserImports += newText.length();
		}
	}

	/**
	 * @param jspReferenceRegion
	 * @return
	 */
	private boolean isUsebeanTag(ITextRegionCollection jspReferenceRegion) {
		ITextRegionList regions = jspReferenceRegion.getRegions();
		ITextRegion r = null;
		boolean isUseBean = false;
		for (int i = 0; i < regions.size(); i++) {
			r = regions.get(i);
			if (r.getType() == XMLRegionContext.XML_TAG_NAME && jspReferenceRegion.getText(r).equals("jsp:useBean")) { //$NON-NLS-1$
				isUseBean = true;
				break;
			}
		}
		return isUseBean;
	}

	/**
	 * @param newText
	 * @param jspReferenceRegion
	 */
	private void appendImportToBuffer(ITextRegionCollection jspReferenceRegion, boolean isIndirect) {
		// these positions will be updated below
		// 7 is length of "import "
		Position javaRange = new Position(fOffsetInUserImports + 7, 1);
		Position jspRange = new Position(jspReferenceRegion.getStart(), jspReferenceRegion.getLength());
		
		// calculate JSP range by finding "import" attribute
		ITextRegionList regions = jspReferenceRegion.getRegions();
		int size = regions.size();
		ITextRegion r = null;
		for (int i = 0; i < size; i++) {
			r = regions.get(i);
			if(r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME)
				if(jspReferenceRegion.getText(r).trim().equals("import")) { //$NON-NLS-1$
					// get the attr value region
					if(size > i+2) {
						r = regions.get(i+2);
						if(r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
							// use the length of the value (minus the quotes)
							// won't work for multiple
							String importText = jspReferenceRegion.getText(r);
							int quoteOffset = (importText.startsWith("\"") || importText.startsWith("'")) ? 1 : 0; //$NON-NLS-1$ //$NON-NLS-2$
							String strippedText = StringUtils.stripQuotesLeaveInsideSpace(importText);
							jspRange = new Position(jspReferenceRegion.getStartOffset(r)+quoteOffset, strippedText.length());
							// set java range length
							javaRange.setLength(strippedText.length());
							break;
						}
					}
				}
		}

		// put ranges in java -> jsp range map
		fImportRanges.put(javaRange, jspRange);
		if(isIndirect)
			fIndirectRanges.put(javaRange, jspRange);
	}

	/**
	 * temp fix for 282295 until better mapping is in place
	 * @param newText
	 * @param jspReferenceRegion
	 */
	private void appendUseBeanToBuffer(String newText, ITextRegionCollection jspReferenceRegion, boolean isIndirect) throws Exception {
		// java string looks like this (tokenized)
		// Type id = new Classname();\n
		// 0    1  2 3   4
		// or
		// Type id = null;\n // if there is no classname
		// 0    1  2 3
		
		//----------------------
		// calculate java ranges
		//----------------------
		StringTokenizer st = new StringTokenizer(newText, " ", false); //$NON-NLS-1$
		int i = 0;
		String[] parsedJava = new String[st.countTokens()];
		while (st.hasMoreTokens())
			parsedJava[i++] = st.nextToken();

		String type = parsedJava[0] != null ? parsedJava[0] : ""; //$NON-NLS-1$
		String id = parsedJava[1] != null ? parsedJava[1] : ""; //$NON-NLS-1$
		String className = parsedJava.length > 4 ? parsedJava[4] : ""; //$NON-NLS-1$

		Position javaTypeRange = new Position(fOffsetInUserCode, type.length());
		Position javaIdRange = new Position(fOffsetInUserCode + type.length() + 1, id.length());
		Position javaClassRange = new Position(fOffsetInUserCode + type.length() + 1 + id.length() + 7, 0);
		if(className.length() >= 4) {
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=86132
            int classNameLength = className.substring(0,className.indexOf('(')).length();
			javaClassRange = new Position(fOffsetInUserCode + type.length() + 1 + id.length() + 7, classNameLength);
        }

		//---------------------
		// calculate jsp ranges
		//---------------------
		ITextRegionList regions = jspReferenceRegion.getRegions();
		ITextRegion r = null;
		String attrName = "", attrValue = ""; //$NON-NLS-1$ //$NON-NLS-2$
		int quoteOffset = 0;
		Position jspTypeRange = null;
		Position jspIdRange = null;
		Position jspClassRange = null;

		for (int j = 0; j < regions.size(); j++) {
			r = regions.get(j);
			if (r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) {
				attrName = jspReferenceRegion.getText(r);
				if (regions.size() >= j + 2 && regions.get(j + 2).getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
					// get attr value
					r = regions.get(j + 2);
					attrValue = jspReferenceRegion.getText(r);
					
					// may have quotes
					quoteOffset = (attrValue.startsWith("\"") || attrValue.startsWith("'")) ? 1 : 0; //$NON-NLS-1$ //$NON-NLS-2$
					
					if (attrName.equals("type")) //$NON-NLS-1$
						jspTypeRange = new Position(jspReferenceRegion.getStartOffset(r) + quoteOffset, StringUtils.stripQuotesLeaveInsideSpace(attrValue).length());
					else if (attrName.equals("id")) //$NON-NLS-1$
						jspIdRange = new Position(jspReferenceRegion.getStartOffset(r) + quoteOffset, StringUtils.stripQuotesLeaveInsideSpace(attrValue).length());
					else if (attrName.equals("class"))  //$NON-NLS-1$
						jspClassRange = new Position(jspReferenceRegion.getStartOffset(r) + quoteOffset, StringUtils.stripQuotesLeaveInsideSpace(attrValue).length());
				}
			}
		}
		
		// put ranges in java -> jsp range map
		if (!type.equals("") && jspTypeRange != null) { //$NON-NLS-1$
			fCodeRanges.put(javaTypeRange, jspTypeRange);
			// note: don't update offsets for this map when result is built
			// they'll be updated when code ranges offsets are updated
			fUseBeanRanges.put(javaTypeRange, jspTypeRange);
			if(isIndirect)
				fIndirectRanges.put(javaTypeRange, jspTypeRange);
		}
		if (!id.equals("") && jspIdRange != null) { //$NON-NLS-1$
			fCodeRanges.put(javaIdRange, jspIdRange);
			// note: don't update offsets for this map when result is built
			// they'll be updated when code ranges offsets are updated
			fUseBeanRanges.put(javaIdRange, jspTypeRange);
			if(isIndirect)
				fIndirectRanges.put(javaIdRange, jspTypeRange);
		}
		if (!className.equals("") && jspClassRange != null) { //$NON-NLS-1$
			fCodeRanges.put(javaClassRange, jspClassRange);
			// note: don't update offsets for this map when result is built
			// they'll be updated when code ranges offsets are updated
			fUseBeanRanges.put(javaClassRange, jspTypeRange);
			if(isIndirect)
				fIndirectRanges.put(javaClassRange, jspTypeRange);
		}
	}

	/**
	 * Set the buffer to the current JSPType:
	 * STANDARD_JSP, EMBEDDED_JSP, DECLARATION, EXPRESSION, SCRIPTLET
	 * (for keepting track of cursor position when the final document is built)
	 * @param JSPType the JSP type that the cursor is in
	 */
	protected void setCursorOwner(int JSPType) {
		switch (JSPType) {
			case DECLARATION :
				setCursorOwner(fUserDeclarations);
				break;
			case EXPRESSION :
			case SCRIPTLET :
				setCursorOwner(fUserCode);
				break;
			default :
				setCursorOwner(fUserCode);
		}
	}

	/**
	 * this piece of code iterates through fCurrentNodes
	 * and clumps them together in a big text string
	 * - unescaping characters if they are not CDATA
	 * - simply appending if they are CDATA
	 * it stops iteration when it hits a node that is an XML_TAG_NAME (which should be the region closing tag)
	 */
	protected String getUnescapedRegionText(ITextRegionCollection stRegion, int JSPType) {
		StringBuffer buffer = new StringBuffer();
		int start = stRegion.getStartOffset();
		int end = stRegion.getEndOffset();
		// adjustment necessary for embedded region containers
		if (stRegion instanceof ITextRegionContainer && stRegion.getType() == XMLRegionContext.BLOCK_TEXT) {
			if (stRegion.getRegions() != null && stRegion.getRegions().size() > 1) {
				ITextRegion jspContent = stRegion.getRegions().get(1); // should be jspContent region
				start = stRegion.getStartOffset(jspContent);
				end = stRegion.getEndOffset(jspContent);
			}
		}
		int CDATAOffset = 0; // number of characters lost in conversion
		int bufferSize = 0;
		if (stRegion.getType() == XMLJSPRegionContexts.JSP_CONTENT || stRegion.getType() == XMLRegionContext.BLOCK_TEXT // need this for embedded JSP regions
					|| stRegion.getType() == XMLRegionContext.XML_TAG_NAME) // need this in case there's no region...
		{
			fInCodeRegion = (start <= fSourcePosition && fSourcePosition <= end);
			if (fInCodeRegion) {
				setCursorOwner(JSPType);
				setRelativeOffset((fSourcePosition - start) + getCursorOwner().length());
				if (JSPType == EXPRESSION) {
					// if an expression, add then length of the enclosing paren..
					setCursorInExpression(true);
					setRelativeOffset(getRelativeOffset() + EXPRESSION_PREFIX.length());
				}
			}
			ITextRegion jspContent = null;
			if (stRegion.getRegions() != null && stRegion.getRegions().size() > 1)
				jspContent = stRegion.getRegions().get(1);
			return (jspContent != null) ? stRegion.getFullText(jspContent) : stRegion.getFullText(); // don't unescape if it's not an XMLJSP tag
		}
		else if (stRegion.getType() == XMLJSPRegionContexts.JSP_CLOSE) {
			// need to determine cursor owner so that the fCurosorPosition will be
			// correct even if there is no region after the cursor in the JSP file
			setCursorOwner(JSPType);
		}
		// iterate XMLCONTENT and CDATA regions
		// loop fCurrentNode until you hit </jsp:scriptlet> (or other closing tag name)
		while (getCurrentNode() != null && getCurrentNode().getType() != XMLRegionContext.XML_TAG_NAME) // need to stop on the ending tag name...
		{
			start = getCurrentNode().getStartOffset();
			end = getCurrentNode().getEndOffset();
			bufferSize = buffer.length();
			CDATAOffset = unescapeRegion(getCurrentNode(), buffer);
			fInCodeRegion = (start <= fSourcePosition && fSourcePosition <= end);
			if (fInCodeRegion) {
				setCursorOwner(JSPType);
				// this offset is sort of complicated...
				// it's composed of:
				// 1. the length of the start of the current region up till where the cursor is
				// 2. minus the number of characters lost in CDATA translation
				// 3. plus the length of the escaped buffer before the current region, but 
				//    is still within the jsp tag
				setRelativeOffset((fSourcePosition - getCurrentNode().getStartOffset()) + getCursorOwner().length() - CDATAOffset + bufferSize);
				if (JSPType == EXPRESSION) {
					setCursorInExpression(true);
					// if an expression, add then length of the enclosing paren..
					setRelativeOffset(getRelativeOffset() + EXPRESSION_PREFIX.length());
				}
			}
			if (getCurrentNode() != null)
				advanceNextNode();
		}
		return buffer.toString();
	}

	/**
	 * @param r the region to be unescaped (XMLContent, XML ENTITY REFERENCE, or CDATA)
	 * @param sb the stringbuffer to append the text to
	 * @return the number of characters removed in unescaping this text
	 */
	protected int unescapeRegion(ITextRegion r, StringBuffer sb) {
		String s = ""; //$NON-NLS-1$
		int lengthBefore = 0, lengthAfter = 0, cdata_tags_length = 0;
		if (r != null && (r.getType() == XMLRegionContext.XML_CONTENT || r.getType() == XMLRegionContext.XML_ENTITY_REFERENCE)) {
			lengthBefore = (getCurrentNode() != r) ? getCurrentNode().getFullText(r).length() : getCurrentNode().getFullText().length();
			s = EscapedTextUtil.getUnescapedText(getCurrentNode(), r);
			lengthAfter = s.length();
			sb.append(s);
		}
		else if (r != null && r.getType() == XMLRegionContext.XML_CDATA_TEXT) {
			if (r instanceof ITextRegionContainer) // only interested in contents
			{
				// navigate to next region container (which should be a JSP region)
				Iterator it = ((ITextRegionContainer) r).getRegions().iterator();
				ITextRegion temp = null;
				while (it.hasNext()) {
					temp = (ITextRegion) it.next();
					if (temp instanceof ITextRegionContainer || temp.getType() == XMLRegionContext.XML_CDATA_TEXT) {
						sb.append(getCurrentNode().getFullText(temp));
					}
					else if (temp.getType() == XMLRegionContext.XML_CDATA_OPEN || temp.getType() == XMLRegionContext.XML_CDATA_CLOSE) {
						cdata_tags_length += temp.getLength();
					}
				}
			}
		}
		return (lengthBefore - lengthAfter + cdata_tags_length);
	}

	//
	// <jsp:useBean>
	protected void translateUseBean(ITextRegionCollection container) {
		ITextRegion r = null;
		String attrName = null;
		String attrValue = null;
		String id = null;
		String type = null;
		String className = null;
		
		Iterator regions = container.getRegions().iterator();
		while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && (r.getType() != XMLRegionContext.XML_TAG_CLOSE || r.getType() != XMLRegionContext.XML_EMPTY_TAG_CLOSE)) {

			attrName = attrValue = null;
			if (r.getType().equals(XMLRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
				attrName = container.getText(r).trim();
				if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
					if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
						attrValue = StringUtils.stripQuotes(container.getText(r));
					}
					// has equals, but no value?
				}
				// an attribute with no equals?
			}
			// (pa) might need different logic here if we wanna support more
			if (attrName != null && attrValue != null) {
				if (attrName.equals("id")) //$NON-NLS-1$
					id = attrValue;
				else if (attrName.equals("class")) //$NON-NLS-1$
					className = attrValue;
				else if (attrName.equals("type")) //$NON-NLS-1$
					type = attrValue;
			}

		}
		// has id w/ type and/or classname 
		// Type id = new Classname();
		// or
		// Type id = null; // if there is no classname
		if (id != null && (type != null || className != null)) {
			if (type == null)
				type = className;
			String prefix = type + " " + id + " = "; //$NON-NLS-1$ //$NON-NLS-2$
			String suffix = "null;\n"; //$NON-NLS-1$
			if (className != null)
				suffix = "new " + className + "();\n"; //$NON-NLS-1$ //$NON-NLS-2$
			IStructuredDocumentRegion referenceRegion = fCurrentNode.getPrevious() != null ? fCurrentNode.getPrevious() : fCurrentNode;
			appendToBuffer(prefix + suffix, fUserCode, true, referenceRegion);
		}
	}

	final public int getCursorPosition() {
		return fCursorPosition;
	}

	protected boolean isCursorInExpression() {
		return fCursorInExpression;
	}

	protected void setCursorInExpression(boolean in) {
		fCursorInExpression = in;
	}

	final public void setSourceCursor(int i) {
		fSourcePosition = i;
	}

	final public int getSourcePosition() {
		return fSourcePosition;
	}
	
	final public TLDCMDocumentManager getTLDCMDocumentManager() {
		return TaglibController.getTLDCMDocumentManager(fStructuredDocument);
	}

	final public void setRelativeOffset(int relativeOffset) {
		this.fRelativeOffset = relativeOffset;
	}

	final public int getRelativeOffset() {
		return fRelativeOffset;
	}

	private void setCursorOwner(StringBuffer cursorOwner) {
		this.fCursorOwner = cursorOwner;
	}

	final public StringBuffer getCursorOwner() {
		return fCursorOwner;
	}

	private IStructuredDocumentRegion setCurrentNode(IStructuredDocumentRegion currentNode) {
		return this.fCurrentNode = currentNode;
	}

	final public IStructuredDocumentRegion getCurrentNode() {
		return fCurrentNode;
	}
}

Back to the top