| /******************************************************************************* |
| * 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) |
| javaClassRange = new Position(fOffsetInUserCode + type.length() + 1 + id.length() + 7, className.length()-4); |
| |
| //--------------------- |
| // 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; |
| } |
| } |