/*
* Created on Mar 21, 2004
*
* The MIT License
* Copyright (c) 2004 Oliver Tupman
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cfeclipse.cfml.parser;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.antlr.runtime.BitSet;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.IntStream;
import org.antlr.runtime.ParserRuleReturnScope;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.cfeclipse.cfml.CFMLPlugin;
import org.cfeclipse.cfml.dictionary.DictionaryManager;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlComment;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagCase;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagCatch;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagDefaultCase;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagElse;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagElseIf;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagFunction;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagIf;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagInvokeArgument;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagProperty;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagQueryParam;
import org.cfeclipse.cfml.parser.cfmltagitems.CfmlTagSet;
import org.cfeclipse.cfml.parser.docitems.AttributeItem;
import org.cfeclipse.cfml.parser.docitems.CfmlCustomTag;
import org.cfeclipse.cfml.parser.docitems.CfmlTagItem;
import org.cfeclipse.cfml.parser.docitems.DocItem;
import org.cfeclipse.cfml.parser.docitems.ScriptItem;
import org.cfeclipse.cfml.parser.docitems.TagItem;
import org.cfeclipse.cfml.preferences.ParserPreferenceConstants;
import org.cfeclipse.cfml.properties.CFMLPropertyManager;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.texteditor.MarkerUtilities;
import cfml.parsing.CFMLParser;
import cfml.parsing.cfscript.ANTLRNoCaseReaderStream;
import cfml.parsing.cfscript.CFScriptLexer;
import cfml.parsing.cfscript.CFScriptParser;
import cfml.parsing.cfscript.IErrorReporter;
import cfml.parsing.cfscript.poundSignFilterStream;
import cfml.parsing.cfscript.script.CFScriptStatement;
/*
Nasty, bastard test data for the parser:
<html>
<cffunction name="test">
<cfargument name="fred" test="test"/>
<cfscript>
WriteOutput("FREDFREDFRED");
</cfscript>
<cfif thisisatest is 1>
<cfoutput>#fred#</cfoutput>
</cfif>
</cffunction>
<cfscript>
todaysDate = now();
function doSomething(String doWhat) {
var done = arguments.doWhat & " later";
return done;
}
function returnSomething(theThing) {
return theThing;
}
</cfscript>
<cfset fred = 2/>
<cfset bob = doSomething("build a parser") />
<cfset test(fred)/>
<cffunction name="test" >
<cfset var woo="hoo" />
<cfargument name="test" default="#WriteOutput("">"")#"/> <!--- I think this is valid! --->
</cffunction>
<cfoutput>
This is a <b>test</b>
</cfoutput>
<table>
<tr>
<td style="<cfoutput>#somethinghere#</cfoutput>">asdfasdf</td>
<td style="fred"></td>
</td>
</table>
</html>
*/
/**
* @author Oliver Tupman
*
* CFParser basically parses a CF file and builds the document tree from the result.
*
* Currently the document tree only contains CF elements as the HTML handling isn't implemented.
* HTML tags are matched, just not inserted into the document tree.
* I believe we need some kind of server-side/client-side view. The reasoning for this is that
* at the client-side one only sees HTML and no CF. But the documents we are viewing can contain
* embedded CF therefore breaking some of the attributes - we may not know the correct values
* because the embedded CF decides it.
*/
public class CFParser {
/**
* <code>REG_TAG</code> - the regular expression for matching tags. NB: Doesn't work on multi-line tags :(
* TODO: Either modify the REG_TAG regex to match multiline tags or completely rewrite the tag matcher.
*/
static protected final String REG_TAG = "<(\\w*)(.*)/{0,1}>";
// the below matches multiple lines, but doesn't seem to do anything different
//static protected final String REG_TAG = "(?s)<(\\w*)(.*)/{0,1}>";
/**
* <code>REG_ATTRIBUTES</code> - regular expression for getting the attributes out of a tag match.
* (\w+\s?=\s?)?((((\w+ & )?\x22|\x27)((?!\4).|\4{2})*\4?(.*&.*)?))
*/
/*
* Various "almost" regexes, might have to use something dynamical-er :-/
* this gets any double-quote/apos escaped strings ("woo ""hoo"" man!" or ' ''blah''! ')
* (\x22|\x27)((?!\1).).*((\1{2})+.*)\1
*
* this one is super close -- gets any string var, just can't get the attrib name first :(
* (\x22|\x27)((?!\1).|\1{2})*\1
*
* (\w+)[\s=]+(((\x22|\x27|#)((?!\4).|\4{2})*\4))
*
* Holy crap, right? Maybe better to split it? gets: [blah] = "wee ""hoo"" you!", etc. but no &s
* (\w+\s?=)?((\w+\s?\&\s?)?([\&?\w?\s?&\s?\&?]?\x22|\x27)((?!\4).|\4{2})*\4(\s?&?\s?\w+\s?&?\s)?)
* same, but better
* (\w+\s?=\s?)?((\x22|\x27)((?!\3).|\3{2})*\3?(.*&.*)?)
* bester (gets: [blah=] [fun & "wee ""hoo"" you!" & whatever]) we'll roll with this!
* (\w+\s?=\s?)?((((\w+ & )?\x22|\x27)((?!\4).|\4{2})*\4?(.*&.*)?))
* bestest (captures woo= #hoo# stuff too!)
* (\w+)[\s?=\s?]+?((((\w+ & )?\x22|\x27|#)((?!\4).|\4{2})*\4?(.*&.*)?))
* bestester?
* (\w+)[\s=]+((((\w+ & )?\x22|\x27|#)((?!\4).|\4{2})*\4?(\s*&\s*\w*)*))
*
* (\w+)[\s=]+(((\x22|\x27|#)((?!\4).|\4{2})*\4)(\s*&\s*\w+)*(\s&\s)*)
*
* Wow...
* (\w+)[\s=]+(((\x22|\x27|#)((?!\4).|\4{2})*\4)(\s*&\s*\w+)*(\s*\&\s*((\x22|\x27|#)((?!\4).|\4{2})*\4))*)
* At some point, parsing the string by hand will be faster :-)p
* (\w+)[\s=]+(((\x22|\x27|#)((?!\4).|\4{2})*\4)(\s*&\s*\w+)*(\s*\&\s*((\x22|\x27|#)((?!\4).|\4{2})*\4))*(\s*&\s*\w+)*)
* This handles everything, apparently, but ouch!
* (\w+)\s*=\s*(((\x22|\x27|#)((?!\4).|\4{2})*\4)((\s*&\s*\w+)*(\s*\&\s*((\x22|\x27|#)((?!\10).|\10{2})*\10))*(\s*&\s*\w+)*)*)
*/
//static protected final String REG_ATTRIBUTES = "(\\w+)\\s*=\\s*(((\\x22|\\x27|#)((?!\\4).|\\4{2})*\\4)((\\s*&\\s*\\w+)*(\\s*\\&\\s*((\\x22|\\x27|#)((?!\\10).|\\10{2})*\\10))*(\\s*&\\s*\\w+)*)*)";
// this was closest out of all so far
//static protected final String REG_ATTRIBUTES = "(\\w+)?[\\s=]?((((\\w+ & )?\\x22|\\x27)((?!\\4).|\\4{2})*\\4?(.*&.*)?))";
// static protected final String REG_ATTRIBUTES_BRACKETS= "(?<!\\\\)\\[(\\\\\\[|\\\\\\]|[^\\[\\]]|(?<!\\\\)\\[.*(?<!\\\\)\\])*(?<!\\\\)\\]";
static protected final String REG_ATTRIBUTES_BRACKETS= "[^\\[]*(\\[.*\\])[^\\]]*";
// static protected final String REG_ATTRIBUTES_BRACES = "(?<!\\\\)\\{(\\\\\\{|\\\\\\}|[^\\{\\}]|(?<!\\\\)\\{.*(?<!\\\\)\\})*(?<!\\\\)\\}";
static protected final String REG_ATTRIBUTES_BRACES = "[^\\{]*(\\{.*\\})[^\\}]*";
static protected final String REG_ATTRIBUTES_BB = "(?s)(\\w+)+\\s*+=\\s*+(" + REG_ATTRIBUTES_BRACKETS + "|" + REG_ATTRIBUTES_BRACES + ")";
// unescaped: Curly ({}): (?<!\\)\{(\\\{|\\\}|[^\{\}]|(?<!\\)\{.*(?<!\\)\})*(?<!\\)\}
// static protected final String REG_ATTRIBUTES = "(?s)(\\w++)\\s?=\\s?+((((\\w++ & )?\\x22|\\x27|#)((?!\\4).|\\4{2})*\\4?(.*&.*)?))";
static protected final String REG_ATTRIBUTES = "(?si)(\\w+)[\\s=]+(((\\x22|\\x27|#)((?!\\4).|\\4{2})*\\4))";
// unescaped: (\w++)[\s=]+((((\w++ & )?\x22|\x27|#)((?!\4).|\4{2})*\4?(.*&.*)?))
static protected final int USRMSG_INFO = 0x00;
static protected final int USRMSG_WARNING = 0x01;
static protected final int USRMSG_ERROR = 0x02;
protected State parserState = null;
protected IResource res = null;
/** Tells the parser whether it should parse CFScript blocks or not. Pretty buggy at the moment */
protected boolean parseCFScript = false;
protected boolean reportErrors = true;
/**
* Tells the parser whether it should be reporting the errors it finds.
*
* @param enable set to true to report errors to the Problems view or false to not.
*/
public void setReportErrors(boolean enable) {
this.reportErrors = enable;
}
/**
* Tells the parser whether it should parse CFScript blocks or not
* @param enable set to true to parse CFScript, false to not
*/
public void setCFScriptParsing(boolean enable) {
this.parseCFScript = enable;
}
/**
* <code>parseDoc</code> - the document to parse. Could just use a string, but IDocument provides line number capabilities.
*/
protected IDocument parseDoc = null;
/**
* <code>docFilename</code> - the pathname of the document, so we can stick messages into the Problems tasklist
*/
protected IPath docFilename = null; // Document file info... not working just yet.
/**
* <code>parseResult</code> - the resultant document tree.
* <b>NB:</b> Currently the root node is called 'root' and has no real data, it's just a root node.
*/
protected CFDocument parseResult = null; // The end result of the parse.
protected String data2Parse = "";
/**
* <code>getParseResult</code> - Get's the document tree from a parse
* @return The CF document tree that results from calling <code>parseDoc()</code>
*/
public CFDocument getParseResult() {
if(parseResult == null)
//System.err.println("CFParser::getParseResult() - WARNING: parseResult is null!");
;
if(parserState == null)
//System.err.println("CFParser::getParseResult() - WARNING: parserState is null. This probably means that the parser has not been run.");
;
return parseResult;
}
/**
* <code>CFParser</code> Constructor without params.
*
* Just sets the parseDoc to null.
*
*/
public CFParser() {
super();
parseDoc = null;
data2Parse = null;
}
private void setData2Parse(String data) {
//SPIKE: Added the toLowerCase() as a quick hack to sort out the case sensitivity issues.
//OLLIE: Stuck a generic call to set the data to parse to put the toLowerCase() in the same
// place. This enables better debugging of the actual cause of the problem.
// this.data2Parse = data.toLowerCase();
this.data2Parse = data;
}
private void initOrgCFParser() {
if (parser == null) {
CFMLPropertyManager propertyManager = new CFMLPropertyManager();
if (res == null) {
IEditorPart editor = CFMLPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
String dict = propertyManager.getCurrentDictionary(((IFileEditorInput) editor.getEditorInput()).getFile().getProject());
parser = CFMLPlugin.newCFMLParser(dict);
} else {
String dict = propertyManager.getCurrentDictionary(res.getProject());
parser = CFMLPlugin.newCFMLParser(dict);
}
}
}
/**
* <code>CFParser</code> - Constructor.
*
* @param doc2Parse - the IDocument doc to parse
* @param filename - the pathname of the file so we can attempt to report stuff to the user
*/
public CFParser(IDocument doc2Parse, IResource newRes)
{
parseDoc = doc2Parse;
this.setData2Parse(this.parseDoc.get());
res = newRes;
initOrgCFParser();
}
public CFParser(String inData, IResource dataRes)
{
this.setData2Parse(inData);
res = dataRes;
initOrgCFParser();
}
/**
* The line offsets within the parsed file.
* Element 0 is line 1 and the value is the end of the line doc offset
* Element 1 is line 2 and the value is the end of line 2 in a doc offset, and so on.
*/
protected int [] lineOffsets = null;
/**
* Returns the line number that the document offset lies on.
* @param docOffset
* @return the line number, 0 indexed (i.e. 0 is line 1)
*/
protected int getLineNumber(int docOffset)/* throws BadLocationException */
{
/*
* Line number calculation simply goes through the lineOffset var looking for when the
* line offset is greater than the docOffset passed.
*
* Should this method throw a BadLocationException???
*/
//
// Quick check to make sure it's not negative.
if(docOffset < 0)
return 0;//throw new BadLocationException("Doc offset is less than 0. Value was " + docOffset);
for(int i = 0; i < lineOffsets.length; i++)
{
if(lineOffsets[i] > docOffset)
return i;
}
//
// We should never reach here. Throw a BadLocationException;
//throw new BadLocationException("Doc offset " + docOffset + " is out of range. Max value is " + lineOffsets[lineOffsets.length]);
return 0;
}
/**
* <code>userMessage</code> - Outputs a message at a certain tree depth to the console
* @param indent - the indent to use
* @param method - the method that is doing the calling, so we can keep track nicely
* @param message - the message to give to the user.
*/
protected void userMessage(int indent, String method, String message)
{
////System.out..println("CFParser::userMessage() - " + Util.GetIndent(indent) + "CFParser::" + method + "() - " + message);
}
/**
* <code>userMessage</code> - Outputs a message at a certain tree depth to the console.
* Also allows the passing of message types so the method can decide whether to report
* the message to the user or not.
*
* <b>NB:</b> USRMSG_ERRORs are inserted into the Problems tasklist <b>and will not disappear</b>
* until you restart Eclipse.
* TODO: Need to work out how to keep track of markers & invalidate them as the parser is run again & again.
*
* @param indent - the indent to output the message at
* @param method - the method doing the calling
* @param message - the message
* @param msgType - the type of message. CFParser.USERMSG_* (i.e. CFParser.USERMSG_ERROR is an error to the user)
*/
protected void userMessage(int indent, String method, String message, int msgType, ParseItemMatch match)
{
switch(msgType)
{
case USRMSG_WARNING:
userMessage(indent, method, "WARNING: " + message);
break;
case USRMSG_ERROR:
if(this.reportErrors && this.res != null) {
/*
IWorkspaceRoot myWorkspaceRoot = CFMLPlugin.getWorkspace().getRoot();
parserState.addMessage(new ParseError(
getLineNumber(match.getStartPos()), match.getStartPos(), match.getStartPos() + match.getMatch().length(), match.getMatch(),
message
));
*/
try {
//
// Not sure what the start & end positions are good for!
//MarkerUtilities.createMarker(this.res, attrs, IMarker.PROBLEM);
IMarker marker = this.res.createMarker("org.cfeclipse.cfml.parserProblemMarker");
Map attrs = new HashMap();
MarkerUtilities.setLineNumber(attrs, match.lineNumber+1);
MarkerUtilities.setMessage(attrs, message);
MarkerUtilities.setCharStart(attrs, match.startPos);
MarkerUtilities.setCharEnd(attrs, match.endPos);
marker.setAttributes(attrs);
marker.setAttribute(IMarker.MESSAGE,message);
MarkerUtilities.createMarker(this.res, attrs, IMarker.PROBLEM);
}catch(CoreException excep) {
userMessage(0, "userMessage", "ERROR: Caught CoreException when creating a problem marker. Message: \'" + excep.getMessage() + "\'");
}
}
break;
default:
case USRMSG_INFO:
userMessage(indent, method, message);
break;
}
}
/**
* <code>stripAttributes</code> - Strips the attributes from some data and puts them into a hash map
* @param inData - the string data to get the attributes out of
* @param match
* @return array list of the attributes found. May contain duplicates
*/
protected ArrayList stripAttributes(String inData, int lineNum, int offset, ParseItemMatch match)
{
ArrayList attributes = new ArrayList();
Matcher matcher;
Pattern pattern;
String attributeName,attributeValue;
pattern = Pattern.compile(REG_ATTRIBUTES,Pattern.CASE_INSENSITIVE);
matcher = pattern.matcher(inData);
if(inData.trim().endsWith("&")){
userMessage(0,
"stripAttributes", "Last attribute cannot be an ampersand",
USRMSG_ERROR, match);
}
while(matcher.find())
{
if (matcher.group(1) != null && matcher.group(2) != null) {
AttributeItem newAttr;
attributeName = matcher.group(1).trim();
attributeValue = matcher.group(2).trim();
attributeValue = attributeValue.substring(1,attributeValue.length()-1);
newAttr = new AttributeItem(lineNum, offset + matcher.start(1), offset + matcher.end(1),
attributeName, attributeValue);
attributes.add(newAttr);
//System.out.println(attributeName + " = " +attributeValue);
}
else {
System.out.println("CFParser::stripAttributes() - failed on |" + inData + "| with " + matcher.groupCount() + " matches");
// for (int i = 0; i<=matcher.groupCount(); i++) {
// System.out.println("Match " + i + " : " + matcher.group(i));
// }
}
}
return attributes;
}
/**
* <code>handleClosingTag</code> - Handles a closing tag in the document
* @return true - everything okay, false - error during parsing.
* @param match the match that's a closer
* @param matchStack - the stack of matched items
*/
protected boolean handleClosingTag(ParseItemMatch match, Stack matchStack)
{
/*
* Quite simply it works out what the item is. Then it grabs the top-most item
* out of the matchStack, as that's the most recent opening tag. If the topItem
* matches the closing tag we're okay. We get the next item off the stack which
* is the tag's parent and add the opener & closer to the parent's children.
* Then we push the parent back onto the stack to wait for another day.
*
* If the item does not match the most recent item we've a problem. At the moment
* it reports an error and throws away the closer.
*/
//System.out..println("CFParser::handleClosingTag() - " + Util.GetTabs(matchStack) + "Parser: Found closing tag of " + match.match);
// Closing tag, so we attempt to match it to the current top of the stack.
String closerName = match.match;
//SPIKE: Added the toLowerCase()
if(closerName.toLowerCase().indexOf("</cf") != -1)
{
// CF tag
closerName = closerName.substring(2, closerName.length()-1);
DocItem topItem = (DocItem)matchStack.peek();
//System.out.println("Top item on stack is " + topItem.getName());
if(topItem instanceof TagItem)
{
// Look for hybrids at the top of the stack
// and remove them if there is an opener below them.
try {
boolean foundCloser = false;
ArrayList removals = new ArrayList();
Object[] items = matchStack.toArray();
//System.out.println("Looking on stack for opening " + closerName + ". Closer found on line: " + this.getLineNumber(match.getStartPos()));
for (int i=items.length-1;i>0;i--) {
if (items[i] instanceof TagItem) {
TagItem item = (TagItem)items[i];
//System.out.println("Checking " + item.getName());
if (item.getName().equalsIgnoreCase(closerName)) {
//System.out.println("Found opener. Exiting loop.");
foundCloser = true;
break;
} else if (item.isHybrid()) {
removals.add(item);
}
}
}
// If we found a closer, we want to remove any unclosed hybrids.
if (foundCloser) {
items = removals.toArray();
DocItem parent = (DocItem)matchStack.get(items.length);
for (int i=0;i<items.length;i++) {
TagItem item = (TagItem)items[i];
//System.out.println(item.getChildNodes().size() + " children need to be moved to " + parent.getName());
Object[] orphans = item.getChildNodes().toArray();
if (item instanceof CfmlCustomTag) {
((CfmlCustomTag)item).hasCloser = false;
parent.addChild(item);
item.setParent(parent);
}
for (int j=0;j<orphans.length;j++) {
DocItem orphan = (DocItem)orphans[j];
//System.out.println("Moving " + orphan.getName() + " under " + parent.getName());
parent.addChild(orphan);
item.removeChild(orphan);
}
//System.out.println("Removing " + ((TagItem)items[i]).getName() + " from the stack." + " Current parent is " + parent.getName());
matchStack.remove(items[i]);
}
Iterator iter = parent.getChildNodes().iterator();
while (iter.hasNext()) {
DocItem di = (DocItem)iter.next();
//System.out.println("Child: " + di.getName());
}
}
else {
//System.out.println("Opener not found on stack for " + closerName);
//System.out.println(" ");
}
}
catch (Exception e) {
e.printStackTrace();
}
try {
TagItem tempItem = new TagItem(match.lineNumber, match.startPos, match.endPos+1, match.match);
((TagItem)topItem).setMatchingItem(tempItem);
} catch(Exception e){
System.err.println("Caught exception: " + e.getMessage());
e.printStackTrace();
}
}
// Take the top item off the stack.
topItem = (DocItem)matchStack.pop(); // Should be the opening item for this closer
//System.out..println("CFParser::handleClosingTag() - " + Util.GetTabs(matchStack) + "Parser: Does \'" + closerName + "\' match \'" + topItem.itemName + "\'");
//SPIKE: Made this case insensitive
if(topItem.getName().compareToIgnoreCase(closerName) == 0)
{
//System.out.println("Found matcher at top of stack!!!");
DocItem parentItem = (DocItem)matchStack.pop();
try {
parentItem.addChild(topItem);
}
catch(Exception excep)
{
//
// Tell the user there was a problem and then rethrow the exception.
//System.out..println("CFParser::handleClosingTag() - Caught exception \'" + excep.getMessage() + "\'");
excep.printStackTrace();
//System.out..println(excep.hashCode());
throw (RuntimeException)excep.fillInStackTrace();
}
matchStack.push(parentItem);
}
else
{
//
// If we're here that means that the top item of the match stack isn't the
// opener of the current closer. Therefore we report this as an error
// and finish parsing as we can't easily make sense of the rest of the document.
ParseItemMatch tempMatch = new ParseItemMatch(match.match, match.startPos, match.endPos,
getLineNumber(match.startPos), MATCHER_NOTHING);
userMessage(matchStack.size(),
"handleClosingTag", "Closing tag \'" + match.match +
"\' does not match the current parent item: \'" + topItem.getName() + "\'",
USRMSG_ERROR, tempMatch);
return false;
//
// So we just push the top item back onto the stack, ready to be matched again.
// NB: Note that this only copes with extra closing tags, not extra opening tags.
//matchStack.push(topItem);
}
}
return true;
}
private CFMLParser parser;
private Stack<DocItem> matchStack;
private CommonTokenStream tokenStream;
private void addParseError(int lineNumber, int startPos, int endPos, String message) {
IMarker marker;
try {
if (this.res != null) {
marker = this.res.createMarker("org.cfeclipse.cfml.parserProblemMarker");
Map attrs = new HashMap();
MarkerUtilities.setLineNumber(attrs, lineNumber);
MarkerUtilities.setMessage(attrs, message);
MarkerUtilities.setCharStart(attrs, startPos);
MarkerUtilities.setCharEnd(attrs, endPos);
marker.setAttributes(attrs);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
marker.setAttribute(IMarker.MESSAGE, message);
}
// MarkerUtilities.createMarker(this.res, attrs, IMarker.PROBLEM);
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public class StdErrReporter implements IErrorReporter {
private String getTokenName(int i) {
Class<CFScriptParser> c = CFScriptParser.class;
for (Field f : c.getDeclaredFields()) {
int mod = f.getModifiers();
if (Modifier.isStatic(mod) && Modifier.isPublic(mod) && Modifier.isFinal(mod)) {
try {
System.out.printf("%s = %d%n", f.getName(), f.get(null));
if ((Integer) f.get(null) == i)
return f.getName();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return "";
}
public void reportError(String error) {
addParseError(0, 0, 0, "(no stracktrace): " + error);
}
public void reportError(RecognitionException re) {
int offset = getLineOffset(re.line) + re.charPositionInLine;
int endoffset = offset + 1;
String tokenText = "";
if (re.token != null) {
endoffset += re.token.getText().length();
if (re.token.getText() != null) {
tokenText = re.token.getText();
}
}
addParseError(re.line, offset, endoffset, re.getMessage() + " " + tokenText + " " + re.getUnexpectedType()
+ re.getClass().getName());
//re.printStackTrace();
// System.out.println(re.getMessage() + " line:" + re.line);
}
public void reportError(String[] tokenNames, RecognitionException re) {
System.err.println(tokenNames);
int offset = getLineOffset(re.line) + re.charPositionInLine;
int endoffset = offset + 1;
if (re.token != null) {
endoffset += +re.token.getText().length();
}
addParseError(re.line, offset, endoffset, re.getMessage());
// System.err.println(re.getMessage());
}
public void reportError(IntStream input, RecognitionException re, BitSet follow) {
int offset = getLineOffset(re.line) + re.charPositionInLine;
int endoffset = offset + 1;
if (re.token != null) {
endoffset += +re.token.getText().length();
}
addParseError(re.line, offset, endoffset, re.getMessage());
System.err.println(re.getMessage());
}
}
/**
* <code>handleCFScriptBlock</code> - handles a CFScript'd block (at the moment it does nothing)
* @param match - the match
* @param matchStack - the stack of tag items. This will have all of the new Script items added to it.
*/
protected void handleCFScriptBlock(ParseItemMatch match, Stack matchStack)
{
String mainData = match.match;
mainData = mainData.toLowerCase().substring("<cfscript>".length());
StringReader tempRdr =new StringReader(mainData);
CFScriptStatement rootElement = null;
if(!this.parseCFScript) {
return;
}
parseCFScript(mainData, match.getLineNumber(), match.startPos);
return;
}
protected void parseCFScript(String mainData) {
if(mainData.length() > 9) {
parseCFScript(mainData, 0, 0);
}
}
protected void parseCFScript(String mainData, int addLines, int addOffset) {
mainData = mainData.replaceFirst("<cfscript>", "");
CFScriptStatement rootElement = null;
if (!this.parseCFScript) {
return;
}
if (parser == null) {
initOrgCFParser();
} else {
parser.getMessages().removeAll(parser.getMessages());
parserState.getMessages().removeAll(parserState.getMessages());
}
try {
char[] scriptWithEndTag = mainData.toCharArray();
poundSignFilterStream psfstream = new poundSignFilterStream(new CharArrayReader(scriptWithEndTag));
ANTLRNoCaseReaderStream input = new ANTLRNoCaseReaderStream(psfstream); // +
//ANTLRNoCaseReaderStream input = new ANTLRNoCaseReaderStream(new CharArrayReader(scriptWithEndTag)); // +
CFScriptLexer lexer = new CFScriptLexer(input);
tokenStream = new CommonTokenStream(lexer);
CFScriptParser parser = new CFScriptParser(tokenStream);
StdErrReporter errorReporter = new StdErrReporter();
lexer.setErrorReporter(errorReporter);
parser.setErrorReporter(errorReporter);
try {
// "</CFSCRIPT>")
// )
// );
ParserRuleReturnScope r = parser.scriptBlock();
CommonTree tree = (CommonTree) r.getTree();
// first item is EOF, so get kids to get kids
if (tree == null) {
ScriptItem errorNode = new ScriptItem(0, 0, 0, "error");
errorNode.setItemData("Error creating outline: " + parserState.getMessages().toString());
addDocItemToTree(errorNode);
} else {
scriptItemTree(tree, matchStack.peek(), addLines, addOffset, true);
}
} catch (RecognitionException e) {
e.printStackTrace();
} catch (org.antlr.runtime.tree.RewriteEmptyStreamException e) {
ScriptItem errorNode = new ScriptItem(0, 0, 0, "error");
errorNode.setItemData("Error parsing: " + parserState.getMessages().toString());
addDocItemToTree(errorNode);
}
} catch (cfml.parsing.cfscript.ParseException e) {
parserState.addMessage(new ParseError(e.getLine() + addLines, e.getCol() + addOffset, e.getCol() + addOffset, e.getMessage()
+ e.getLine(), e
.getMessage() + e.getLine() + ":" + e.getCol()));
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return;
}
private Map<String, Integer> getTreeLineAndOffset(CommonTree tree, Boolean recurse) {
HashMap<String, Integer> lineAndOffset = new HashMap<String, Integer>();
while (tree.getTokenStartIndex() < 0) {
tree = (CommonTree) tree.getChild(0);
}
Token startToken = tokenStream.get(tree.getTokenStartIndex());
Token stopToken = tokenStream.get(tree.getTokenStopIndex());
int length = 0;
int endOffset = getLineOffset(stopToken.getLine()) + stopToken.getCharPositionInLine();
int offset = getLineOffset(startToken.getLine()) + startToken.getCharPositionInLine();
if (offset == -1) {
offset = 0;
endOffset = 0;
} else if (endOffset == offset) {
endOffset = endOffset + startToken.getText().length() - 1;
} else {
length = startToken.getText().length();
}
lineAndOffset.put("line", startToken.getLine());
lineAndOffset.put("offset", offset);
lineAndOffset.put("length", length);
lineAndOffset.put("endoffset", endOffset);
return lineAndOffset;
}
private void scriptItemTree(CommonTree tree, DocItem rootNode, int addLines, int addOffset, boolean root) {
Map<String, Integer> lineAndOffset = getTreeLineAndOffset(tree, true);
int startLine = lineAndOffset.get("line");
int offset = lineAndOffset.get("offset");
int startPos = offset;
int endPos = lineAndOffset.get("endoffset");
ScriptItem node = getScriptNode(tree, startLine, startPos, endPos);
if (node == null) {
// hidden nodes like function bodies ("{", etc.)
node = (ScriptItem) rootNode;
} else {
// node.setItemData(node.getItemData() + " line:" + startLine + " length:" + lineAndOffset.get("length") + " offset:" + startPos
// + " endoffset:" + endPos);
// node.setItemData(node.getItemData() + " line:" + node.getLineNumber() + " length:" + lineAndOffset.get("length") + " offset:"
// + node.getStartPosition() + " endoffset:" + node.getEndPosition());
rootNode.addChild(node);
node.setParent(rootNode);
}
if (tree.getChildCount() != 0) {
for (Object kid : tree.getChildren()) {
CommonTree leaf = (CommonTree) kid;
scriptItemTree(leaf, node, startLine, endPos, root);
}
}
}
private int getLineOffset(int line) {
if(line == 0) return 0;
if (line == 1)
return 0;
return lineOffsets[line - 2];
}
private ScriptItem getScriptNode(CommonTree tree, int startLine, int startPos, int endPos) {
ScriptItem childNode = null;
switch (tree.getType()) {
case CFScriptParser.FUNCTION_NAME:
case CFScriptParser.FUNCTION_RETURNTYPE:
case CFScriptParser.SLASH:
case CFScriptParser.EMPTYARGS:
case CFScriptParser.RIGHTCURLYBRACKET:
case CFScriptParser.DOT:
case CFScriptParser.EOF:
break;
case CFScriptParser.FUNCDECL:
case CFScriptParser.FUNCTION:
FunctionInfo funkInfo = new FunctionInfo(tree);
funkInfo.setLineNumber(startLine);
funkInfo.setStartPosition(startPos);
// funkInfo.setStartPosition(startPos + funkInfo.getStartPosition());
// funkInfo.setEndPosition(funkInfo.getEndPosition() + endPos);
funkInfo.setEndPosition(endPos);
childNode = funkInfo;
break;
case CFScriptParser.FUNCTION_PARAMETER:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTFunctionParameter");
childNode.setItemData(getChildrenText(tree, ' '));
break;
case CFScriptParser.NEW:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTNewOperator");
childNode.setItemData(getChildrenText(tree, '.'));
break;
case CFScriptParser.IDENTIFIER:
if (tree.getParent().getType() == CFScriptParser.NEW) {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTComponent");
childNode.setItemData("new " + tree.getText() + "()");
} else if (tree.getParent().getType() == CFScriptParser.FUNCTIONCALL) {
if (tree.getParent().getChild(0).getText().toLowerCase().equals("createobject")) {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTComponent");
childNode.setItemData("createobject " + tree.getText() + "()");
} else if (tree.getParent().getChild(0).getText().toLowerCase().equals("entitynew")) {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTComponent");
childNode.setItemData("entitynew " + tree.getText() + "()");
} else {
childNode = new ScriptItem(startLine, startPos, endPos, "FunctionCall");
childNode.setItemData(tree.getText());
}
} else if (tree.getParent().getType() == CFScriptParser.JAVAMETHODCALL) {
childNode = new ScriptItem(startLine, startPos, endPos, "FunctionCall");
childNode.setItemData(tree.getText());
} else {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTIdentifier");
childNode.setItemData(tree.getText());
}
break;
case CFScriptParser.IMPLICITARRAY:
case CFScriptParser.IMPLICITSTRUCT:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTUnassigned");
childNode.setItemData(tree.getText());
break;
case CFScriptParser.INTEGER_LITERAL:
case CFScriptParser.STRING_LITERAL:
if (tree.getParent().getType() == CFScriptParser.FUNCTIONCALL) {
if (tree.getParent().getChild(0).getText().toLowerCase().equals("createobject")) {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTComponent");
childNode.setItemData(tree.getText());
} else {
childNode = new ScriptItem(startLine, startPos, endPos, "Literal");
childNode.setItemData(tree.getText());
}
} else {
childNode = new ScriptItem(startLine, startPos, endPos, "Literal");
childNode.setItemData(tree.getText());
}
break;
case CFScriptParser.FUNCTIONCALL:
case CFScriptParser.JAVAMETHODCALL:
childNode = new ScriptItem(startLine, startPos, endPos, "Call");
childNode.setItemData("call");
break;
case CFScriptParser.RETURN:
childNode = new ScriptItem(startLine, startPos, endPos, "return");
childNode.setItemData(tree.getText());
break;
case CFScriptParser.VARLOCAL:
// endPos += ((CommonToken) ((CommonTree) tree.getChild(2)).getToken()).getStopIndex() - 2;
childNode = new ScriptItem(startLine, startPos, endPos, "ASTVarDeclaration");
childNode.setItemData("var " + tree.getChild(0).getText() + tree.getChild(1).getText() + tree.getChild(2).getText());
break;
case CFScriptParser.COMPDECL:
case CFScriptParser.COMPONENT:
// endPos += ((CommonToken) ((CommonTree) tree.getChild(0)).getToken()).getStopIndex() - 2;
childNode = new ScriptItem(startLine, startPos, endPos, "cfcomponent");
childNode.setItemData("component");
break;
case CFScriptParser.LEFTCURLYBRACKET:
break;
case CFScriptParser.FUNCTION_ATTRIBUTE:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTFunctionAttribute");
childNode.setItemData(tree.getChild(0).getText() + "=" + tree.getChild(1).getText());
break;
case CFScriptParser.COMPONENT_ATTRIBUTE:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTComponentAttribute");
childNode.setItemData(tree.getChild(0).getText() + "=" + tree.getChild(1).getText());
break;
case CFScriptParser.PROPERTYSTATEMENT:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTPropertyStatement");
childNode.setItemData(getChildrenText(tree, ' '));
break;
case CFScriptParser.ANDOPERATOR:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTAndOperator");
childNode.setItemData(getChildrenText(tree, ' '));
break;
case CFScriptParser.CFMLFUNCTIONSTATEMENT:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTCFMLFunctionStatement");
childNode.setItemData(getChildrenText(tree, ' '));
break;
case CFScriptParser.COLON:
if (tree.getParent().getType() == CFScriptParser.COMPONENT_ATTRIBUTE) {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTComponentAttribute");
childNode.setItemData(getChildrenText((CommonTree) tree.getParent(), ' '));
} else if (tree.getParent().getType() == CFScriptParser.CASE) {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTSwitchStatement");
childNode.setItemData(getChildrenText((CommonTree) tree.getParent(), ' '));
} else if (tree.getParent().getType() == CFScriptParser.DEFAULT) {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTSwitchStatement");
childNode.setItemData(getChildrenText((CommonTree) tree.getParent(), ' '));
} else {
childNode = new ScriptItem(startLine, startPos, endPos, "ASTAssignment");
childNode.setItemData(tree.getChild(0).getText() + ":" + tree.getChild(1).getText());
}
break;
case CFScriptParser.SWITCH:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTSwitchStatement");
childNode.setItemData(tree.getText());
break;
case CFScriptParser.EQUALSOP:
// unscoped assignment registers as EQUALSOP currently
if (tree.getChildren() != null) {
// startPos -= (tree.getChild(0).getText().length() + 1);
// endPos += ((CommonToken) ((CommonTree) tree.getChild(1)).getToken()).getStopIndex();
childNode = new ScriptItem(startLine, startPos, endPos, "ASTAssignment");
if (tree.getChild(1).getText().toLowerCase().equals("new")) {
childNode.setItemData(tree.getChild(0).getText() + '=' + getChildrenText((CommonTree) tree.getChild(1), '.'));
} else {
childNode.setItemData(tree.getChild(0).getText() + '=' + tree.getChild(1).getText());
}
}
break;
default:
childNode = new ScriptItem(startLine, startPos, endPos, "ASTUnassigned");
childNode.setItemData(tree.getText() + ":" + tree.getType());
break;
}
return childNode;
}
private String getChildrenText(CommonTree tree, char delimiter) {
StringBuffer sb = new StringBuffer();
if (tree.getChildCount() != 0) {
for (Object childOb : tree.getChildren()) {
CommonTree child = (CommonTree) childOb;
String text = ((CommonTree) child).getText();
if (text.equals("EMPTYARGS"))
text = ")";
if (text.equals("=") && child.getChildCount() > 0)
text = child.getChild(0).getText() + '=' + child.getChild(1).getText() + " ";
if (text.length() != 0) {
sb.append(text);
}
}
} else {
System.out.println("No children for " + tree.getText() + "!");
}
return sb.toString().trim();
}
/**
* <code>handleHTMLTag</code> - Handles an HTML tag (does nothing at the moment.)<br/>
* <b>NB:</b> HTML files will bugger up the parser as handleClosing doesn't handle closing HTML tags!
* @param tagName - tag name with chevrons removed
* @param match - the tag match made
* @param matchStack
* @param attrMap - map of attributes for this item
* @param isACloser - whether the tag is a closer or not (or has been closed by the user)
*/
protected void handleHTMLTag(String tagName, ParseItemMatch match, Stack matchStack, ArrayList attrList, boolean isACloser)
{
//System.err.println("CFParser::handleHTMLTag() - " + Util.GetTabs(matchStack) + "Parser: Got an HTML tag called \'" + tagName + "\'. Ignoring for the moment");
}
/**
* Returns an instance of a CfmlTagItem based upon the tag name provided.
* Essentially this is a class factory... it would go in a separate class but I'm not
* sure how best to do that. Gah, my poor Java knowledge :)
*
* @param tagName - tag name to match. Note this tag name should just be the cf-less name (e.g. else for <cfelse>)
* @param match - the TagMatch data.
* @param lineNum - line that the match occured on.
* @return An instance of the matched tag.
*/
// TODO: Make a class factory for CfmlTagItems...
protected CfmlTagItem getNameBasedCfmlTag(String tagName, ParseItemMatch match, int lineNum)
{
//
// There _so_ must be a better way of doing this, but what it is I'm not sure!
if(tagName.compareToIgnoreCase("else") == 0)
return new CfmlTagElse(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("if") == 0)
return new CfmlTagIf(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("case") == 0)
return new CfmlTagCase(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("defaultcase") == 0)
return new CfmlTagDefaultCase(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("catch") == 0)
return new CfmlTagCatch(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("property") == 0)
return new CfmlTagProperty(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("elseif") == 0)
return new CfmlTagElseIf(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("queryparam") == 0)
return new CfmlTagQueryParam(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("invokeargument") == 0)
return new CfmlTagInvokeArgument(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("function") == 0)
return new CfmlTagFunction(lineNum, match.startPos, match.endPos, tagName);
else if(tagName.compareToIgnoreCase("set") == 0)
return new CfmlTagSet(lineNum, match.startPos, match.endPos, tagName);
else
return new CfmlTagItem(lineNum, match.startPos, match.endPos, tagName);
}
/**
* <code>handleCFTag</code> - Handles a opening CF tag.
*
* @param tagName - name of the tag
* @param match - the TagMatch made
* @param matchStack - match stack
* @param attrList - map of attributes that are for this tag
* @param isACloser - whether it's a self-closer
*/
protected void handleCFTag(String tagName, ParseItemMatch match, Stack matchStack, ArrayList attrList, boolean isACloser)
throws Exception
{
//
// If a CF tag then we get it's CF tag name (i.e. <cffunction, CF tag name is 'function')
// and init the new CfmlTagItem with that. We then check to see whether this type of tag
// has a closing tag.
// If it has it means that it is a branch element on the tree, so we pop it onto the stack.
// If not then it's a child element and so we add it to the child list of the top element
// of the stack.
tagName = tagName.substring(1, tagName.length());
TagItem newItem;
//System.out.println("CFParser::handleCFTag found " + tagName);
boolean singleQuoted = false;
boolean doubleQuoted = false;
char c;
for (int i=0;i<tagName.length();i++) {
c = tagName.charAt(i);
if (c == '\'' && !doubleQuoted) {
singleQuoted = !singleQuoted;
}
if (c == '\"' && !singleQuoted) {
doubleQuoted = !doubleQuoted;
}
if (c == '<' && !singleQuoted && !doubleQuoted) {
parserState.addMessage(new ParseError(
getLineNumber(match.getStartPos()), match.getStartPos(), match.getStartPos() + match.getMatch().length(), match.getMatch(),
"Invalid token \"" + c + "\" found in opening <b>" + tagName + "</b> tag. The tag is probably missing a closing \">\""
));
throw new FatalException("Fatal parser error. Unable to continue parsing past line " + getLineNumber(match.getStartPos()));
}
}
// }
// }
//}
//
// First test to see whether we've found a custom tag. If so we do nothing fancy (yet).
// Also tests to make sure it catches CFX tags.
if(tagName.length() >= 3 && (tagName.charAt(2) == '_'
|| (
(tagName.charAt(2) == 'x' || (tagName.charAt(2) == 'X'))
&& tagName.charAt(3) == '_')
))
{
newItem = new CfmlCustomTag(getLineNumber(match.startPos), match.startPos, match.endPos, tagName);
newItem.setItemData(match.match);
}
else
{
newItem = getNameBasedCfmlTag(tagName, match, getLineNumber(match.startPos));
newItem.initDictionary(DictionaryManager.getDictionary(DictionaryManager.CFDIC));
newItem.setItemData(match.match);
}
newItem.addAttributes(attrList);
addTagItemToTree(match, matchStack, isACloser, newItem);
}
private void addDocItemToTree(DocItem item) {
DocItem topItem = (DocItem) matchStack.pop();
topItem.addChild(item);
matchStack.push(topItem);
}
private void addDocItemToTree(ParseItemMatch match, DocItem newItem)
{
if(newItem instanceof TagItem) {
addTagItemToTree(match, matchStack, false, (TagItem)newItem);
System.err.println("CFParser::addDocItemToTree() - A tag item has been passed. This is wrong but I\'ve passed it to addTagItemToTree as a non-closer");
return;
}
addDocItemToTree(newItem);
}
/**
* @param match
* @param matchStack
* @param isACloser
* @param newItem
*/
private void addTagItemToTree(ParseItemMatch match, Stack matchStack, boolean isACloser, TagItem newItem) {
//
// Either the syntax dictionary says it closes itself or the user has specified it will
try {
if(newItem.hasClosingTag() && !isACloser)
{ // Not a closing item, it's an opener so on the stack it goes.
matchStack.push(newItem);
}
else
{ // It's a closing item, so we get the parent item and add this item to it's children.
DocItem top = (DocItem)matchStack.pop();
top.addChild(newItem);
matchStack.push(top);
}
} catch(Exception anExcep) {
parserState.addMessage(new ParseError(getLineNumber(match.startPos), match.startPos, match.endPos, match.match, "An unknown error occurred during parsing."));
System.err.println("CFParser::handleCFTag() - Caught an exception during item popping. Exception was " + anExcep.getLocalizedMessage());
anExcep.printStackTrace();
////System.out..println(anExcep.hashCode());
throw (RuntimeException)anExcep.fillInStackTrace();
}
}
/**
*
* @param tagName
* @param matches
* @param matchPos
* @param isACloser
* @return
*/
boolean isTagACloser(String tagName, ArrayList matches, int matchPos, boolean isACloser)
{
if(tagName.compareToIgnoreCase("<cfinvoke") == 0)
{
if(((ParseItemMatch)matches.get(matchPos+1)).match.indexOf("invokeargument") == -1)
{
isACloser = true;
}
else
{
isACloser = false;
}
}
return isACloser;
}
/**
* <code>createDocTree</code> - Creates the document tree from the TagMatches made
*
* @param inData
*
* @param matches
* - the matches found previously
* @return a document tree.
*
*/
/*
* The document tree is created using two things:
* 1) A match stack.
* 2) A document tree
*
* The method loops through all of the matches. For every opening tag there must be a closing tag.
* Therefore for every opening tag we take it and push it onto the stack. For every closing tag
* we pop the most recent tag off, compare it with the closer. If there's a match it's the closing
* tag for the opener. We add them to their parent's entry and carry on.
* If they don't match then at present the closer is thrown away.
*
* Eventually we reach the end of the matches and that should mean - in theory - that we've got a
* valid tree.
*
* Unfortunately this can easily not be the case due to any user errors. At the moment it just
* carries on regardless of any major errors. Perhaps some higher-level routine should take care
* of whether the document tree is valid or not. Perhaps the tree display?
*
* TODO: break open CFSET's and grab variable assignments.
* TODO: somehow implement tag variable grabbing (i.e. from <cfquery>'s 'name' attribute) Should the tag object do it, or the parser?
*
*/
public CFDocument createDocTree(String inData)
{
ArrayList matches = parserState.getMatches();
// System.out.println("=============> Beginning match dump" );
// Util.dumpMatches(matches);
// System.out.println("=============> Finishing match dump");
CFDocument newDoc = new CFDocument();
matchStack = new Stack();
ArrayList rootElements = new ArrayList();
TagItem rootItem = new TagItem(0, 0, 0, "Doc Root");
int matchPos = 0;
StringBuffer nonParsedTextBuffer = new StringBuffer();
matchStack.push(rootItem);
ParseItemMatch lastMatch = null;
// little hackish way to detect cfscript based cfcs TODO: something better
if (inData.split("(?i).*component[^>]+\\{").length > 1) {
parseCFScript(inData);
newDoc.setDocumentRoot(rootItem);
return newDoc;
}
try {
for(; matchPos < matches.size(); matchPos++)
{
ParseItemMatch match = (ParseItemMatch)matches.get(matchPos);
String matchStr = match.match;
/*
if(lastMatch != null)
{
int difference = match.getStartPos() - lastMatch.getEndPos();
System.out.println(lastMatch.getMatch() + " -> " + match.getMatch() + ": diff of : "+ difference);
String nonParsedText = this.data2Parse.substring(lastMatch.getEndPos()+1, difference + lastMatch.getEndPos());
System.out.println("-> Thinks this is non matched: \'" + nonParsedText + "\'");
TextNode textNode = new TextNode(match.getLineNumber(), match.getStartPos(), match.getEndPos(), "#TEXT");
textNode.setNodeText(nonParsedText);
addDocItemToTree(match, matchStack, textNode);
}
*/
lastMatch = match;
if(matchStr.charAt(0) == '<') // Funnily enough this should always be the case!
{
if(matchStr.charAt(1) == '/') {
if(!handleClosingTag(match, matchStack)) {
parserState.addMessage(new ParseError(
getLineNumber(matchPos), matchPos, matchPos, "",
"Something in here is (probably) missing a closing tag or a closing \">\" and thus totally borking the parse!"
));
break;
}
} else {
// get just tag name, e.g. : <cffunction name="blah" becomes cffunction
String tagName = matchStr.split("[\\s/>]")[0];
tagName = "<"+tagName.substring(1, tagName.length()).toLowerCase();
int tagEnd = matchStr.indexOf(tagName)+tagName.length();
boolean isACloser = false;
//
// Find the end of the tag
int currPos = 0;
for(int quoteCount = 0; currPos < match.match.length(); currPos++) {
char currChar = match.match.charAt(currPos);
boolean inQuotes = (1 == quoteCount % 2);
if(!inQuotes && currChar == '>') {
break;
}
else if(currChar == '\"')
quoteCount++;
}
//
// Handle a self-closer (i.e. <cfproperty ... />
String attributes = "";
if(match.match.charAt(currPos-1) == '/') {
isACloser = true;
if(match.match.length() - tagEnd >= 2)
attributes = match.match.substring(tagEnd, match.match.length()-2); // minus one to strip the closing '/>'
}
else
attributes = match.match.substring(tagEnd, match.match.length()-1); // minus one to strip the closing '>'
switch(match.getMatchType())
{
case MATCHER_CFMLTAG:
if(tagName.startsWith("<cfif") || tagName.startsWith("<cfelseif") || tagName.startsWith("<cfmodule")
|| tagName.startsWith("<cfset"))
{
handleCFTag(tagName, match, matchStack, new ArrayList(), isACloser);
} else {
handleCFTag(tagName, match, matchStack, stripAttributes(attributes, match.lineNumber, tagEnd, match), isACloser);
}
if((tagName.startsWith("<cfif") || tagName.startsWith("<cfelseif") || tagName.startsWith("<cfmodule")
|| tagName.startsWith("<cfset"))
&& attributes.trim().length() == 0)
{
userMessage(0,
"stripAttributes", tagName + "> requires at least one attribute",
USRMSG_ERROR, match);
}
break;
case MATCHER_CFMLCOMMENT:
//System.out.println("CFParser::createDocTree() - Got a CFML comment!");
DocItem newComment = new CfmlComment(
match.getLineNumber(),
match.getStartPos(),
match.getEndPos(),
match.getMatch()
);
newComment.setItemData(match.getMatch());
addDocItemToTree(match, newComment);
break;
case MATCHER_CFSCRIPT:
tagName = tagName.substring(1, tagName.length());
TagItem newItem;
newItem = getNameBasedCfmlTag(tagName.substring(0, "cfscript".length()), match, getLineNumber(match.startPos));
newItem.initDictionary(DictionaryManager.getDictionary(DictionaryManager.CFDIC));
newItem.setItemData("");
addTagItemToTree(match, matchStack, isACloser, newItem);
handleCFScriptBlock(match, matchStack);
break;
default:
break;
}
}
}
}
//newDoc.docTree = matchStack;
}
catch (FatalException e) {
parserState.addMessage(new ParseError(
getLineNumber(matchPos), matchPos, matchPos, "",
e.getMessage()
));
}
catch(Exception anyException) {
parserState.addMessage(new ParseError(
getLineNumber(matchPos), matchPos, matchPos, "",
"Doc tree creation: caught an unhandled exception: "
+ anyException.getMessage()
));
//System.err.println(
// Util.GetTabs(matchStack) + "Parser: Caught an exception!"
// + anyException.getMessage()
//);
anyException.printStackTrace();
////System.out..println(anyException.hashCode());
}
handleUnclosedTags(matchStack,rootItem);
newDoc.setDocumentRoot(rootItem);
return newDoc;
}
private void handleUnclosedTags(Stack matchStack, DocItem defaultParent) {
try {
// If we've got more than one item left on the stack check if any of the remaining items are unclosed custom tags
if (matchStack.size() > 1) {
for (int i=0;i<matchStack.size();i++) {
TagItem t = (TagItem)matchStack.peek();
String tagName = t.getName();
//System.out.println("Looking at " + tagName);
// Look for tags that got left over.
if (tagName.toLowerCase().startsWith("cf") ) {
// Mark them as being self closers.
if (tagName.toLowerCase().startsWith("cf_") || tagName.toLowerCase().startsWith("cfx_")) {
((CfmlCustomTag)t).hasCloser= false;
}
// Get the current parent of the tag.
DocItem parent = t.getParent();
if (parent == null) {
parent = defaultParent;
}
// Don't make the thing a child of itself.
if (!parent.equals(t)) {
parent.addChild(t);
CFNodeList childNodes = t.getChildNodes();
Iterator iter = childNodes.iterator();
ArrayList deletedChildren = new ArrayList();
while (iter.hasNext()) {
Object o = iter.next();
if (o instanceof DocItem) {
DocItem d = (DocItem)o;
d.setParent(parent);
parent.addChild(d);
deletedChildren.add(d);
System.out.println("Added " + d.getClass().getName() + " as child of " + parent.getName() + ". Was child of " + tagName);
}
}
iter = deletedChildren.iterator();
while(iter.hasNext()) {
t.removeChild((DocItem)iter.next());
}
}
}
}
}
// any tags left will be unclosed tags that should be closed
// or maybe a hybrid (cfmodule poped up without the check)
while(matchStack.size() > 1) //leave the doc root
{
TagItem orphanTag = (TagItem)matchStack.pop();
if (!orphanTag.isHybrid()) {
parserState.addMessage(new ParseError(
orphanTag.getLineNumber(), orphanTag.getStartPosition(), orphanTag.getEndPosition(), orphanTag.getName(),
orphanTag.getName() + " is missing a closing tag"
));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public final int MATCHER_NOTHING = 0x00;
public final int MATCHER_COMMENT = 0x01;
public final int MATCHER_HTMLTAG = 0x02;
public final int MATCHER_ATTRIBUTE = 0x04;
public final int MATCHER_CFSCRIPT = 0x08;
public final int MATCHER_CFMLCOMMENT = 0x16;
public final int MATCHER_CFSCRCOMMENT = 0x32;
public final int MATCHER_STRING = 0x64;
public final int MATCHER_CFMLTAG = 0x128;
protected final int INDEX_NOTFOUND = -1; // For String::indexOf(), make it nicer to read!
protected int matchingHTML(State parseState, String inData, int currDocOffset)
{
int finalOffset = currDocOffset;
int currPos = currDocOffset + 1;
ParseItemMatch embeddedMatch = null;
int cfTagCount = 0;
int quoteCount = 0;
for(; currPos < inData.length(); currPos++)
{
char currChar = inData.charAt(currPos);
boolean inQuotes = (1 == quoteCount % 2);
String next2Chars = "";
String next3Chars = "";
if(inData.length() - currPos > 2) // For CF stuff we get the next two chars as well.
next2Chars = inData.substring(currPos + 1, currPos + 3);
if(inData.length() - currPos > 3) // For CF closer tags </cf...
next3Chars = inData.substring(currPos + 1, currPos + 4);
if(currChar == '<' && (next2Chars.compareToIgnoreCase("cf") == 0 || next3Chars.compareToIgnoreCase("/cf") == 0))
{ // CFML tag embedded in HTML
////System.out..println("CFParser::matchingHTML() - FOUND!: an embedded CFML tag within HTML! : " + inData.substring(0, currPos));
currPos = matchingCFML(parseState, inData, currPos);
cfTagCount++;
}
else if(!inQuotes && currChar == '>')
{
////System.out..println("CFParser::matchingHTML() - FOUND!: an HTML tag!: " + inData.substring(currDocOffset, currPos+1));
parseState.addMatch(new ParseItemMatch(inData.substring(currDocOffset, currPos+1), currDocOffset, currPos, 0, MATCHER_HTMLTAG),
State.ADD_BEFORE, cfTagCount);
finalOffset = currPos;
break;
}
else if(currChar == '\"')
quoteCount++;
}
if(finalOffset != currPos)
{
//System.err.println("CFParser::matchingHTML() - FATAL ERROR: Failed to find the end of an HTML tag!: " + inData.substring(currDocOffset, currPos));
parseState.addMessage(new ParseError(getLineNumber(currDocOffset), currDocOffset, currPos,
inData.substring(currDocOffset, currPos),
"Reached end of document before finding end of HTML tag.",
true )); // Fatal error
}
return currPos;
}
protected int matchingCFScript(State parseState, String inData, int currDocOffset)
{
int finalOffset = currDocOffset;
int currPos = currDocOffset;
String nextChars = ""; // </cfscript>
String closingText = "</cfscript>";
//
//System.out.println("CFParser::matchingCFScript() - Matching CFScript");
for(; currPos < inData.length(); currPos++)
{
if(inData.length() - currPos + 1 > closingText.length())
nextChars = inData.substring(currPos, currPos + closingText.length());
else
break; // Not enough space left for it to be a closing cfscript tag.
if(nextChars.compareToIgnoreCase(closingText) == 0)
{
finalOffset = currPos;
break;
}
}
//System.out.println("matchingCFScript() -");
//System.out.println(inData.substring(currDocOffset, finalOffset));
if(finalOffset != currPos)
{
System.err.println("FATAL ERROR: Searching for a closing <cfscript> tag but could not find one: " + inData.substring(currDocOffset, currPos));
parseState.addMessage(new ParseError(getLineNumber(currDocOffset), currDocOffset, currPos,
inData.substring(currDocOffset, currPos),
"Reached end of document before finding a closing cfscript tag.",
true )); // Fatal error
//} else if(this.parseCFScript) {
} else if(true) {
int scriptStart = currDocOffset + "<cfscript>".length();
String cfScriptData = inData.substring(currDocOffset, finalOffset);
cfScriptData = cfScriptData.trim();
//System.out.println("CFScript data:");
//System.out.println(cfScriptData);
//
// We cheat now. We're actually creating a tag match for a <cfscript> block and pass all
// of the data in so we have a tag called "<cfscript>...". But this breaks if it's empty,
// so we trimmed the data and now we compare with <cfscript>. If it doesn't equal it (
// and therefore it's got CFScript data in) we add the match.
//if(cfScriptData.toLowerCase().startsWith("<cfscript>")) {
ParseItemMatch scriptMatch = new ParseItemMatch(cfScriptData, scriptStart, finalOffset, getLineNumber(scriptStart), MATCHER_CFSCRIPT);
parseState.addMatch(scriptMatch);
/*
TagMatch endScriptTag = new TagMatch("</cfscript>", finalOffset,
finalOffset + "</cfscript>".length(), getLineNumber(finalOffset + 3));
parseState.addMatch(endScriptTag);
*/
//}
}
//
// finalOffset is assigned the end of the cfscript block _including_ the closing '</cfscript>'
// so we remove it from the offset so that the parser can handle the closing tag correctly.
return finalOffset-1;// - "</cfscript>".length();
}
/*
protected int matchingCFScript(State parseState, String inData, int currDocOffset)
{
int finalOffset = currDocOffset;
int currPos = currDocOffset;
String nextChars = ""; // </cfscript>
String closingText = "</cfscript>";
////System.out..println("CFParser::matchingCFScript() - Matching CFScript");
for(; currPos < inData.length(); currPos++)
{
if(inData.length() - currPos + 1 > closingText.length())
nextChars = inData.substring(currPos, currPos + closingText.length());
else
break; // Not enough space left for it to be a closing cfscript tag.
if(nextChars.compareToIgnoreCase(closingText) == 0)
{
finalOffset = currPos;
break;
}
}
int scriptStart = currDocOffset + "<cfscript>".length();
String cfScriptData = inData.substring(scriptStart, finalOffset);
TagMatch scriptMatch = new TagMatch(cfScriptData, scriptStart, finalOffset, getLineNumber(scriptStart));
parseState.addMatch(scriptMatch);
if(finalOffset != currPos)
{
//System.err.println("FATAL ERROR: Searching for a closing <cfscript> tag but could not find one: " + inData.substring(currDocOffset, currPos));
parseState.addMessage(new ParseError(getLineNumber(currDocOffset), currDocOffset, currPos,
inData.substring(currDocOffset, currPos),
"Reached end of document before finding a closing cfscript tag.",
true )); // Fatal error
}
return finalOffset;
}
*/
protected int matchingCFML(State parseState, String inData, int currDocOffset)
{
int finalOffset = currDocOffset;
int currPos = currDocOffset;
// for recognizing quote escape sequences.
// If it's even we're out of quotes, odd we're in 'em. Try it out manually and see!
char currQuote = 0;
int quoteCount = 0;
boolean inComment = false;
for(; currPos < inData.length(); currPos++) {
char currChar = inData.charAt(currPos);
if(currChar == '<' && inData.length() >= 5 && inData.substring(currPos,currPos+5).equals("<!---")) {
inComment = true;
}
if(currChar == '>' && inData.length()-3 >= 0 && inData.substring(currPos-3, currPos).equals("---")) {
inComment = false;
continue;
}
if(inComment) {
// if we're inside a comment, we don't give a hoot, keep going until out of it
continue;
}
boolean inQuotes = (1 == quoteCount % 2);
if(!inQuotes && currChar == '>') {
finalOffset = currPos;
//System.out.println("Parser found a cfml tag: " + inData.substring(currDocOffset, currPos+1));
parseState.addMatch(new ParseItemMatch(inData.substring(currDocOffset, currPos+1), currDocOffset, currPos,
getLineNumber(currDocOffset), MATCHER_CFMLTAG));
break;
} else if( inQuotes && currQuote == currChar ) {
// We are in quotes and we found the character that started the quotes
quoteCount++;
} else if( !inQuotes && (currChar == '\"' || currChar == '\'') ) {
// Store what started the quote so we know what to look for to end the current quote
currQuote = currChar;
quoteCount++;
}
}
if(finalOffset != currPos && reportErrors) {
//System.err.println("FATAL ERROR: Failed to find the end of a CFML tag!: " + inData.substring(currDocOffset, currPos));
parseState.addMessage(new ParseError(getLineNumber(currDocOffset), currDocOffset, currPos,
inData.substring(currDocOffset, currPos),
"Reached end of document before finding end of CFML tag.",
true )); // Fatal error
}
return finalOffset;
}
/**
* This function goes through the incoming stream of characters that represents the document to parse.
* It processes the document scanning for patterns within the data. Once a pattern has been found it
* is added to a list of matches. This match list will be used later on by the document tree creator.
*
* @param inData
* @return
*/
protected ArrayList tagMatchingAttempts(String inData)
{
//String data = inData;
String data = this.data2Parse;
int lastMatch = 0;
int currPos = 0;
int currState = 0;
Stack stateStack = new Stack();
Stack statePositionStack = new Stack();
ArrayList matches = new ArrayList();
try {
for(currPos = 0; currPos < data.length(); currPos++)
{
char currChar = data.charAt(currPos);
String next2Chars = "";
String next3Chars = "";
String next4Chars = "";
String around = "";
// Make sure we haven't had any fatal errors during parsing.
if(parserState.hadFatal())
break;
//
// Get some next data that will make our life easier in the code ahead
next2Chars = (data.length() - currPos > 2) ? data.substring(currPos + 1, currPos + 3) : "";
next3Chars = (data.length() - currPos > 3) ? next2Chars + data.charAt(currPos + 3) : "";
next4Chars = (data.length() - currPos > 4) ? next3Chars + data.charAt(currPos + 4) : "";
around = getSurroundingData(data, currPos);
if((currState == MATCHER_NOTHING || currState == MATCHER_CFMLCOMMENT && next4Chars.compareTo("!---") == 0) && currChar == '<')
{
if(next2Chars.compareTo("!-") == 0)
{ // Testing for comment: <!--
// TODO: Find out whether comments can occur in tags
if(next4Chars.compareTo("!---") == 0)
{
stateStack.push(new Integer(currState));
statePositionStack.push(new Integer(currPos));
currState = MATCHER_CFMLCOMMENT;
lastMatch = currPos;
}
else
{
currState = MATCHER_COMMENT;
lastMatch = currPos;
}
}
else if(next2Chars.compareToIgnoreCase("cf") == 0)
{
//TODO: saving a <cfscript> tag with no contents causes a heap error
//
// The following handles a CFScript tag. A CFScript tag is NOT part of the document tree as it is a
// container *only* for things to go in the document tree.
if(data.length() - currPos > "<cfscript>".length() &&
data.substring(currPos, currPos + "<cfscript>".length()).compareToIgnoreCase("<cfscript>") == 0)
{
currPos = matchingCFScript(parserState, inData, currPos);
}
else
currPos = matchingCFML(parserState, inData, currPos);
}
else // Notice that the above if doesn't match </cf, that's because it's like a standard HTML tag.
{
// if this test indicates an HTML tag then we need to parse it.
// this specifically works around the issue where an < operator
// in SQL statements was causing false error reporting. Paul V.
// Ticket #146
// BEWARE! If this breaks, code folding breaks!!!
if(next2Chars.matches("^(/?)[a-zA-Z]{0,2}(>?)$"))
currPos = matchingHTML(parserState, inData, currPos);
}
}
else if(currState == MATCHER_CFMLCOMMENT
&& currChar == '-'
&& next2Chars.compareTo("--") == 0
&& inData.charAt(currPos+3) == '>')
{
currState = ((Integer)stateStack.pop()).intValue();
int lastStatePos = ((Integer)statePositionStack.pop()).intValue();
if(currState == MATCHER_NOTHING)
{
ParseItemMatch commentMatch = new ParseItemMatch(
inData.substring(lastStatePos, currPos + 4), lastStatePos, currPos + 4,
getLineNumber(lastStatePos), MATCHER_CFMLCOMMENT
);
parserState.addMatch(commentMatch);
}
}
else if(currState == MATCHER_COMMENT
&& currChar == '-'
&& next2Chars.compareTo("->") == 0)
{
currState = MATCHER_NOTHING;
}
}
}catch(Exception excep) {
parserState.addMessage(new ParseError(0, currPos, currPos, "", "Caught an exception during parsing.", true));
}
return matches;
}
/**
* Gets some surrounding data that is around the current cursor position.
*
* @param data The data currently being scanned
* @param currPos The current position in the document
* @return The +/- 10 characters around the current position in the document
*/
private String getSurroundingData(String data, int currPos) {
String around = "";
if(data.length() - currPos > 10 && currPos > 10)
around = data.substring(currPos - 10, currPos) + data.substring(currPos, currPos + 10);
else if(data.length() - currPos > 10)
around = data.substring(currPos, currPos + 10);
return around;
}
protected void processParseResultMessages()
{
ArrayList messages = parserState.getMessages();
IWorkspaceRoot myWorkspaceRoot = CFMLPlugin.getWorkspace().getRoot();
for(int i = 0; i < messages.size(); i++)
{
ParseMessage currMsg = (ParseMessage)messages.get(i);
Map attrs = new HashMap();
MarkerUtilities.setLineNumber(attrs, currMsg.getLineNumber() + 1);
MarkerUtilities.setMessage(attrs, currMsg.getMessage());
// attrs.put(IMarker.CHAR_START, new Integer(currMsg.docStartOffset));
int endOffset = 0;
if (currMsg.docEndOffset > currMsg.docStartOffset) {
endOffset = currMsg.docEndOffset;
// System.out.println("End offset is: " + endOffset + " start is " + currMsg.docStartOffset);
} else {
endOffset = currMsg.docStartOffset + currMsg.docData.length();
}
//
// Not sure what the start & end positions are good for!
// MarkerUtilities.setCharStart(attrs, currMsg.getDocStartOffset());
// MarkerUtilities.setCharEnd(attrs, currMsg.getDocEndOffset());
//
// Not sure right now how to set the problem to be a warning or an error.
// There is IMarker.SEVERITY_ERROR & IMarker.SEVERITY_WARNING but I'm
// not sure how I set them.
String type = "org.cfeclipse.cfml.parserProblemMarker";
if (currMsg instanceof ParseError) {
type = "org.cfeclipse.cfml.parserProblemMarker";
} else if (currMsg instanceof ParseWarning) {
type = "org.cfeclipse.cfml.parserWarningMarker";
}
try {
MarkerUtilities.createMarker(this.res, attrs, type);
// MarkerUtilities.createMarker(this.res, attrs, IMarker.PROBLEM);
} catch (CoreException excep) {
userMessage(0, "userMessage",
"ERROR: Caught CoreException when creating a problem marker. Message: \'" + excep.getMessage() + "\'");
} catch (Exception anyExcep) {
userMessage(0, "processParseResultMessage", "ERROR: Caught exception " + anyExcep.getMessage());
}
}
}
/**
* Traverses the document tree for the final time, calling each document item's
* sanity checker and then retrieving any parse messages that each document item
* may hold.
* @param startNode The node to start at.
* @return an <code>ArrayList</code> of the messages retrieved.
*/
public ArrayList finalDocTreeTraversal(DocItem startNode)
{
ArrayList messages = new ArrayList();
//
// Perform sanity check. Method adds to the object's message list which we shall gather next.
startNode.IsSane();
if(startNode.getParseState() != null) {
messages.addAll(startNode.getParseState().getMessages());
}
if(startNode.hasChildren())
{
CFNodeList children = startNode.getChildNodes();
Iterator nodeIter = children.iterator();
//System.out.println("Node ("+startNode.getClass().getSimpleName()+ ") " + startNode.toString() + " has " + children.size() + " children");
while(nodeIter.hasNext())
{
DocItem item = (DocItem)nodeIter.next();
//item.setParent(startNode);
messages.addAll(finalDocTreeTraversal(item));
}
}
return messages;
}
public CFDocument parseDoc(String inData)
{
CFDocument docTree = null;
try {
parserState = new State("doesn\'t matter!");
lineOffsets = Util.calcLineNumbers(inData);
this.setData2Parse(inData);
// Code folding is performed by tagMatchingAttempts
// this code will cause a warning as codeFoldingMatches is never referenced again...
ArrayList codeFoldingMatches = tagMatchingAttempts(inData);
//parserState.getMatches() was called twice in succession, this should speed it up a bit.
docTree = createDocTree(inData);
DocItem documentRoot = docTree.getDocumentRoot();
ArrayList list = finalDocTreeTraversal(documentRoot);
parserState.addMessages(list);
processParseResultMessages();
//This should parse a document and setup all the variables
//TODO: Make sure the variable parser only parses up to the cursor
//need to get preferences
IPreferenceStore prefStore = CFMLPlugin.getDefault().getPreferenceStore();
if(prefStore.getBoolean(ParserPreferenceConstants.P_PARSE_VARIABLES)){
//System.out.println("calling the variables parser!");
VariablesParser vParser = new VariablesParser(docTree,inData);
docTree.setVariableMap(vParser.getVariableMap());
}
} catch(Exception excep)
{
System.err.println("CFParser::parseDoc() - Exception: " + excep.getMessage());
excep.printStackTrace();
}
return docTree;
}
public CFDocument parseDoc()
{
if(parseDoc == null)
{
return parseDoc(data2Parse);
}
else
return parseDoc(parseDoc.get());
}
public CFDocument parseDoc(IDocument doc2Parse)
{
return parseDoc(doc2Parse.get());
}
/**
* Parses the document and saves the result into the parseResult variable
* so it maintains it's tree.
* @author rob
* @deprecated
*/
/* public void parseSaveDoc()
{
//////System.out..println(parseDoc.get());
parseResult = parseDoc();
} */
}