blob: ebe7fc8ad4e609f7c85b89e7ef6ac2a8a2f97d09 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2011 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
* Frits Jalvingh - contributions for bug 150794
*******************************************************************************/
package org.eclipse.jst.jsp.core.internal.java;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
import javax.servlet.jsp.tagext.VariableInfo;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
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.jst.jsp.core.internal.contentmodel.tld.provisional.JSP12TLDNames;
import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDElementDeclaration;
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache.PropertyGroup;
import org.eclipse.jst.jsp.core.internal.provisional.JSP11Namespace;
import org.eclipse.jst.jsp.core.internal.provisional.JSP12Namespace;
import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
import org.eclipse.jst.jsp.core.internal.taglib.CustomTag;
import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelper;
import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelperManager;
import org.eclipse.jst.jsp.core.internal.taglib.TaglibVariable;
import org.eclipse.jst.jsp.core.internal.util.FacetModuleCoreSupport;
import org.eclipse.jst.jsp.core.internal.util.ZeroStructuredDocumentRegion;
import org.eclipse.jst.jsp.core.jspel.IJSPELTranslator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.html.core.internal.contentmodel.JSP20Namespace;
import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.core.utils.StringUtils;
import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNode;
import org.eclipse.wst.xml.core.internal.parser.ContextRegionContainer;
import org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMDocumentTracker;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import com.ibm.icu.text.MessageFormat;
import com.ibm.icu.util.StringTokenizer;
/**
* Translates a JSP document into a HttpServlet subclass. Keeps two way mapping from
* Java translation to the original JSP source, which can be obtained through
* getJava2JspRanges() and getJsp2JavaRanges().
*/
public class JSPTranslator implements Externalizable {
/**
* <p>This value should be incremented if any of the following methods change:
* <ul>
* <li>{@link #writeExternal(ObjectOutput)}</li>
* <li>{@link #readExternal(ObjectInput)}</li>
* <li>{@link #writeString(ObjectOutput, String)}</li>
* <li>{@link #readString(ObjectInput)}</li>
* <li>{@link #writeRanges(ObjectOutput, HashMap)}</li>
* <li>{@link #readRanges(ObjectInput)}</li>
* </ul>
*
* This is because if any of these change then previously externalized {@link JSPTranslator}s
* will no longer be able to be read by the new implementation. This value is used by
* the {@link Externalizable} API automatically to determine if the file being read is of the
* correct version to be read by the current implementation of the {@link JSPTranslator}</p>
*
* @see #writeExternal(ObjectOutput)
* @see #readExternal(ObjectInput)
* @see #writeString(ObjectOutput, String)
* @see #readString(ObjectInput)
* @see #writeRanges(ObjectOutput, HashMap)
* @see #readRanges(ObjectInput)
*/
private static final long serialVersionUID = 2L;
/** for debugging */
private static final boolean DEBUG = Boolean.valueOf(Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspjavamapping")).booleanValue(); //$NON-NLS-1$
/** handy plugin ID constant */
private static final String JSP_CORE_PLUGIN_ID = "org.eclipse.jst.jsp.core"; //$NON-NLS-1$
// constants for reading extension point
/** Default EL Translator extension ID */
private static final String DEFAULT_JSP_EL_TRANSLATOR_ID = "org.eclipse.jst.jsp.defaultJSP20"; //$NON-NLS-1$
/** the name of the element in the extension point */
private static final String EL_TRANSLATOR_EXTENSION_NAME = "elTranslator"; //$NON-NLS-1$
/** the name of the property in the extension point */
private static final String ELTRANSLATOR_PROP_NAME = "ELTranslator"; //$NON-NLS-1$
// these constants are commonly used strings during translation
/** end line characters */
public static final String ENDL = "\n"; //$NON-NLS-1$
/** session variable declaration */
private static final String SESSION_VARIABLE_DECLARATION = "javax.servlet.http.HttpSession session = pageContext.getSession();" + ENDL; //$NON-NLS-1$
/** footer text */
private static final String FOOTER = "}}"; //$NON-NLS-1$
/** exception declaration */
private static final String EXCEPTION = "Throwable exception = null;"; //$NON-NLS-1$
/** expression prefix */
public static final String EXPRESSION_PREFIX = "out.print("; //$NON-NLS-1$
/** expression suffix */
public static final String EXPRESSION_SUFFIX = ");"; //$NON-NLS-1$
/** try/catch start */
private static final String TRY_CATCH_START = ENDL + "try {" + ENDL; //$NON-NLS-1$
/** try/catch end */
private static final String TRY_CATCH_END = " } catch (java.lang.Exception e) {} " + ENDL; //$NON-NLS-1$
/** JSP tag name prefix */
static final String JSP_PREFIX = "jsp:"; //$NON-NLS-1$
// these constants are to keep track of what type of code is currently being translated
/** code in question is standard JSP */
protected final static int STANDARD_JSP = 0;
/** code in question is embedded (JSP as an attribute or within comment tags) */
protected final static int EMBEDDED_JSP = 1;
/** code in question is a JSP declaration */
protected final static int DECLARATION = 2;
/** code in question is a JSP expression */
protected final static int EXPRESSION = 4;
/** code in question is a JSP scriptlet */
protected final static int SCRIPTLET = 8;
// strings specific to this translation
/** translated class header */
String fClassHeader = null;
/** translated class name */
String fClassname = null;
/** translated class super class */
String fSuperclass = null;
/** translated class imports */
String fImplicitImports = null;
/** translated class service header */
String fServiceHeader = null;
/** translated user defined imports */
private StringBuffer fUserImports = new StringBuffer();
//translation specific state
/** {@link IDOMModel} for the JSP file being translated */
IDOMModel fStructuredModel = null;
/** {@link IStructuredDocument} for the JSP file being translated */
IStructuredDocument fStructuredDocument = null;
/** the EL translator */
private IJSPELTranslator fELTranslator = null;
/** reported translation problems */
private List fTranslationProblems = new ArrayList();
/** 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 = false;
private boolean fCursorInExpression = false;
private boolean fIsInASession = true;
/** user java code in body of the service method */
private StringBuffer fUserCode = new StringBuffer();
/** user EL Expression */
private StringBuffer fUserELExpressions = new StringBuffer();
/** user defined vars declared in the beginning of the class */
private StringBuffer fUserDeclarations = new StringBuffer();
/**
* A map of tag names to tag library variable information; used to store
* the ones needed for AT_END variable support.
*/
private StackMap fTagToVariableMap = null;
private Stack fUseBeansStack = new Stack();
/** the final translated java document */
private StringBuffer fResult;
/** the buffer where the cursor is */
private StringBuffer fCursorOwner = null;
private IStructuredDocumentRegion fCurrentNode;
/** flag for if the cursor is in the current regionb eing translated */
private boolean fInCodeRegion = false;
/** used to avoid infinite looping include files */
private Stack fIncludes = null;
private Set fIncludedPaths = new HashSet(2);
private boolean fProcessIncludes = true;
/** mostly for helper classes, so they parse correctly */
private ArrayList fBlockMarkers = 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();
private HashMap fUserELRanges = 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();
/** EL Translator ID (pluggable) */
private String fELTranslatorID;
/**
* <code>true</code> if code has been found, such as HTML tags, that is not translated
* <code>false</code> otherwise. Useful for deciding if a place holder needs to be
* written to translation
*/
private boolean fFoundNonTranslatedCode;
/**
* <code>true</code> if code has been translated for the current region,
* <code>false</code> otherwise
*/
private boolean fCodeTranslated;
/**
* A structure for holding a region collection marker and list of variable
* information. The region can be used later for positioning validation
* messages.
*/
static class RegionTags {
ITextRegionCollection region;
CustomTag tag;
RegionTags(ITextRegionCollection region, CustomTag tag) {
this.region = region;
this.tag = tag;
}
}
public JSPTranslator() {
init();
}
/**
* configure using an XMLNode
*
* @param node
* @param monitor
*/
private void configure(IDOMNode node, IProgressMonitor monitor) {
fProgressMonitor = monitor;
fStructuredModel = node.getModel();
String baseLocation = fStructuredModel.getBaseLocation();
fELTranslatorID = getELTranslatorProperty(baseLocation);
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;
fELTranslatorID = getELTranslatorProperty(jspFile);
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);
}
}
/**
* Get the value of the ELTranslator property from a workspace relative
* path string
*
* @param baseLocation
* Workspace-relative string path
* @return Value of the ELTranslator property associated with the project.
*/
private String getELTranslatorProperty(String baseLocation) {
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
String elTranslatorValue = null;
IFile file = workspaceRoot.getFile(new Path(baseLocation));
if (file != null) {
elTranslatorValue = getELTranslatorProperty(file);
}
return elTranslatorValue;
}
/**
* Get the value of the ELTranslator property from an IFile
*
* @param file
* IFile
* @return Value of the ELTranslator property associated with the project.
*/
private String getELTranslatorProperty(IFile file) {
String elTranslatorValue = null;
if (file != null) {
if (file.exists()) {
try {
elTranslatorValue = file.getPersistentProperty(new QualifiedName(JSP_CORE_PLUGIN_ID, ELTRANSLATOR_PROP_NAME));
if (null == elTranslatorValue) {
elTranslatorValue = file.getProject().getPersistentProperty(new QualifiedName(JSP_CORE_PLUGIN_ID, ELTRANSLATOR_PROP_NAME));
}
}
catch (CoreException e) {
// ISSUE: why do we log this here? Instead of allowing to
// throwup?
Logger.logException(e);
}
}
}
return elTranslatorValue;
}
/**
* @param node
* @return the simple class name, not fully qualified
*/
private String createClassname(IDOMNode 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;
}
private IJSPProblem createJSPProblem(final int problemEID, final int problemID, final String message, final int start, final int end) {
final int line = fStructuredDocument.getLineOfOffset(start);
final char[] classname = fClassname.toCharArray();
/*
* Note: these problems would result in translation errors on the
* server, so the severity is not meant to be controllable
*/
return new IJSPProblem() {
public void setSourceStart(int sourceStart) {
}
public void setSourceLineNumber(int lineNumber) {
}
public void setSourceEnd(int sourceEnd) {
}
public boolean isWarning() {
return false;
}
public boolean isError() {
return true;
}
public int getSourceStart() {
return start;
}
public int getSourceLineNumber() {
return line;
}
public int getSourceEnd() {
return end;
}
public char[] getOriginatingFileName() {
return classname;
}
public String getMessage() {
return message;
}
public int getID() {
return problemID;
}
public String[] getArguments() {
return new String[0];
}
public int getEID() {
return problemEID;
}
};
}
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(IDOMNode 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
*/
void reset() {
// reset progress monitor
if (fProgressMonitor != null)
fProgressMonitor.setCanceled(false);
// reinit fields
fSourcePosition = -1;
fRelativeOffset = -1;
fCursorPosition = -1;
fIsErrorPage = fCursorInExpression = false;
fIsInASession = true;
fUserCode = new StringBuffer();
fUserDeclarations = new StringBuffer();
fUserImports = new StringBuffer();
fUserELExpressions = new StringBuffer();
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;
fOffsetInUserImports = 0;
fOffsetInUserDeclarations = 0;
fOffsetInUserCode = 0;
fJava2JspRanges.clear();
fImportRanges.clear();
fCodeRanges.clear();
fUseBeanRanges.clear();
fDeclarationRanges.clear();
fUserELRanges.clear();
fIndirectRanges.clear();
fIncludedPaths.clear();
fJspTextBuffer = new StringBuffer();
fFoundNonTranslatedCode = false;
fCodeTranslated = false;
}
/**
* @return just the "shell" of a servlet, nothing contributed from the JSP
* doc
*/
public final StringBuffer getEmptyTranslation() {
reset();
buildResult(true);
return getTranslation();
}
/**
* <p>put the final java document together</p>
*
* @param updateRanges <code>true</code> if the ranges need to be updated as the result
* is built, <code>false</code> if the ranges have already been updated. This is useful
* if building a result from a persisted {@link JSPTranslator}.
*/
private final void buildResult(boolean updateRanges) {
// to build the java document this is the order:
//
// + default imports
// + user imports
// + class header
// [+ error page]
// + user declarations
// + service method header
// + try/catch start
// + user code
// + try/catch end
// + service method footer
fResult = new StringBuffer(fImplicitImports.length() + fUserImports.length() + fClassHeader.length() +
fUserDeclarations.length() + fServiceHeader.length() + TRY_CATCH_START.length()
+ fUserCode.length() + TRY_CATCH_END.length() + FOOTER.length());
int javaOffset = 0;
fResult.append(fImplicitImports);
javaOffset += fImplicitImports.length();
// updateRanges(fIndirectImports, javaOffset);
if(updateRanges) {
updateRanges(fImportRanges, javaOffset);
}
// user imports
append(fUserImports);
javaOffset += fUserImports.length();
// class header
fResult.append(fClassHeader);
javaOffset += fClassHeader.length();
fResult.append(fSuperclass + "{" + ENDL); //$NON-NLS-1$
javaOffset += fSuperclass.length() + 2;
if(updateRanges) {
updateRanges(fDeclarationRanges, javaOffset);
}
// user declarations
append(fUserDeclarations);
javaOffset += fUserDeclarations.length();
if(updateRanges) {
updateRanges(fUserELRanges, javaOffset);
}
append(fUserELExpressions);
javaOffset += fUserELExpressions.length();
fResult.append(fServiceHeader);
javaOffset += fServiceHeader.length();
// session participant
if (fIsInASession) {
fResult.append(SESSION_VARIABLE_DECLARATION);
javaOffset += SESSION_VARIABLE_DECLARATION.length();
}
// error page
if (fIsErrorPage) {
fResult.append(EXCEPTION);
javaOffset += EXCEPTION.length();
}
fResult.append(TRY_CATCH_START);
javaOffset += TRY_CATCH_START.length();
if(updateRanges) {
updateRanges(fCodeRanges, javaOffset);
}
// user code
append(fUserCode);
javaOffset += fUserCode.length();
fResult.append(TRY_CATCH_END);
javaOffset += TRY_CATCH_END.length();
// footer
fResult.append(FOOTER);
javaOffset += FOOTER.length();
fJava2JspRanges.putAll(fImportRanges);
fJava2JspRanges.putAll(fDeclarationRanges);
fJava2JspRanges.putAll(fCodeRanges);
fJava2JspRanges.putAll(fUserELRanges);
}
/**
* @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();
appendImportToBuffer(tok, fCurrentNode, addToMap);
}
}
/**
* /* 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() {
if (DEBUG) {
StringBuffer debugString = new StringBuffer();
try {
Iterator it = fJava2JspRanges.keySet().iterator();
while (it.hasNext()) {
debugString.append("--------------------------------------------------------------\n"); //$NON-NLS-1$
Position java = (Position) it.next();
debugString.append("Java range:[" + java.offset + ":" + java.length + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
debugString.append("[" + fResult.toString().substring(java.offset, java.offset + java.length) + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$
debugString.append("--------------------------------------------------------------\n"); //$NON-NLS-1$
debugString.append("|maps to...|\n"); //$NON-NLS-1$
debugString.append("==============================================================\n"); //$NON-NLS-1$
Position jsp = (Position) fJava2JspRanges.get(java);
debugString.append("JSP range:[" + jsp.offset + ":" + jsp.length + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
debugString.append("[" + fJspTextBuffer.toString().substring(jsp.offset, jsp.offset + jsp.length) + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$
debugString.append("==============================================================\n"); //$NON-NLS-1$
debugString.append("\n"); //$NON-NLS-1$
debugString.append("\n"); //$NON-NLS-1$
}
}
catch (Exception e) {
Logger.logException("JSPTranslation error", e); //$NON-NLS-1$
}
Logger.log(Logger.INFO_DEBUG, debugString.toString());
}
return fResult;
}
public List getTranslationProblems() {
return fTranslationProblems;
}
/**
* Only valid after a configure(...), translate(...) or
* translateFromFile(...) call
*
* @return the text in the JSP file
*/
public final String getJspText() {
return fJspTextBuffer.toString();
}
protected void addTaglibVariables(String tagToAdd, ITextRegionCollection customTag) {
addTaglibVariables(tagToAdd, customTag, -1);
}
/**
* Add the server-side scripting variables used by this tag, along with
* any scoping.
*
* @param tagToAdd
* @param customTag
*/
protected void addTaglibVariables(String tagToAdd, ITextRegionCollection customTag, int index) {
if (customTag.getFirstRegion().getType().equals(DOMRegionContext.XML_TAG_OPEN)) {
/*
* Start tag
*/
addStartTagVariable(tagToAdd, customTag,index);
}
else if (customTag.getFirstRegion().getType().equals(DOMRegionContext.XML_END_TAG_OPEN)) {
/*
* End tag
*/
addEndTagVariable(tagToAdd, customTag);
}
}
private void addEndTagVariable(String tagToAdd, ITextRegionCollection customTag){
IFile f = getFile();
if (f == null || !f.exists())
return;
String decl = ""; //$NON-NLS-1$
RegionTags regionTag = (RegionTags) fTagToVariableMap.pop(tagToAdd);
if (regionTag != null) {
// even an empty array will indicate a need for a closing brace
TaglibVariable[] taglibVars = regionTag.tag.getTagVariables();
StringBuffer text = new StringBuffer();
if (regionTag.tag.isIterationTag())
doAfterBody(text, regionTag);
text.append("} // </"); //$NON-NLS-1$
text.append(tagToAdd);
text.append(">\n"); //$NON-NLS-1$
appendToBuffer(text.toString(), fUserCode, false, customTag);
for (int i = 0; i < taglibVars.length; i++) {
if (taglibVars[i].getScope() == VariableInfo.AT_END) {
decl = taglibVars[i].getDeclarationString();
appendToBuffer(decl, fUserCode, false, customTag);
}
}
}
else {
/*
* Since something should have been in the map because of a
* start tag, its absence now means an unbalanced end tag.
* Extras will be checked later to flag unbalanced start tags.
*/
IJSPProblem missingStartTag = createJSPProblem(IJSPProblem.StartCustomTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_4, tagToAdd), customTag.getStartOffset(), customTag.getEndOffset());
fTranslationProblems.add(missingStartTag);
}
}
private void addStartTagVariable(String tagToAdd,ITextRegionCollection customTag, int index){
IFile f = getFile();
if (f == null || !f.exists())
return;
TaglibHelper helper = TaglibHelperManager.getInstance().getTaglibHelper(f);
String decl = ""; //$NON-NLS-1$
List problems = new ArrayList();
CustomTag tag = helper.getCustomTag(tagToAdd, getStructuredDocument(), customTag, problems);
TaglibVariable[] taglibVars = tag.getTagVariables();
fTranslationProblems.addAll(problems);
/*
* Add AT_BEGIN variables
*/
for (int i = 0; i < taglibVars.length; i++) {
if (taglibVars[i].getScope() == VariableInfo.AT_BEGIN) {
decl = taglibVars[i].getDeclarationString();
appendToBuffer(decl, fUserCode, false, customTag);
}
}
boolean isEmptyTag = false;
if (index != -1)
isEmptyTag= isEmptyTag(customTag, index);
else
isEmptyTag= isEmptyTag(customTag);
/*
* Add a single { to limit the scope of NESTED variables
*/
StringBuffer text = new StringBuffer();
if (!isEmptyTag && tag.isIterationTag() && tag.getTagClassName() != null) {
text.append("\nwhile(true) "); //$NON-NLS-1$
}
text.append("{ // <"); //$NON-NLS-1$
text.append(tagToAdd);
if (isEmptyTag)
text.append("/>\n"); //$NON-NLS-1$
else
text.append(">\n"); //$NON-NLS-1$
appendToBuffer(text.toString(), fUserCode, false, customTag);
for (int i = 0; i < taglibVars.length; i++) {
if (taglibVars[i].getScope() == VariableInfo.NESTED) {
decl = taglibVars[i].getDeclarationString();
appendToBuffer(decl, fUserCode, false, customTag);
}
}
/*
* For empty tags, add the corresponding } and AT_END variables immediately.
*/
if (isEmptyTag) {
text = new StringBuffer();
text.append("} // <"); //$NON-NLS-1$
text.append(tagToAdd);
text.append("/>\n"); //$NON-NLS-1$
appendToBuffer(text.toString(), fUserCode, false, customTag);
/* Treat this as the end for empty tags */
for (int i = 0; i < taglibVars.length; i++) {
if (taglibVars[i].getScope() == VariableInfo.AT_END) {
decl = taglibVars[i].getDeclarationString();
appendToBuffer(decl, fUserCode, false, customTag);
}
}
}
else {
/*
* For non-empty tags, remember the variable information
*/
fTagToVariableMap.push(tagToAdd, new RegionTags(customTag, tag));
}
}
private boolean isEmptyTag(ITextRegionCollection customTag, int index) {
String type = null;
// custom tag is embedded
ITextRegionList regions = customTag.getRegions();
ITextRegion nextRegion = regions.get(index);
int size = customTag.getNumberOfRegions() ;
type = nextRegion.getType();
while (index <= size && !(DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(type) || DOMRegionContext.XML_TAG_NAME.equals(type) || DOMRegionContext.XML_TAG_CLOSE.equals(type) )) {
nextRegion = regions.get(++index);
type = nextRegion.getType();
}
return DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(type);
}
private boolean isEmptyTag(ITextRegionCollection customTag) {
ITextRegion lastRegion = customTag.getLastRegion();
// custom tag is embedded
if (customTag instanceof ITextRegionContainer) {
ITextRegionList regions = customTag.getRegions();
int size = customTag.getNumberOfRegions() - 1;
while (size > 0 && !(DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(lastRegion.getType()) || DOMRegionContext.XML_TAG_NAME.equals(lastRegion.getType()) || DOMRegionContext.XML_TAG_CLOSE.equals(lastRegion.getType()) )) {
lastRegion = regions.get(--size);
}
}
return DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(lastRegion.getType());
}
private void addCustomTaglibVariables(String tagToAdd, ITextRegionCollection customTag, ITextRegion prevRegion, int index) {
//Can't judge by first region as start and end tag are part of same ContextRegionContainer
if (prevRegion != null && prevRegion.getType().equals(DOMRegionContext.XML_END_TAG_OPEN)) {
/*
* End tag
*/
addEndTagVariable(tagToAdd, customTag);
}
else if (prevRegion != null && prevRegion.getType().equals(DOMRegionContext.XML_TAG_OPEN)) {
/*
* Start tag
*/
addStartTagVariable(tagToAdd,customTag, index);
}
}
private void doAfterBody(StringBuffer buffer, RegionTags regionTag) {
buffer.append("\tif ( (new "); //$NON-NLS-1$
buffer.append(regionTag.tag.getTagClassName());
buffer.append("()).doAfterBody() != javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_AGAIN)\n\t\tbreak;\n"); //$NON-NLS-1$
}
/**
* @return the workspace file for this model, null otherwise
*/
private IFile getFile() {
IFile f = null;
ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(getStructuredDocument());
if (buffer != null) {
IPath path = buffer.getLocation();
if (path.segmentCount() > 1) {
f = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
}
if (f != null && f.isAccessible()) {
return f;
}
}
return null;
}
/*
* 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() {
if (fTagToVariableMap == null) {
fTagToVariableMap = new StackMap();
}
fTranslationProblems.clear();
setCurrentNode(new ZeroStructuredDocumentRegion(fStructuredDocument, 0));
translatePreludes();
setCurrentNode(fStructuredDocument.getFirstStructuredDocumentRegion());
while (getCurrentNode() != null && !isCanceled()) {
//no code has been translated for this region yet
fCodeTranslated = false;
// 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() == DOMRegionContext.XML_COMMENT_TEXT || getCurrentNode().getType() == DOMRegionContext.XML_CDATA_TEXT || getCurrentNode().getType() == DOMRegionContext.UNDEFINED) {
translateXMLCommentNode(getCurrentNode());
}
else {
// iterate through each region in the flat node
translateRegionContainer(getCurrentNode(), STANDARD_JSP);
}
//if no code was translated for this region then found "non translated code"
if(!fCodeTranslated) {
fFoundNonTranslatedCode = true;
}
if (getCurrentNode() != null)
advanceNextNode();
}
writePlaceHolderForNonTranslatedCode();
setCurrentNode(new ZeroStructuredDocumentRegion(fStructuredDocument, fStructuredDocument.getLength()));
translateCodas();
/*
* Any contents left in the map indicate start tags that never had end
* tags. While the '{' that is present without the matching '}' should
* cause a Java translation fault, that's not particularly helpful to
* a user who may only know how to use custom tags as tags. Ultimately
* unbalanced custom tags should just be reported as unbalanced tags,
* and unbalanced '{'/'}' only reported when the user actually
* unbalanced them with scriptlets.
*/
Iterator regionAndTaglibVariables = fTagToVariableMap.values().iterator();
while (regionAndTaglibVariables.hasNext()) {
RegionTags regionTag = (RegionTags) regionAndTaglibVariables.next();
ITextRegionCollection extraStartRegion = regionTag.region;
IJSPProblem missingEndTag = createJSPProblem(IJSPProblem.EndCustomTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_5,regionTag.tag.getTagName()), extraStartRegion.getStartOffset(), extraStartRegion.getEndOffset());
fTranslationProblems.add(missingEndTag);
StringBuffer text = new StringBuffer();
// Account for iteration tags that have a missing end tag
if (regionTag.tag.isIterationTag())
doAfterBody(text, regionTag);
text.append("} // [</"); //$NON-NLS-1$
text.append(regionTag.tag.getTagName());
text.append(">]"); //$NON-NLS-1$
appendToBuffer(text.toString(), fUserCode, false, fStructuredDocument.getLastStructuredDocumentRegion());
}
fTagToVariableMap.clear();
/*
* Now do the same for jsp:useBean tags, whose contents get their own
* { & }
*/
while (!fUseBeansStack.isEmpty()) {
appendToBuffer("}", fUserCode, false, fStructuredDocument.getLastStructuredDocumentRegion()); //$NON-NLS-1$
ITextRegionCollection extraStartRegion = (ITextRegionCollection) fUseBeansStack.pop();
IJSPProblem missingEndTag = createJSPProblem(IJSPProblem.UseBeanEndTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_5,JSP11Namespace.ElementName.USEBEAN), extraStartRegion.getStartOffset(), extraStartRegion.getEndOffset());
fTranslationProblems.add(missingEndTag);
}
buildResult(true);
}
/**
* Translates a region container (and XML JSP container, or <% JSP
* container). This method should only be called in this class and for
* containers in the primary structured document as all buffer appends
* will be direct.
*/
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
}
}
}
}
protected void init() {
fClassname = "_JSPServlet"; //$NON-NLS-1$
fClassHeader = "public class " + fClassname + " extends "; //$NON-NLS-1$ //$NON-NLS-2$
fImplicitImports = "import javax.servlet.*;" + ENDL + //$NON-NLS-1$
"import javax.servlet.http.*;" + ENDL + //$NON-NLS-1$
"import javax.servlet.jsp.*;" + ENDL + ENDL; //$NON-NLS-1$
fServiceHeader = "public void _jspService(javax.servlet.http.HttpServletRequest request," + //$NON-NLS-1$
" javax.servlet.http.HttpServletResponse response)" + ENDL + //$NON-NLS-1$
"\t\tthrows java.io.IOException, javax.servlet.ServletException {" + ENDL + //$NON-NLS-1$
"javax.servlet.jsp.PageContext pageContext = JspFactory.getDefaultFactory().getPageContext(this, request, response, null, true, JspWriter.DEFAULT_BUFFER, true);" + ENDL + //$NON-NLS-1$
"javax.servlet.ServletContext application = pageContext.getServletContext();" + ENDL + //$NON-NLS-1$
"javax.servlet.ServletConfig config = pageContext.getServletConfig();" + ENDL + //$NON-NLS-1$
"javax.servlet.jsp.JspWriter out = pageContext.getOut();" + ENDL + //$NON-NLS-1$
"Object page = this;" + ENDL; //$NON-NLS-1$
fSuperclass = "javax.servlet.http.HttpServlet"; //$NON-NLS-1$
}
/**
*
* @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() == DOMJSPRegionContexts.JSP_CONTENT || r.getType() == DOMRegionContext.XML_CONTENT)
break;
else if (r.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)
break;
else if (r.getType() == DOMRegionContext.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)
*
* This method should only be called in this class and for containers in
* the primary structured document as all buffer appends will be direct
*/
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();
// content assist was not showing up in JSP inside a javascript region
if (DOMRegionContext.BLOCK_TEXT == type) {
// check if it's nested jsp in a script tag...
if (region instanceof ITextRegionContainer) {
// pass in block text's container & iterator
Iterator regionIterator = ((ITextRegionCollection) region).getRegions().iterator();
translateJSPNode(region, regionIterator, type, EMBEDDED_JSP);
}
else {
//be sure to combine all of the text from the block region
StringBuffer fullText = new StringBuffer(containerRegion.getFullText(region));
while(regions.hasNext()) {
region = (ITextRegion)regions.next();
if (region instanceof ITextRegionContainer) {
// pass in block text's container & iterator
Iterator regionIterator = ((ITextRegionCollection) region).getRegions().iterator();
translateJSPNode(region, regionIterator, type, EMBEDDED_JSP);
}
if(region.getType() == DOMRegionContext.BLOCK_TEXT) {
fullText.append(containerRegion.getFullText(region));
} else {
//update type for when we exit if statement for BLOCK_TEXT
type = region.getType();
break;
}
}
/**
* LIMITATION - Normally the script content within a
* script tag is a single document region with a single
* BLOCK_TEXT text region within it. Any JSP scripting
* will be within its own region container (for the sake
* of keeping the scripting open/content/end as a group)
* also of BLOCK_TEXT. That ignores custom tags that might
* be in there, though, as they require proper scoping and
* variable declaration to be performed even though
* they're not proper nodes in the DOM. The only way to
* really do this is to treat the entire script content as
* JSP content on its own, akin to an included segment.
* Further complicating this solution is that tagdependent
* custom tags have their comment marked as BLOCK_TEXT as
* well, so there's no clear way to tell the two cases
* apart.
*/
// ////////////////////////////////////////////////////////////////////////////////
// THIS EMBEDDED JSP TEXT WILL COME OUT LATER WHEN
// PARTITIONING HAS
// SUPPORT FOR NESTED XML-JSP
// CMVC 241882
decodeScriptBlock(fullText.toString(), containerRegion.getStartOffset());
// ////////////////////////////////////////////////////////////////////////////////
}
}
// if (region instanceof ITextRegionCollection &&
// ((ITextRegionCollection) region).getNumberOfRegions() > 0) {
// translateRegionContainer((ITextRegionCollection) region,
// EMBEDDED_JSP);
// }
if (type != null && isJSP(type)) // <%, <%=, <%!, <%@
{
// translateJSPNode(region, regions, type, JSPType);
translateJSPNode(containerRegion, regions, type, JSPType);
}
else if (type != null && (type == DOMRegionContext.XML_TAG_OPEN || type == DOMRegionContext.XML_END_TAG_OPEN)) {
translateXMLNode(containerRegion, regions);
}
else if(type != null && type == DOMRegionContext.XML_CONTENT && region instanceof ITextRegionContainer) {
//this case was put in to parse EL that is not in an attribute
translateXMLContent((ITextRegionContainer)region);
}
//the end tags of these regions are "translated" in a sense
else if(type == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE ||
type == DOMJSPRegionContexts.JSP_CLOSE) {
this.fCodeTranslated = true;
}
}
}
/*
* ////////////////////////////////////////////////////////////////////////////////// **
* TEMP WORKAROUND FOR CMVC 241882 Takes a String and blocks out
* jsp:scriptlet, jsp:expression, and jsp:declaration @param blockText
* @return
*/
void decodeScriptBlock(String blockText, int startOfBlock) {
XMLJSPRegionHelper helper = new XMLJSPRegionHelper(this, false);
helper.addBlockMarker(new BlockMarker("jsp:scriptlet", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
helper.addBlockMarker(new BlockMarker("jsp:expression", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
helper.addBlockMarker(new BlockMarker("jsp:declaration", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
helper.addBlockMarker(new BlockMarker("jsp:directive.include", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
helper.addBlockMarker(new BlockMarker("jsp:directive.taglib", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
helper.reset(blockText, startOfBlock);
// force parse
helper.forceParse();
}
/*
* 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 == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN || type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN || type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || type == DOMJSPRegionContexts.JSP_CONTENT || type == DOMJSPRegionContexts.JSP_EL_OPEN) && type != DOMRegionContext.XML_TAG_OPEN);
// checking XML_TAG_OPEN so <jsp:directive.xxx/> gets treated like
// other XML jsp tags
}
/**
* This currently only detects EL content and translates it,
* but if other cases arise later then they could be added in here
*
* @param embeddedContainer the container that may contain EL
*/
protected void translateXMLContent(ITextRegionContainer embeddedContainer) {
ITextRegionList embeddedRegions = embeddedContainer.getRegions();
int length = embeddedRegions.size();
for (int i = 0; i < length; i++) {
ITextRegion delim = embeddedRegions.get(i);
String type = delim.getType();
// check next region to see if it's EL content
if (i + 1 < length) {
if((type == DOMJSPRegionContexts.JSP_EL_OPEN || type == DOMJSPRegionContexts.JSP_VBL_OPEN)) {
ITextRegion region = null;
int start = delim.getEnd();
while (++i < length) {
region = embeddedRegions.get(i);
if (region == null || !isELType(region.getType()))
break;
}
fLastJSPType = EXPRESSION;
String elText = embeddedContainer.getFullText().substring(start, (region != null ? region.getStart() : embeddedContainer.getLength() - 1));
translateEL(elText, embeddedContainer.getText(delim), fCurrentNode,
embeddedContainer.getEndOffset(delim), elText.length());
}
}
}
}
private boolean isELType(String type) {
return DOMJSPRegionContexts.JSP_EL_CONTENT.equals(type) || DOMJSPRegionContexts.JSP_EL_DQUOTE.equals(type) || DOMJSPRegionContexts.JSP_EL_QUOTED_CONTENT.equals(type) || DOMJSPRegionContexts.JSP_EL_SQUOTE.equals(type);
}
/**
* 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();
// <jsp:directive.xxx > comes in as this
if (r.getType() == DOMRegionContext.XML_TAG_NAME || r.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)
{
String fullTagName = container.getText(r);
if (fullTagName.indexOf(':') > -1 && !fullTagName.startsWith(JSP_PREFIX)) {
addTaglibVariables(fullTagName, container,-1); // it
// may
// be a
// custom
// tag
}
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("scriptlet")) //$NON-NLS-1$
{
translateXMLJSPContent(SCRIPTLET);
}
else if (jspTagName.equals("expression")) //$NON-NLS-1$
{
translateXMLJSPContent(EXPRESSION);
}
else if (jspTagName.equals("declaration")) //$NON-NLS-1$
{
translateXMLJSPContent(DECLARATION);
}
else if (jspTagName.equals("directive")) //$NON-NLS-1$
{
if (st.hasMoreTokens()) {
String directiveName = st.nextToken();
if (directiveName.equals("taglib")) { //$NON-NLS-1$
while (r != null && regions.hasNext() && !r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
r = (ITextRegion) regions.next();
if (container.getText(r).equals(JSP11Namespace.ATTR_NAME_PREFIX)) {
String prefix = getAttributeValue(r, regions);
if (prefix != null) {
handleTaglib(prefix);
}
}
}
return;
}
else if (directiveName.equals("include")) { //$NON-NLS-1$
String fileLocation = ""; //$NON-NLS-1$
// 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(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
r = (ITextRegion) regions.next();
}
fileLocation = getAttributeValue(r, regions);
if (fileLocation != null)
handleIncludeFile(fileLocation);
}
else if (directiveName.equals("page")) { //$NON-NLS-1$
// 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) {
// 'regions' contain the attrs
translatePageDirectiveAttributes(regions, getCurrentNode());
}
}
else if (directiveName.equals("tag")) { //$NON-NLS-1$
translatePageDirectiveAttributes(regions, getCurrentNode());
}
else if (directiveName.equals("variable")) { //$NON-NLS-1$
translateVariableDirectiveAttributes(regions);
}
}
}
else if (jspTagName.equals("include")) { //$NON-NLS-1$
// <jsp:include page="filename") />
checkAttributeValueContainer(regions, "page"); //$NON-NLS-1$
}
else if (jspTagName.equals("forward")) { //$NON-NLS-1$
checkAttributeValueContainer(regions, "page"); //$NON-NLS-1$
}
else if (jspTagName.equals("param")) { //$NON-NLS-1$
checkAttributeValueContainer(regions, "value"); //$NON-NLS-1$
}
else if (jspTagName.equals("setProperty")) { //$NON-NLS-1$
checkAttributeValueContainer(regions, "value"); //$NON-NLS-1$
}
else if (jspTagName.equals("useBean")) //$NON-NLS-1$
{
checkAttributeValueContainer(regions, "name"); //$NON-NLS-1$
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=103004
// advanceNextNode(); // get the content
if (getCurrentNode() != null) {
translateUseBean(container); // 'regions'
}
}
}
}
else {
checkAllAttributeValueContainers(container,regions);
}
}
}
}
/**
* translates embedded containers for ALL attribute values
*
* @param regions
*/
private void checkAllAttributeValueContainers(ITextRegionCollection container, Iterator regions) {
// tag name is not jsp
// handle embedded jsp attributes...
ITextRegion embedded = null;
// Iterator attrRegions = null;
// ITextRegion attrChunk = null;
ITextRegion prevRegion = null;
while (regions.hasNext()) {
embedded = (ITextRegion) regions.next();
if (embedded.getType() == DOMRegionContext.XML_TAG_NAME || embedded.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)
{
String fullTagName = container.getText(embedded);
if (fullTagName.indexOf(':') > -1 && !fullTagName.startsWith(JSP_PREFIX)) {
if (prevRegion != null)
addCustomTaglibVariables(fullTagName, container,prevRegion,-1); // it may be a custom tag
}
}
else if (embedded instanceof ITextRegionContainer) {
// parse out container
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=130606
// fix exponential iteration problem w/ embedded expressions
translateEmbeddedJSPInAttribute((ITextRegionContainer) embedded);
// attrRegions = ((ITextRegionContainer)
// embedded).getRegions().iterator();
// while (attrRegions.hasNext()) {
// attrChunk = (ITextRegion) attrRegions.next();
// String type = attrChunk.getType();
// // 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 == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN ||
// type ==
// DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || type ==
// DOMJSPRegionContexts.JSP_DECLARATION_OPEN || type ==
// DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || type ==
// DOMJSPRegionContexts.JSP_EL_OPEN) {
// // now call jsptranslate
// translateEmbeddedJSPInAttribute((ITextRegionContainer)
// embedded);
// break;
// }
// }
}
prevRegion = embedded;
}
}
/**
* translates embedded container for specified attribute
*
* @param regions
* @param attrName
*/
private void checkAttributeValueContainer(Iterator regions, String attrName) {
ITextRegion r = null;
while (regions.hasNext()) {
r = (ITextRegion) regions.next();
if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME && getCurrentNode().getText(r).equals(attrName)) {
// skip to attribute value
while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null) {
if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)
break;
}
// forces embedded region to be translated
if (r instanceof ContextRegionContainer) {
translateEmbeddedJSPInAttribute((ContextRegionContainer) r);
}
break;
}
}
}
/*
* example:
*
* <jsp:scriptlet>scriptlet jsp-java content <![CDATA[ more jsp java ]]>
* jsp-java content... <![CDATA[ more jsp java ]]> </jsp:scriptlet>
*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=93366
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=88590 translate
* everything inbetween <scriptlet> tags, which may be more than one
* region (esp. CDATA)
*
*/
private void translateXMLJSPContent(int type) {
IStructuredDocumentRegion sdr = getCurrentNode().getNext();
int start = sdr.getStartOffset();
int end = sdr.getEndOffset();
String sdrText = ""; //$NON-NLS-1$
StringBuffer regionText = new StringBuffer();
// read structured document regions until </jsp:scriptlet> or EOF
while (sdr != null && sdr.getType() != DOMRegionContext.XML_TAG_NAME) {
// setup for next region
if (regionText.length() == 0)
start = sdr.getStartOffset();
sdrText = sdr.getText();
if (sdr.getType() == DOMRegionContext.XML_CDATA_TEXT) {
// Clear out the buffer
if (regionText.length() > 0) {
writeToBuffer(type, regionText.toString(), start, end);
regionText = new StringBuffer();
}
// just to be safe, make sure CDATA start & end are there
if (sdrText.startsWith("<![CDATA[") && sdrText.endsWith("]]>")) { //$NON-NLS-1$ //$NON-NLS-2$
start = sdr.getStartOffset() + 9; // <![CDATA[
end = sdr.getEndOffset() - 3; // ]]>
sdrText = sdrText.substring(9, sdrText.length() - 3);
writeToBuffer(type, sdrText, start, end);
}
}
else {
// handle entity references
regionText.append(EscapedTextUtil.getUnescapedText(sdrText));
end = sdr.getEndOffset();
}
sdr = sdr.getNext();
}
if (regionText.length() > 0)
writeToBuffer(type, regionText.toString(), start, end);
setCurrentNode(sdr);
setSourceReferencePoint();
}
private void writeToBuffer(int type, String content, int jspStart, int jspEnd) {
switch (type) {
case SCRIPTLET :
translateScriptletString(content, getCurrentNode(), jspStart, jspEnd - jspStart, false);
break;
case EXPRESSION :
translateExpressionString(content, getCurrentNode(), jspStart, jspEnd - jspStart, false);
break;
case DECLARATION :
translateDeclarationString(content, getCurrentNode(), jspStart, jspEnd - jspStart, false);
break;
}
}
/**
* 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...
}
else if (DOMRegionContext.XML_COMMENT_TEXT.equals(commentRegion.getType())) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=222215
// support custom tags hidden in a comment region
decodeScriptBlock(node.getFullText(commentRegion), node.getStartOffset(commentRegion));
}
}
}
/**
* determines which type of JSP node to translate
*/
protected void translateJSPNode(ITextRegion region, Iterator regions, String type, int JSPType) {
if (type == DOMJSPRegionContexts.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) {
translateEmbeddedJSPInBlock((ITextRegionCollection) region, regions);
// ensure the rest of this method won't be called
}
/* NOTE: the type here is of the node preceding the current node
* thus must check to see if the current node is JSP close, if it is
* then the JSP is something akin to <%%> and should not be translated
* (Bug 189318)
*/
if (contentRegion != null && contentRegion.getType() != DOMJSPRegionContexts.JSP_CLOSE) {
if (type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN) {
translateExpression(contentRegion);
}
else if (type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN) {
translateDeclaration(contentRegion);
}
else if (type == DOMJSPRegionContexts.JSP_CONTENT || type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN) {
translateScriptlet(contentRegion);
}
}
else {
// this is the case of an attribute w/ no region <p
// align="<%%>">
setCursorOwner(getJSPTypeForRegion(region));
}
}
}
private void translateEL(String elText, String delim, IStructuredDocumentRegion currentNode, int contentStart, int contentLength) {
IJSPELTranslator translator = getELTranslator();
if (null != translator) {
List elProblems = translator.translateEL(elText, delim, currentNode, contentStart, contentLength, fUserELExpressions, fUserELRanges, fStructuredDocument);
fTranslationProblems.addAll(elProblems);
}
}
/**
* Discover and instantiate an EL translator.
*/
public IJSPELTranslator getELTranslator() {
if (fELTranslator == null) {
/*
* name of plugin that exposes this extension point
*/
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(JSP_CORE_PLUGIN_ID, EL_TRANSLATOR_EXTENSION_NAME); // -
// extension
// id
// Iterate over all declared extensions of this extension point.
// A single plugin may extend the extension point more than once,
// although it's not recommended.
IConfigurationElement bestTranslator = null;
IExtension[] extensions = extensionPoint.getExtensions();
for (int curExtension = 0; curExtension < extensions.length; curExtension++) {
IExtension extension = extensions[curExtension];
IConfigurationElement[] translators = extension.getConfigurationElements();
for (int curTranslator = 0; curTranslator < translators.length; curTranslator++) {
IConfigurationElement elTranslator = translators[curTranslator];
if (!EL_TRANSLATOR_EXTENSION_NAME.equals(elTranslator.getName())) { // -
// name
// of
// configElement
continue;
}
String idString = elTranslator.getAttribute("id"); //$NON-NLS-1$
if (null != idString && idString.equals(fELTranslatorID) || (null == bestTranslator && DEFAULT_JSP_EL_TRANSLATOR_ID.equals(idString))) {
bestTranslator = elTranslator;
}
}
}
if (null != bestTranslator) {
try {
Object execExt = bestTranslator.createExecutableExtension("class"); //$NON-NLS-1$
if (execExt instanceof IJSPELTranslator) {
return fELTranslator = (IJSPELTranslator) execExt;
}
}
catch (CoreException e) {
Logger.logException(e);
}
}
}
return fELTranslator;
}
/**
* Pass the ITextRegionCollection which is the embedded region
*
* @param regions
* iterator for collection
*/
private void translateEmbeddedJSPInBlock(ITextRegionCollection collection, Iterator regions) {
ITextRegion region = null;
while (regions.hasNext()) {
region = (ITextRegion) regions.next();
if (isJSP(region.getType()))
break;
region = null;
}
if (region != null) {
translateEmbeddedJSPInAttribute(collection);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=126377
// all of collection was translated so just finish off iterator
while (regions.hasNext())
regions.next();
}
}
/*
* Translates all embedded jsp regions in embeddedContainer for example:
* <a href="index.jsp?p=<%=abc%>b=<%=xyz%>">abc</a>
*/
private void translateEmbeddedJSPInAttribute(ITextRegionCollection embeddedContainer) {
// THIS METHOD IS A FIX FOR
// jsp embedded in attribute regions
// loop all regions
ITextRegionList embeddedRegions = embeddedContainer.getRegions();
ITextRegion delim = null;
ITextRegion content = null;
String type = null;
String quotetype = null;
for (int i = 0; i < embeddedRegions.size(); i++) {
// possible delimiter, check later
delim = embeddedRegions.get(i);
type = delim.getType();
if (type == DOMRegionContext.XML_TAG_NAME ) {
String fullTagName = embeddedContainer.getText(delim);
if (fullTagName.indexOf(':') > -1 && !fullTagName.startsWith(JSP_PREFIX)) {
ITextRegion prevRegion =null;
if (i>0)
prevRegion = embeddedRegions.get(i-1);
addCustomTaglibVariables(fullTagName, embeddedContainer,prevRegion,i+1); // it may be a custom tag
}
}
if(type == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_DQUOTE || type == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_SQUOTE
|| type == DOMJSPRegionContexts.JSP_TAG_ATTRIBUTE_VALUE_DQUOTE || type == DOMJSPRegionContexts.JSP_TAG_ATTRIBUTE_VALUE_SQUOTE)
quotetype = type;
// check next region to see if it's content
if (i + 1 < embeddedRegions.size()) {
String regionType = embeddedRegions.get(i + 1).getType();
if (regionType == DOMJSPRegionContexts.JSP_CONTENT || regionType == DOMJSPRegionContexts.JSP_EL_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 == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN) {
fLastJSPType = EXPRESSION;
// translateExpressionString(embeddedContainer.getText(content),
// fCurrentNode, contentStart, content.getLength());
translateExpressionString(embeddedContainer.getText(content), embeddedContainer, contentStart, content.getLength(), quotetype);
}
else if (type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN) {
fLastJSPType = SCRIPTLET;
// translateScriptletString(embeddedContainer.getText(content),
// fCurrentNode, contentStart, content.getLength());
translateScriptletString(embeddedContainer.getText(content), embeddedContainer, contentStart, content.getLength(), false);
}
else if (type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN) {
fLastJSPType = DECLARATION;
// translateDeclarationString(embeddedContainer.getText(content),
// fCurrentNode, contentStart, content.getLength());
translateDeclarationString(embeddedContainer.getText(content), embeddedContainer, contentStart, content.getLength(), false);
}
else if (type == DOMJSPRegionContexts.JSP_EL_OPEN) {
fLastJSPType = EXPRESSION;
translateEL(embeddedContainer.getText(content), embeddedContainer.getText(delim), 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;
}
}
}
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 == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN)
type = SCRIPTLET;
else if (regionType == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN)
type = EXPRESSION;
else if (regionType == DOMJSPRegionContexts.JSP_DECLARATION_OPEN)
type = DECLARATION;
else if (regionType == DOMJSPRegionContexts.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() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME) { // could
// be
// XML_CONTENT
// =
// "",
// skips
// attrs?
regionText = getCurrentNode().getText(r);
if (regionText.equals("taglib")) { //$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(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
r = (ITextRegion) regions.next();
}
fileLocation = getAttributeValue(r, regions);
if (attrValue != null)
handleIncludeFile(fileLocation);
}
else if (regionText.equals("page")) { //$NON-NLS-1$
translatePageDirectiveAttributes(regions, getCurrentNode());
}
else if (regionText.equals("tag")) { //$NON-NLS-1$
// some attributes overlap, so both are handled in this method
translatePageDirectiveAttributes(regions, getCurrentNode());
}
else if (regionText.equals("variable")) { //$NON-NLS-1$
translateVariableDirectiveAttributes(regions);
}
else if (regionText.equals("attribute")) { //$NON-NLS-1$
translateAttributeDirectiveAttributes(regions);
}
}
}
private void translateAttributeDirectiveAttributes(Iterator regions) {
ITextRegion r = null;
String attrName, attrValue;
String varType = "java.lang.String"; //$NON-NLS-1$ // the default class...
String varName = null;
String description = "";//$NON-NLS-1$
boolean isFragment = false;
// iterate all attributes
while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() != DOMJSPRegionContexts.JSP_CLOSE) {
attrName = attrValue = null;
if (r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
attrName = getCurrentNode().getText(r).trim();
if (attrName.length() > 0) {
if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
attrValue = StringUtils.strip(getCurrentNode().getText(r));
}
// has equals, but no value?
}
if (attrName.equals(JSP11Namespace.ATTR_NAME_TYPE)) {
varType = attrValue;
}
else if (attrName.equals(JSP20Namespace.ATTR_NAME_FRAGMENT)) {
isFragment = Boolean.valueOf(attrValue).booleanValue();
}
else if (attrName.equals(JSP11Namespace.ATTR_NAME_NAME)) {
varName = attrValue;
}
else if (attrName.equals(JSP20Namespace.ATTR_NAME_DESCRIPTION)) {
description = attrValue;
}
}
}
}
if (varName != null) {
if (isFragment) {
// 2.0:JSP.8.5.2
varType = "javax.servlet.jsp.tagext.JspFragment"; //$NON-NLS-1$
}
String declaration = new TaglibVariable(varType, varName, "", description).getDeclarationString(true, TaglibVariable.M_PRIVATE); //$NON-NLS-1$
appendToBuffer(declaration, fUserDeclarations, false, fCurrentNode);
}
}
private void translateVariableDirectiveAttributes(Iterator regions) {
/*
* Shouldn't create a scripting variable in *this* tag file's
* translation, only in JSP files that use it -
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=188780
*/
}
/*
* 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
*
* @deprecated - does not properly handle prefixes
*/
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();
// since may be call from another thread (like a background
// job)
// this check is to be safer
if (sdRegion != null && !sdRegion.isDeleted()) {
taglibRegions = sdRegion.getRegions().iterator();
while (!sdRegion.isDeleted() && taglibRegions.hasNext()) {
r = (ITextRegion) taglibRegions.next();
if (r.getType().equals(DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)) {
String text = sdRegion.getText(r);
if (JSP12TLDNames.TAGLIB.equals(text) || JSP12Namespace.ElementName.DIRECTIVE_TAGLIB.equals(text)) {
addBlockMarkers(tracker.getDocument());
}
}
}
}
}
}
}
/*
* 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
*/
private void handleTaglib(String prefix) {
// get/create TLDCMDocument
TLDCMDocumentManager mgr = TaglibController.getTLDCMDocumentManager(fStructuredDocument);
if (mgr != null) {
// get trackers for the CMDocuments enabled at this offset
List trackers = mgr.getCMDocumentTrackers(getCurrentNode().getEnd());
Iterator it = trackers.iterator();
CMDocumentTracker tracker = null;
while (it.hasNext()) {
tracker = (CMDocumentTracker) it.next();
addBlockMarkers(prefix + ":", tracker.getDocument()); //$NON-NLS-1$
}
}
}
/*
* 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, DOMJSPRegionContexts.JSP_CONTENT, true));
}
}
}
/*
* adds block markers to JSPTranslator's block marker list for all
* elements in doc @param doc
*/
protected void addBlockMarkers(String prefix, CMDocument doc) {
if (doc.getElements().getLength() > 0) {
Iterator elements = doc.getElements().iterator();
CMNode node = null;
while (elements.hasNext()) {
node = (CMNode) elements.next();
if (node instanceof TLDElementDeclaration && ((TLDElementDeclaration) node).getBodycontent().equals(JSP12TLDNames.CONTENT_TAGDEPENDENT))
getBlockMarkers().add(new BlockMarker(prefix + node.getNodeName(), null, DOMRegionContext.BLOCK_TEXT, true));
else
getBlockMarkers().add(new BlockMarker(prefix + node.getNodeName(), null, DOMJSPRegionContexts.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(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
if (remainingRegions.hasNext() && (r = (ITextRegion) remainingRegions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
if (remainingRegions.hasNext() && (r = (ITextRegion) remainingRegions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
return StringUtils.stripQuotes(getCurrentNode().getText(r));
}
}
}
return null;
}
/**
* takes an iterator of the attributes of a page directive and the
* directive itself. The iterator is used in case it can be optimized, but
* the documentRegion is still required to ensure that the values are
* extracted from the correct text.
*/
protected void translatePageDirectiveAttributes(Iterator regions, IStructuredDocumentRegion documentRegion) {
ITextRegion r = null;
String attrName, attrValue;
// iterate all attributes
while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() != DOMJSPRegionContexts.JSP_CLOSE) {
attrName = attrValue = null;
if (r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
attrName = documentRegion.getText(r).trim();
if (attrName.length() > 0) {
if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
attrValue = StringUtils.strip(documentRegion.getText(r));
}
// has equals, but no value?
}
setDirectiveAttribute(attrName, attrValue);
}
}
}
}
/**
* sets the appropriate page/tag 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$
{
fIsInASession = Boolean.valueOf(attrValue).booleanValue();
}
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 && fProcessIncludes) {
IPath modelPath = getModelPath();
IPath basePath = modelPath;
if (basePath != null) {
/*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=227576
*
* The resolution of the included fragment should use the file
* containing the directive as the base reference, not always
* the main JSP being invoked. Verified behavior with Apache
* Tomcat 5.5.20.
*/
if (!getIncludes().isEmpty()) {
basePath = new Path((String) getIncludes().peek());
}
String filePathString = FacetModuleCoreSupport.resolve(basePath, filename).toString();
fIncludedPaths.add(filePathString);
if (!getIncludes().contains(filePathString) && !filePathString.equals(modelPath.toString())) {
getIncludes().push(filePathString);
JSPIncludeRegionHelper helper = new JSPIncludeRegionHelper(this, true);
// Should we consider preludes on this segment?
helper.parse(filePathString);
getIncludes().pop();
}
}
}
}
private Stack getIncludes() {
if (fIncludes == null)
fIncludes = new Stack();
return fIncludes;
}
public Collection getIncludedPaths() {
return fIncludedPaths;
}
protected void translateExpressionString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, embeddedContainer, true);
appendToBuffer(newText, fUserCode, true, embeddedContainer, jspPositionStart, jspPositionLength, isIndirect, true);
appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, embeddedContainer);
}
protected void translateExpressionString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength, String quotetype) {
if(quotetype == null || quotetype == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_DQUOTE ||quotetype == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_SQUOTE ) {
translateExpressionString(newText, embeddedContainer, jspPositionStart, jspPositionLength, false);
return;
}
//-- This is a quoted attribute. We need to unquote as per the JSP spec: JSP 2.0 page 1-36
appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, embeddedContainer, true);
int length = newText.length();
int runStart = 0;
int i = 0;
for ( ; i < length; i++) {
//-- collect a new run
char c = newText.charAt(i);
if (c == '\\') {
//-- Escaped value. Add the run, then unescape
int runLength = i-runStart;
if (runLength > 0) {
appendToBuffer(newText.substring(runStart, i), fUserCode, true, embeddedContainer, jspPositionStart, runLength, true, true);
jspPositionStart += runLength + 1;
jspPositionLength -= runLength + 1;
}
runStart = ++i;
if (i >= length) { // Escape but no data follows?!
//- error.
break;
}
c = newText.charAt(i); // The escaped character, copied verbatim
}
}
//-- Copy last-run
int runLength = i - runStart;
if (runLength > 0)
appendToBuffer(newText.substring(runStart, i), fUserCode, true, embeddedContainer, jspPositionStart, runLength, true, false);
appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, embeddedContainer);
}
protected void translateDeclarationString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
appendToBuffer(newText, fUserDeclarations, true, embeddedContainer, jspPositionStart, jspPositionLength, isIndirect);
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, boolean isIndirect) {
appendToBuffer(newText, fUserCode, true, embeddedContainer, jspPositionStart, jspPositionLength, isIndirect);
}
// the following 3 methods determine the cursor position
// <%= %>
protected void translateExpression(ITextRegionCollection region) {
String newText = getUnescapedRegionText(region, EXPRESSION);
appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, region, true);
appendToBuffer(newText, fUserCode, true, region, true);
appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, region);
}
//
// <%! %>
protected void translateDeclaration(ITextRegionCollection region) {
String newText = getUnescapedRegionText(region, DECLARATION);
appendToBuffer(newText, fUserDeclarations, true, region);
appendToBuffer(ENDL, fUserDeclarations, false, region);
}
//
// <% %>
protected void translateScriptlet(ITextRegionCollection region) {
String newText = getUnescapedRegionText(region, SCRIPTLET);
appendToBuffer(newText, fUserCode, true, region);
}
/**
* 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);
}
/**
* Append using a region, probably indirect mapping (eg. <%@page
* include=""%>)
*
* @param newText
* @param buffer
* @param addToMap
* @param jspReferenceRegion
* @param nonl
*/
private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion, boolean nonl) {
int start = 0, length = 0;
if (jspReferenceRegion != null) {
start = jspReferenceRegion.getStartOffset();
length = jspReferenceRegion.getLength();
}
appendToBuffer(newText, buffer, addToMap, jspReferenceRegion, start, length, false, nonl);
}
private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
appendToBuffer(newText, buffer, addToMap, jspReferenceRegion, jspPositionStart, jspPositionLength, isIndirect, false);
}
/**
* 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, boolean nonl) {
int origNewTextLength = newText.length();
// nothing to append
if (jspReferenceRegion == null)
return;
// add a newline so translation looks cleaner
if (! nonl && !newText.endsWith(ENDL))
newText += ENDL;
//dump any non translated code before writing translated code
writePlaceHolderForNonTranslatedCode();
//if appending to the buffer can assume something got translated
fCodeTranslated = true;
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, origNewTextLength);
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();
}
}
/**
*
* @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);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=128490
// length of 11 is the length of jsp:useBean
// and saves the expensive getText.equals call
if (r.getType() == DOMRegionContext.XML_TAG_NAME) {
if (r.getTextLength() == 11 && jspReferenceRegion.getText(r).equals("jsp:useBean")) { //$NON-NLS-1$
isUseBean = true;
}
// break no matter what if you hit tagname
break;
}
}
return isUseBean;
}
/**
* @param importName
* should be just the package plus the type eg. java.util.List
* or java.util.*
* @param jspReferenceRegion
* should be the <%@ page import = "java.util.List"%> region
* @param addToMap
*/
private void appendImportToBuffer(String importName, ITextRegionCollection jspReferenceRegion, boolean addToMap) {
String javaImportString = "import " + importName + ";" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$
fUserImports.append(javaImportString);
if (addToMap) {
addImportToMap(importName, jspReferenceRegion);
}
fOffsetInUserImports += javaImportString.length();
}
/**
* new text can be something like: "import java.lang.Object;\n"
*
* but the reference region could have been something like: <%@page
* import="java.lang.Object, java.io.*, java.util.List"%>
*
* so the exact mapping has to be calculated carefully.
*
* isIndirect means that the import came from an included file (if true)
*
* @param importName
* @param jspReferenceRegion
*/
private void addImportToMap(String importName, ITextRegionCollection jspReferenceRegion) {
// massage text
// String jspText = importName.substring(importName.indexOf("import ")
// + 7, importName.indexOf(';'));
// String jspText = importName.trim();
// these positions will be updated below
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();
int start = -1;
int length = -1;
ITextRegion r = null;
for (int i = 0; i < size; i++) {
r = regions.get(i);
if (r.getType() == DOMRegionContext.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() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
String jspImportText = jspReferenceRegion.getText(r);
// the position in question (in the JSP) is what
// is bracketed below
// includes whitespace
// <%@page import="java.lang.Object,[ java.io.* ],
// java.util.List"%>
// in the java file
// import [ java.io.* ];
start = jspImportText.indexOf(importName);
length = importName.length();
// safety, don't add to map if bad positioning
if (start == -1 || length < 1)
break;
// update jsp range
jspRange.setOffset(jspReferenceRegion.getStartOffset(r) + start);
jspRange.setLength(length);
// update java range
javaRange.setLength(length);
break;
}
}
}
}
// safety for bad ranges
if (start != -1 && length > 1) {
// put ranges in java -> jsp range map
fImportRanges.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);
/*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=212242 - Check for
* the existence of '(' first.
*/
int parenPos = -1;
if (className.length() >= 4 && (parenPos = className.indexOf('(')) >= 0) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=86132
int classNameLength = className.substring(0, parenPos).length();
javaClassRange = new Position(fOffsetInUserCode + type.length() + 1 + id.length() + 7, classNameLength);
}
// ---------------------
// calculate jsp ranges
// ---------------------
ITextRegionList regions = jspReferenceRegion.getRegions();
ITextRegion r = null;
String attrName = "", attrValue = ""; //$NON-NLS-1$ //$NON-NLS-2$
int quoteOffset = 0;
Position jspTypeRange = null;
Position jspIdRange = null;
Position jspClassRange = null;
for (int j = 0; j < regions.size(); j++) {
r = regions.get(j);
if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
attrName = jspReferenceRegion.getText(r);
if (regions.size() > j + 2 && regions.get(j + 2).getType() == DOMRegionContext.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() == DOMRegionContext.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() == DOMJSPRegionContexts.JSP_CONTENT || stRegion.getType() == DOMRegionContext.BLOCK_TEXT // need
// this
// for
// embedded
// JSP
// regions
|| stRegion.getType() == DOMRegionContext.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() == DOMJSPRegionContexts.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() != DOMRegionContext.XML_TAG_NAME && getCurrentNode().getType() != DOMJSPRegionContexts.JSP_CLOSE) // 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() == DOMRegionContext.XML_CONTENT || r.getType() == DOMRegionContext.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() == DOMRegionContext.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() == DOMRegionContext.XML_CDATA_TEXT) {
sb.append(getCurrentNode().getFullText(temp));
}
else if (temp.getType() == DOMRegionContext.XML_CDATA_OPEN || temp.getType() == DOMRegionContext.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;
ITextRegion idRegion = null;
String type = null;
ITextRegion typeRegion = null;
String className = null;
ITextRegion classnameRegion = null;
String beanName = null;
ITextRegion beanNameRegion = null;
if (DOMRegionContext.XML_END_TAG_OPEN.equals(container.getFirstRegion().getType())) {
if (!fUseBeansStack.isEmpty()) {
fUseBeansStack.pop();
appendToBuffer("}", fUserCode, false, fCurrentNode); //$NON-NLS-1$
}
else {
// no useBean start tag being remembered
ITextRegionCollection extraEndRegion = container;
IJSPProblem missingStartTag = createJSPProblem(IJSPProblem.UseBeanStartTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_4,JSP11Namespace.ElementName.USEBEAN), extraEndRegion.getStartOffset(), extraEndRegion.getEndOffset());
fTranslationProblems.add(missingStartTag);
}
return;
}
Iterator regions = container.getRegions().iterator();
while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && (r.getType() != DOMRegionContext.XML_TAG_CLOSE || r.getType() != DOMRegionContext.XML_EMPTY_TAG_CLOSE)) {
attrName = attrValue = null;
if (r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
attrName = container.getText(r).trim();
if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.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;
idRegion = r;
}
else if (attrName.equals("class")) {//$NON-NLS-1$
className = attrValue;
classnameRegion = r;
}
else if (attrName.equals("type")) {//$NON-NLS-1$
type = attrValue;
typeRegion = r;
}
else if (attrName.equals("beanName")) { //$NON-NLS-1$
beanName = attrValue;
beanNameRegion = r;
}
}
}
if (id != null) {
// The id is not a valid Java identifier
if (!isValidJavaIdentifier(id) && idRegion != null) {
Object problem = createJSPProblem(IJSPProblem.UseBeanInvalidID, IProblem.ParsingErrorInvalidToken, MessageFormat.format(JSPCoreMessages.JSPTranslator_0, new String[]{id}), container.getStartOffset(idRegion), container.getTextEndOffset(idRegion) - 1);
fTranslationProblems.add(problem);
}
// No Type information is provided
if (((type == null && className == null) || (type == null && beanName != null)) && idRegion != null) {
Object problem = createJSPProblem(IJSPProblem.UseBeanMissingTypeInfo, IProblem.UndefinedType, NLS.bind(JSPCoreMessages.JSPTranslator_3, new String[]{id}), container.getStartOffset(idRegion), container.getTextEndOffset(idRegion) - 1);
fTranslationProblems.add(problem);
}
// Cannot specify both a class and a beanName
if (className != null && beanName != null && beanNameRegion != null) {
ITextRegion nameRegion = container.getRegions().get(1);
Object problem = createJSPProblem(IJSPProblem.UseBeanAmbiguousType, IProblem.AmbiguousType, JSPCoreMessages.JSPTranslator_2, container.getStartOffset(nameRegion), container.getTextEndOffset(nameRegion) - 1);
fTranslationProblems.add(problem);
}
/*
* Only have a class or a beanName at this point, and potentially
* a type has id w/ type and/or classname/beanName
*/
// Type id = new Classname/Beanname();
// or
// Type id = null; // if there is no classname or beanname
if ((type != null || className != null)) {
if (className != null)
className = decodeType(className);
if (type == null) {
type = className;
typeRegion = classnameRegion;
}
else
type = decodeType(type);
/* Now check the types (multiple of generics may be involved) */
List errorTypeNames = new ArrayList(2);
if (!isTypeFound(type, errorTypeNames)) {
for (int i = 0; i < errorTypeNames.size(); i++) {
Object problem = createJSPProblem(IJSPProblem.F_PROBLEM_ID_LITERAL, IProblem.UndefinedType, MessageFormat.format(JSPCoreMessages.JSPTranslator_1, new String[]{errorTypeNames.get(i).toString()}), container.getStartOffset(typeRegion), container.getTextEndOffset(typeRegion) - 1);
fTranslationProblems.add(problem);
}
}
else {
String prefix = type + " " + id + " = "; //$NON-NLS-1$ //$NON-NLS-2$
String suffix = "null;" + ENDL; //$NON-NLS-1$
if (className != null)
suffix = "new " + className + "();" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$
else if (beanName != null)
suffix = "(" + type + ") java.beans.Beans.instantiate(getClass().getClassLoader(), \"" + beanName + "\");" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
appendToBuffer(prefix + suffix, fUserCode, true, fCurrentNode);
}
}
}
/*
* Add a brace and remember the start tag regardless of whether a
* variable was correctly created
*/
if (!DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(container.getLastRegion().getType())) {
fUseBeansStack.push(container);
appendToBuffer("{", fUserCode, false, fCurrentNode); //$NON-NLS-1$
}
}
/**
* Decodes type strings for XML-style JSPs
*
* @param type the string to decode
* @return the decoded string
*/
private String decodeType(String type) {
final int length = type.length();
final StringBuffer buffer = new StringBuffer(length);
for (int i = 0; i < length; i++) {
final char c = type.charAt(i);
if (c == '&') {
if (length > i + 3) {
final String code = type.substring(i + 1, i + 4);
final boolean isGt = "gt;".equals(code); //$NON-NLS-1$
if (isGt || "lt;".equals(code)) { //$NON-NLS-1$
i+=3;
buffer.append(isGt ? '>' : '<');
continue;
}
}
}
buffer.append(c);
}
return buffer.toString();
}
/**
* @param type
* @return
*/
private boolean isTypeFound(String rawTypeValue, List errorTypeNames) {
IFile file = getFile();
if(file == null)
return true;
IProject project = file.getProject();
IJavaProject p = JavaCore.create(project);
if (p.exists()) {
String types[] = new String[3];
if (rawTypeValue.indexOf('<') > 0) {
// JSR 14 : Generics are being used, parse them out
try {
StringTokenizer toker = new StringTokenizer(rawTypeValue);
// generic
types[0] = toker.nextToken("<"); //$NON-NLS-1$
// type 1 or key
types[1] = toker.nextToken(",>"); //$NON-NLS-1$
// type 2 or value
types[2] = toker.nextToken(",>"); //$NON-NLS-1$
}
catch (NoSuchElementException e) {
// StringTokenizer failure with unsupported syntax
}
}
else {
types[0] = rawTypeValue;
}
for (int i = 0; i < types.length; i++) {
if (types[i] != null) {
// remove any array suffixes
if (types[i].indexOf('[') > 0) {
types[i] = types[i].substring(0, types[i].indexOf('['));
}
// remove any "extends" prefixes (JSR 14)
if (types[i].indexOf("extends") > 0) { //$NON-NLS-1$
types[i] = StringUtils.strip(types[i].substring(types[i].indexOf("extends"))); //$NON-NLS-1$
}
addNameToListIfTypeNotFound(p, types[i], errorTypeNames);
}
}
}
return errorTypeNames.isEmpty();
}
private void addNameToListIfTypeNotFound(IJavaProject p, String typeName, List collectedNamesNotFound) {
try {
if (typeName != null) {
IType type = p.findType(typeName);
if (type == null || !type.exists()) {
collectedNamesNotFound.add(typeName);
}
else {
IResource typeResource = type.getResource();
if(typeResource != null) {
}
}
}
}
catch (JavaModelException e) {
// Not a Java Project
}
}
private boolean isValidJavaIdentifier(String id) {
char[] idChars = id.toCharArray();
if (idChars.length < 1)
return false;
boolean isValid = Character.isJavaIdentifierStart(idChars[0]);
for (int i = 1; i < idChars.length; i++) {
isValid = isValid && Character.isJavaIdentifierPart(idChars[i]);
}
return isValid;
}
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;
}
public IStructuredDocument getStructuredDocument() {
return fStructuredDocument;
}
private IPath getModelPath() {
IPath path = null;
ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(getStructuredDocument());
if (buffer != null) {
path = buffer.getLocation();
}
return path;
}
private void translateCodas() {
fProcessIncludes = false;
IPath modelpath = getModelPath();
if (modelpath != null) {
PropertyGroup[] propertyGroups = DeploymentDescriptorPropertyCache.getInstance().getPropertyGroups(modelpath);
for (int j = 0; j < propertyGroups.length; j++) {
IPath[] codas = propertyGroups[j].getIncludeCoda();
for (int i = 0; i < codas.length; i++) {
if (!getIncludes().contains(codas[i].toString()) && !codas[i].equals(modelpath)) {
getIncludes().push(codas[i]);
JSPIncludeRegionHelper helper = new JSPIncludeRegionHelper(this, true);
helper.parse(codas[i].toString());
getIncludes().pop();
}
}
}
}
fProcessIncludes = true;
}
private void translatePreludes() {
fProcessIncludes = false;
IPath modelpath = getModelPath();
if (modelpath != null) {
PropertyGroup[] propertyGroups = DeploymentDescriptorPropertyCache.getInstance().getPropertyGroups(modelpath);
for (int j = 0; j < propertyGroups.length; j++) {
IPath[] preludes = propertyGroups[j].getIncludePrelude();
for (int i = 0; i < preludes.length; i++) {
if (!getIncludes().contains(preludes[i].toString()) && !preludes[i].equals(modelpath)) {
getIncludes().push(preludes[i]);
JSPIncludeRegionHelper helper = new JSPIncludeRegionHelper(this, true);
helper.parse(preludes[i].toString());
getIncludes().pop();
}
}
}
}
fProcessIncludes = true;
}
/**
* <p>Writes an empty expression to {@link #fUserCode} if previously
* found non translated code</p>
* <p>This should be done before appending any newly translated code.</p>
*/
private void writePlaceHolderForNonTranslatedCode() {
if(fFoundNonTranslatedCode) {
String text = ("{" + EXPRESSION_PREFIX + "\"\"" + EXPRESSION_SUFFIX + "}" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
" //markup"+ ENDL); //$NON-NLS-1$
fUserCode.append(text);
fOffsetInUserCode += text.length();
fFoundNonTranslatedCode = false;
}
}
/**
* <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
* {@link #readExternal(ObjectInput)} and {@link #serialVersionUID}</p>
*
* @see #readExternal(ObjectInput)
* @see #serialVersionUID
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out) throws IOException {
writeString(out, this.fClassHeader);
writeString(out, this.fClassname);
writeString(out, this.fSuperclass);
writeString(out, this.fImplicitImports);
writeString(out, this.fServiceHeader);
writeBuffer(out, this.fUserImports);
out.writeInt(this.fSourcePosition);
out.writeInt(this.fRelativeOffset);
out.writeInt(this.fCursorPosition);
out.writeBoolean(this.fIsErrorPage);
out.writeBoolean(this.fCursorInExpression);
out.writeBoolean(this.fIsInASession);
writeBuffer(out, this.fUserCode);
writeBuffer(out, this.fUserELExpressions);
writeBuffer(out, this.fUserDeclarations);
writeBuffer(out, this.fCursorOwner);
out.writeBoolean(this.fInCodeRegion);
out.writeBoolean(this.fProcessIncludes);
out.writeInt(this.fOffsetInUserImports);
out.writeInt(this.fOffsetInUserDeclarations);
out.writeInt(this.fOffsetInUserCode);
//write included paths
out.writeInt(this.fIncludedPaths.size());
Iterator iter = this.fIncludedPaths.iterator();
while(iter.hasNext()) {
writeString(out, (String)iter.next());
}
writeRanges(out, this.fImportRanges);
writeRanges(out, this.fCodeRanges);
writeRanges(out, this.fDeclarationRanges);
writeRanges(out, this.fUseBeanRanges);
writeRanges(out, this.fUserELRanges);
writeRanges(out, this.fIndirectRanges);
writeString(out, this.fELTranslatorID);
}
/**
* <p><b>NOTE 1: </b>After reading in an externalized {@link JSPTranslator} the caller must
* manually call {@link #postReadExternalSetup(IStructuredModel)} to finish setting up
* the {@link JSPTranslator} for use.</p>
*
* <p><b>NOTE 2: </b>If the implementation of this method is changed be sure to update
* {@link #writeExternal(ObjectOutput)} and {@link #serialVersionUID}</p>
*
* @see #writeExternal(ObjectOutput)
* @see #serialVersionUID
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.fClassHeader = readString(in);
this.fClassname = readString(in);
this.fSuperclass = readString(in);
this.fImplicitImports = readString(in);
this.fServiceHeader = readString(in);
this.fUserImports = new StringBuffer(readString(in));
this.fSourcePosition = in.readInt();
this.fRelativeOffset = in.readInt();
this.fCursorPosition = in.readInt();
this.fIsErrorPage = in.readBoolean();
this.fCursorInExpression = in.readBoolean();
this.fIsInASession = in.readBoolean();
this.fUserCode = new StringBuffer(readString(in));
this.fUserELExpressions = new StringBuffer(readString(in));
this.fUserDeclarations = new StringBuffer(readString(in));
this.fCursorOwner = new StringBuffer(readString(in));
this.fInCodeRegion = in.readBoolean();
this.fProcessIncludes = in.readBoolean();
this.fOffsetInUserImports = in.readInt();
this.fOffsetInUserDeclarations = in.readInt();
this.fOffsetInUserCode = in.readInt();
//read included paths
int size = in.readInt();
this.fIncludedPaths = new HashSet(size);
for(int i = 0; i < size; ++i) {
this.fIncludedPaths.add(readString(in));
}
this.fImportRanges = readRanges(in);
this.fCodeRanges = readRanges(in);
this.fDeclarationRanges = readRanges(in);
this.fUseBeanRanges = readRanges(in);
this.fUserELRanges = readRanges(in);
this.fIndirectRanges = readRanges(in);
this.fELTranslatorID = readString(in);
//build result
this.buildResult(false);
}
/**
* <p>This does mandatory setup needed after a JSPTranslator is restored from
* a persisted file</p>
*
* @param jspModel {@link IStructuredModel} representing the JSP file that this
* is a translator for
*/
protected void postReadExternalSetup(IStructuredModel jspModel) {
this.fStructuredDocument = jspModel.getStructuredDocument();
this.fJspTextBuffer = new StringBuffer(this.fStructuredDocument.get());
if(jspModel instanceof IDOMModel) {
this.fStructuredModel = (IDOMModel)jspModel;
}
}
/**
* <p>Writes a string to an {@link ObjectOutput} stream</p>
*
* <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
* {@link #readString(ObjectInput)} and {@link #serialVersionUID}</p>
*
* @param out {@link ObjectOutput} stream to write <code>s</code> too
* @param s {@link String} to write to <code>out</code>
*
* @throws IOException IO can throw exceptions
*
* @see #readString(ObjectInput)
*/
private static void writeString(ObjectOutput out, String s) throws IOException {
if(s != null) {
out.writeInt(s.length());
out.writeChars(s);
} else {
out.writeInt(0);
}
}
/**
* <p>Reads a {@link String} written by {@link #writeString(ObjectOutput, String)} from
* a {@link ObjectInput} stream.</p>
*
* <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
* {@link #writeString(ObjectOutput, String)} and {@link #serialVersionUID}</p>
*
* @param in {@link ObjectInput} stream to read the {@link String} from
* @return {@link String} read from <code>in</code>
*
* @throws IOException IO Can throw exceptions
*
* @see #writeString(ObjectOutput, String)
*/
private static String readString(ObjectInput in) throws IOException {
int length = in.readInt();
char charArray[] = new char[length];
for(int i=0; i < length;i++){
charArray[i] = in.readChar();
}
return new String(charArray);
}
/**
* <p>Writes a {@link StringBuffer} to an {@link ObjectOutput} stream</p>
*
* @param out {@link ObjectOutput} stream to write <code>s</code> too
* @param s {@link String} to write to <code>out</code>
*
* @throws IOException IO can throw exceptions
*
* @see #readString(ObjectInput)
*/
private static void writeBuffer(ObjectOutput out, StringBuffer buff) throws IOException {
if(buff != null && buff.length() > 0) {
writeString(out, buff.toString());
} else {
writeString(out, null);
}
}
/**
* <p>Writes a {@link HashMap} of {@link Position}s to an {@link ObjectOutput} stream</p>
*
* <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
* {@link #readRanges(ObjectInput)} and {@link #serialVersionUID}</p>
*
* @param out {@link ObjectOutput} stream to write to
* @param ranges {@link HashMap} of {@link Position}s to write to <code>out</code>
*
* @throws IOException IO can throw exceptions
*
* @see #readRanges(ObjectInput)
*/
private static void writeRanges(ObjectOutput out, HashMap ranges) throws IOException {
//this is a strange hack because Position is not designed to be used as keys in a Map, see Position doc
HashMap temp = new HashMap();
temp.putAll(ranges);
Iterator iter = temp.keySet().iterator();
out.writeInt(ranges.size());
while(iter.hasNext()) {
Position javaPos = (Position)iter.next();
Position jspPos = (Position)temp.get(javaPos);
out.writeInt(javaPos.offset);
out.writeInt(javaPos.length);
out.writeBoolean(javaPos.isDeleted);
if(jspPos != null) {
out.writeInt(jspPos.offset);
out.writeInt(jspPos.length);
out.writeBoolean(jspPos.isDeleted);
} else {
out.writeInt(-1);
out.writeInt(-1);
}
}
}
/**
* <p>Reads a {@link HashMap} of {@link Position}s from an {@link ObjectInput} stream that was written by
* {@link #writeRanges(ObjectOutput, HashMap)}</p>
*
* <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
* {@link #writeRanges(ObjectOutput, HashMap)} and {@link #serialVersionUID}</p>
*
* @param in {@link ObjectInput} stream to read the {@link HashMap} of {@link Position}s from
* @return {@link HashMap} of {@link Position}s read from <code>in</code>
*
* @throws IOException IO can throw exceptions
*
* @see #writeRanges(ObjectOutput, HashMap)
*/
private static HashMap readRanges(ObjectInput in) throws IOException {
int size = in.readInt();
HashMap ranges = new HashMap(size);
for(int i = 0; i < size; ++i) {
Position javaPos = new Position(in.readInt(), in.readInt());
if(in.readBoolean()) {
javaPos.delete();
}
//if the jspPos was null for some reason then -1 was written for length and offset
Position jspPos = null;
int jspPosOffset = in.readInt();
int jspPosLength = in.readInt();
if(jspPosOffset != -1 && jspPosLength != -1) {
jspPos = new Position(jspPosOffset, jspPosLength);
}
//only read a boolean if the jspPos was not null
if(jspPos != null && in.readBoolean()) {
jspPos.delete();
}
ranges.put(javaPos, jspPos);
}
return ranges;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
boolean equal = false;
if(obj instanceof JSPTranslator) {
JSPTranslator other = (JSPTranslator)obj;
equal = this.fClassHeader.equals(other.fClassHeader) &&
this.fClassname.equals(other.fClassname) &&
this.fSuperclass.equals(other.fSuperclass) &&
this.fImplicitImports.equals(other.fImplicitImports) &&
this.fServiceHeader.equals(other.fServiceHeader) &&
buffersEqual(this.fUserImports, other.fUserImports) &&
this.fSourcePosition == other.fSourcePosition &&
this.fRelativeOffset == other.fRelativeOffset &&
this.fCursorPosition == other.fCursorPosition &&
this.fIsErrorPage == other.fIsErrorPage &&
this.fCursorInExpression == other.fCursorInExpression &&
this.fIsInASession == other.fIsInASession &&
buffersEqual(this.fUserCode, other.fUserCode) &&
buffersEqual(this.fUserELExpressions, other.fUserELExpressions) &&
buffersEqual(this.fUserDeclarations, other.fUserDeclarations) &&
buffersEqual(this.fCursorOwner, other.fCursorOwner) &&
this.fInCodeRegion == other.fInCodeRegion &&
this.fProcessIncludes == other.fProcessIncludes &&
this.fOffsetInUserImports == other.fOffsetInUserImports &&
this.fOffsetInUserDeclarations == other.fOffsetInUserDeclarations &&
this.fOffsetInUserCode == other.fOffsetInUserCode &&
rangesEqual(this.fImportRanges, other.fImportRanges) &&
rangesEqual(this.fCodeRanges, other.fCodeRanges) &&
rangesEqual(this.fDeclarationRanges, other.fDeclarationRanges) &&
rangesEqual(this.fUseBeanRanges, other.fUseBeanRanges) &&
rangesEqual(this.fUserELRanges, other.fUserELRanges) &&
rangesEqual(this.fIndirectRanges, other.fIndirectRanges) &&
(
(this.fELTranslatorID != null && other.fELTranslatorID != null) ||
(this.fELTranslatorID == null && other.fELTranslatorID != null && other.fELTranslatorID.length() == 0) ||
(this.fELTranslatorID != null && this.fELTranslatorID.length() == 0 && other.fELTranslatorID == null) ||
(this.fELTranslatorID.equals(other.fELTranslator))
);
}
return equal;
}
/**
* <p><code>null</code> is considered equivlent to an empty buffer</p>
*
* @param buff1 can be <code>null</code>
* @param buff2 can be <code>null</code>
* @return <code>true</code> if the two given buffers are equal, <codee>false</code> otherwise
*/
private static boolean buffersEqual(StringBuffer buff1, StringBuffer buff2) {
return (buff1 == null && buff2 == null) ||
(buff1 != null && buff2!= null && buff1.toString().equals(buff2.toString())) ||
(buff1 == null && buff2 != null && buff2.length() == 0) ||
(buff1 != null && buff1.length() == 0 && buff2 == null);
}
/**
* @param ranges1
* @param ranges2
* @return <code>true</code> if the two maps of ranges contains the same key/value pares,
* <code>false</code> otherwise
*/
private static boolean rangesEqual(HashMap ranges1, HashMap ranges2) {
//this is a strange hack because Position is not designed to be used as keys in a Map, see Position doc
HashMap temp = new HashMap();
temp.putAll(ranges1);
ranges1 = temp;
boolean equal = false;
if(ranges1 != null && ranges2 != null) {
equal = true;
Iterator ranges1Keys = ranges1.keySet().iterator();
while(ranges1Keys.hasNext() && equal) {
Position key = (Position)ranges1Keys.next();
Position ranges1Value = (Position)ranges1.get(key);
Position ranges2Value = (Position)ranges2.get(key);
equal = ranges1Value.equals(ranges2Value);
}
}
return equal;
}
}