blob: 1ed2ccd44141d7e4d8a8219539eb9740c29b9a32 [file] [log] [blame]
/*******************************************************************************
* 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.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jst.jsp.core.contentmodel.TaglibController;
import org.eclipse.jst.jsp.core.contentmodel.tld.TLDCMDocumentManager;
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.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.document.DocumentReader;
import org.eclipse.wst.sse.core.document.IEncodedDocument;
import org.eclipse.wst.sse.core.internal.modelhandler.ModelHandlerRegistry;
import org.eclipse.wst.sse.core.modelhandler.IModelHandler;
import org.eclipse.wst.sse.core.parser.BlockMarker;
import org.eclipse.wst.sse.core.parser.RegionParser;
import org.eclipse.wst.sse.core.parser.StructuredDocumentRegionHandler;
import org.eclipse.wst.sse.core.parser.StructuredDocumentRegionParser;
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 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 fSession, fThreadSafe, 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;
/** parse type*/
public static final int S_MODEL_BASED_PARSE = 2>>1;
public static final int S_FILE_BASED_PARSE = 2>>2;
/** last XML tag encountered was... **/
public static final int S_XML_USEBEAN = 2>>3;
public static final int S_XML_SCRIPTLET = 2>>4;
public static final int S_XML_EXPRESSION = 2>>5;
public static final int S_XML_DECLARATION = 2>>6;
private int stateMask = S_NONE;
/**
* configure using an XMLNode
* @param node
* @param monitor
*/
private void configure(XMLNode node, IProgressMonitor monitor) {
setState(S_MODEL_BASED_PARSE);
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
setState(S_FILE_BASED_PARSE);
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;
fSession = fThreadSafe = 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;
}
/**
/* 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) {
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, true, 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();
setState(S_NONE);
}
/**
* Saves memory if a model isn't already available.
* @param file
* @param monitor
*/
public void translateFromFile(IFile file, IProgressMonitor monitor) {
try {
IModelHandler handler = ModelHandlerRegistry.getInstance().getHandlerFor(file);
final IProgressMonitor progressMonitor = monitor;
final IEncodedDocument defaultDocument = handler.getDocumentLoader().createNewStructuredDocument();
if (defaultDocument instanceof IStructuredDocument) {
RegionParser parser = ((IStructuredDocument) defaultDocument).getParser();
if (parser instanceof StructuredDocumentRegionParser) {
String charset = detectCharset(file);
StructuredDocumentRegionParser documentParser = (StructuredDocumentRegionParser) parser;
final IDocument textDocument = new Document();
setDocumentContent(textDocument, file.getContents(true), charset);
documentParser.reset(new DocumentReader(textDocument));
documentParser.addStructuredDocumentRegionHandler(new StructuredDocumentRegionHandler() {
/**
* @see com.ibm.sse.model.parser.StructuredDocumentRegionHandler#nodeParsed(com.ibm.sse.model.text.IStructuredDocumentRegion)
*/
public void nodeParsed(IStructuredDocumentRegion documentRegion) {
// handle the document region (as with regular translation)
translateDocumentRegion(documentRegion);
// this is the memory saver
// disconnect the document regions
if (documentRegion.getPrevious() != null) {
documentRegion.getPrevious().setPrevious(null);
// some parts of code in translator call advanceNextNode()
//documentRegion.getPrevious().setNext(null);
}
if (progressMonitor.isCanceled()) {
textDocument.set(""); //$NON-NLS-1$
}
}
/**
* @see com.ibm.sse.model.parser.StructuredDocumentRegionHandler#resetNodes()
*/
public void resetNodes() {
//
}
});
// kicks off the parsing
documentParser.getDocumentRegions();
// build the translation
buildResult();
}
}
setState(S_NONE);
}
catch (CoreException e) {
Logger.logException(e);
}
}
/**
* called from file based translation (no StructuredModel or StructuredDocument)
* @param sdRegion
*/
void translateDocumentRegion(IStructuredDocumentRegion sdRegion) {
if(!isCanceled()) {
setCurrentNode(sdRegion);
if(sdRegion != null)
setSourceReferencePoint();
// check the last state here
if(hasState(S_XML_USEBEAN)) {
translateUseBean(sdRegion);
unsetState(S_XML_USEBEAN);
}
else if(hasState(S_XML_SCRIPTLET)) {
translateScriptletString(sdRegion.getText(), sdRegion, sdRegion.getStartOffset(), sdRegion.getEndOffset());
unsetState(S_XML_SCRIPTLET);
}
else if(hasState(S_XML_EXPRESSION)) {
translateExpressionString(sdRegion.getText(), sdRegion, sdRegion.getStartOffset(), sdRegion.getEndOffset());
unsetState(S_XML_EXPRESSION);
}
else if(hasState(S_XML_DECLARATION)) {
translateDeclarationString(sdRegion.getText(), sdRegion, sdRegion.getStartOffset(), sdRegion.getEndOffset());
unsetState(S_XML_DECLARATION);
}
else {
// normal case
// 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 structured document region
translateRegionContainer(getCurrentNode(), STANDARD_JSP);
}
}
}
}
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
}
}
}
}
/*
* from TaskTagSeeker
*/
private String detectCharset(IFile file) {
if (file.getType() == IResource.FILE && file.isAccessible()) {
IContentDescription d = null;
try {
// optimized description lookup, might not succeed
d = file.getContentDescription();
if (d != null)
return d.getCharset();
}
catch (CoreException e) {
// should not be possible given the accessible and file type
// check above
}
InputStream contents = null;
try {
contents = file.getContents();
IContentDescription description = Platform.getContentTypeManager().getDescriptionFor(contents, file.getName(), new QualifiedName[]{IContentDescription.CHARSET});
if (description != null) {
return description.getCharset();
}
}
catch (IOException e) {
// don't care
}
catch (CoreException e) {
Logger.logException(e);
}
finally {
if(contents != null) {
try {
contents.close();
} catch (IOException e1) {
// not sure how to recover at this point
}
}
}
}
return ResourcesPlugin.getEncoding();
}
/**
*
* @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
}
setState(S_XML_USEBEAN);
}
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());
}
setState(S_XML_SCRIPTLET);
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());
}
setState(S_XML_EXPRESSION);
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());
}
setState(S_XML_DECLARATION);
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$
// 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(attrValue);
}
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;
}
/*
* ** for workaround only
*/
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);
}
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;
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 fRelativeOffset) {
this.fRelativeOffset = fRelativeOffset;
}
final public int getRelativeOffset() {
return fRelativeOffset;
}
private void setCursorOwner(StringBuffer fCursorOwner) {
this.fCursorOwner = fCursorOwner;
}
final public StringBuffer getCursorOwner() {
return fCursorOwner;
}
private IStructuredDocumentRegion setCurrentNode(IStructuredDocumentRegion fCurrentNode) {
return this.fCurrentNode = fCurrentNode;
}
final public IStructuredDocumentRegion getCurrentNode() {
return fCurrentNode;
}
private void setState(int state) {
this.stateMask = this.stateMask | state;
}
private void unsetState(int state) {
if(hasState(state))
this.stateMask = this.stateMask -= state;
}
public final boolean hasState(int state) {
return (this.stateMask & state) == state;
}
}