package ognl.helperfunction;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.StringTokenizer;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation._NSStringUtilities;
public class WOHelperFunctionHTMLParser {
public static Logger log = Logger.getLogger(WOHelperFunctionHTMLParser.class);
private WOHelperFunctionParser _parserDelegate;
private String _unparsedTemplate;
private StringBuffer _contentText;
private static final int STATE_OUTSIDE = 0;
private static final int STATE_INSIDE_COMMENT = 3;
private static final String JS_START_TAG = "<script";
private static final String JS_END_TAG = "</script";
private static final String WO_END_TAG = "</wo";
private static final String WO_START_TAG = "<wo ";
private static final String WEBOBJECT_END_TAG = "</webobject";
private static final String WEBOBJECT_START_TAG = "<webobject";
private static final String WO_COLON_END_TAG = "</wo:";
private static final String WO_COLON_START_TAG = "<wo:";
private static final String WO_REPLACEMENT_MARKER = "__REPL__";
private static final String XML_CDATA_START_TAG = "<![CDATA[";
private static boolean _parseStandardTags = false;
private NSMutableDictionary _stackDict;
static {
WOHelperFunctionHTMLParser.log.setLevel(Level.WARN);
}
public WOHelperFunctionHTMLParser(WOHelperFunctionParser parserDelegate, String unparsedTemplate) {
_parserDelegate = parserDelegate;
_unparsedTemplate = unparsedTemplate;
_contentText = new StringBuffer(128);
}
public static void setParseStandardTags(boolean flag) {
_parseStandardTags = flag;
}
public void parseHTML() throws WOHelperFunctionHTMLFormatException, WOHelperFunctionDeclarationFormatException, ClassNotFoundException {
_stackDict = new NSMutableDictionary();
StringTokenizer templateTokenizer = new StringTokenizer(_unparsedTemplate, "<");
boolean flag = true;
int parserState = STATE_OUTSIDE;
String token;
if (_unparsedTemplate.startsWith("<") || !templateTokenizer.hasMoreTokens()) {
token = null;
}
else {
token = templateTokenizer.nextToken("<");
}
try {
do {
if (!templateTokenizer.hasMoreTokens()) {
break;
}
switch (parserState) {
case STATE_OUTSIDE:
if (token != null) {
if (token.startsWith(">")) {
token = token.substring(1);
}
_contentText.append(token);
}
token = templateTokenizer.nextToken(">");
int tagIndex;
// parses non wo: tags for dynamic bindings
if (_parseStandardTags) {
token = checkToken(token);
}
String tagLowerCase = token.toLowerCase();
if ( tagLowerCase.startsWith(WEBOBJECT_START_TAG) || tagLowerCase.startsWith(WO_COLON_START_TAG) || tagLowerCase.startsWith(WO_START_TAG) ) {
if (token.endsWith("/")) {
startOfWebObjectTag(token.substring(0, token.length() - 1));
endOfWebObjectTag("/");
}
else {
startOfWebObjectTag(token);
}
}
else if ((tagIndex = tagLowerCase.indexOf(WEBOBJECT_START_TAG)) > 1 || (tagIndex = tagLowerCase.indexOf(WO_COLON_START_TAG)) > 1 || (tagIndex = tagLowerCase.indexOf(WO_START_TAG)) > 1) {
_contentText.append(token.substring(0, token.lastIndexOf("<")));
if (token.endsWith("/")) {
startOfWebObjectTag(token.substring(tagIndex, token.length() - 1));
endOfWebObjectTag("/");
}
else {
startOfWebObjectTag(token.substring(tagIndex, token.length()));
}
}
else if (tagLowerCase.startsWith(WEBOBJECT_END_TAG) || tagLowerCase.startsWith(WO_COLON_END_TAG) || tagLowerCase.equals(WO_END_TAG)) {
endOfWebObjectTag(token);
}
else if (tagLowerCase.startsWith(WOHelperFunctionHTMLParser.JS_START_TAG)) {
didParseText();
_contentText.append(token);
_contentText.append('>');
flag = false;
}
else if (tagLowerCase.startsWith(WOHelperFunctionHTMLParser.JS_END_TAG)) {
didParseText();
_contentText.append(token);
_contentText.append('>');
flag = true;
}
else if (token.startsWith("<!--") && flag) {
didParseText();
_contentText.append(token);
if (token.endsWith("--")) {
_contentText.append('>');
didParseComment();
}
else {
_contentText.append('>');
parserState = STATE_INSIDE_COMMENT;
}
}
else {
_contentText.append(token);
_contentText.append('>');
}
break;
case STATE_INSIDE_COMMENT:
token = templateTokenizer.nextToken(">");
_contentText.append(token);
_contentText.append('>');
if (token.endsWith("--")) {
didParseComment();
parserState = STATE_OUTSIDE;
}
break;
default:
break;
}
token = null;
if (parserState == STATE_OUTSIDE) {
token = templateTokenizer.nextToken("<");
}
}
while (true);
}
catch (NoSuchElementException e) {
log.error(e);
didParseText();
return;
}
if (token != null) {
if (token.startsWith(">")) {
token = token.substring(1);
}
_contentText.append(token);
}
didParseText();
_stackDict = null;
}
/**
* Checks the current token for dynamic inline bindings
*
* @param token
* @return a rewritten token if it has an inline binding or a closing tag, if it belongs to a rewritten token
*/
private String checkToken(String token) {
if (token == null) {
return token;
}
String tokenLowerCase = token.toLowerCase();
if (tokenLowerCase.startsWith(WEBOBJECT_START_TAG) || tokenLowerCase.startsWith(WO_COLON_START_TAG) || tokenLowerCase.startsWith(WO_START_TAG)
|| tokenLowerCase.startsWith(XML_CDATA_START_TAG)) {
// we return immediately, if it is a webobject token or CDATA tag
return token;
}
String original = new String(token);
try {
String[] tokenParts = token.split(" ");
String tokenPart = tokenParts[0].substring(1);
if ((token.indexOf("\"$") != -1 || token.indexOf("\"~") != -1) && token.startsWith("<")) {
// we assume a dynamic tag
token = token.replaceAll(tokenParts[0], "<wo:" + WO_REPLACEMENT_MARKER + tokenPart);
if (log.isDebugEnabled())
log.debug("Rewritten <" + tokenPart + " ...> tag to <wo:" + tokenPart + " ...>");
if (!token.endsWith("/")) {
// no need to keep information for self closing tags
Stack stack = (Stack) _stackDict.objectForKey(tokenPart);
if (stack == null) {
// create one and push a marker
stack = new Stack();
stack.push(WO_REPLACEMENT_MARKER);
_stackDict.setObjectForKey(stack, tokenPart);
}
else {
// just push a marker
stack.push(WO_REPLACEMENT_MARKER);
_stackDict.setObjectForKey(stack, tokenPart);
}
}
}
else if (!token.startsWith("</") && _stackDict.containsKey(tokenPart)) {
// standard opening tag
Stack stack = (Stack) _stackDict.objectForKey(tokenPart);
if (stack != null) {
stack.push(tokenPart);
_stackDict.setObjectForKey(stack, tokenPart);
}
}
else if (token.startsWith("</")) {
// closing tag
Stack stack = (Stack) _stackDict.objectForKey(tokenParts[0].substring(2));
if (stack != null && !stack.empty()) {
String stackContent = (String) stack.pop();
if (stackContent.equals(WO_REPLACEMENT_MARKER)) {
if (log.isDebugEnabled())
log.debug("Replaced end tag for '" + tokenParts[0].substring(2) + "' with 'wo' endtag");
token = "</wo";
}
}
}
}
catch (Exception e) {
// we print the exception and return the token unchanged
e.printStackTrace();
return original;
}
return token;
}
private void startOfWebObjectTag(String token) throws WOHelperFunctionHTMLFormatException {
didParseText();
_contentText.append(token);
didParseOpeningWebObjectTag();
}
private void endOfWebObjectTag(String token) throws WOHelperFunctionDeclarationFormatException, WOHelperFunctionHTMLFormatException, ClassNotFoundException {
didParseText();
_contentText.append(token);
didParseClosingWebObjectTag();
}
private void didParseText() {
if (_contentText != null) {
if (log.isDebugEnabled()) {
log.debug("Parsed Text (" + _contentText.length() + ") : " + _contentText);
}
if (_contentText.length() > 0) {
_parserDelegate.didParseText(_NSStringUtilities.stringFromBuffer(_contentText), this);
_contentText.setLength(0);
}
}
}
private void didParseOpeningWebObjectTag() throws WOHelperFunctionHTMLFormatException {
if (_contentText != null) {
if (log.isDebugEnabled()) {
log.debug("Parsed Opening WebObject (" + _contentText.length() + ") : " + _contentText);
}
if (_contentText.length() > 0) {
_parserDelegate.didParseOpeningWebObjectTag(_NSStringUtilities.stringFromBuffer(_contentText), this);
_contentText.setLength(0);
}
}
}
private void didParseClosingWebObjectTag() throws WOHelperFunctionDeclarationFormatException, WOHelperFunctionHTMLFormatException, ClassNotFoundException, ClassNotFoundException {
if (_contentText != null) {
if (log.isDebugEnabled()) {
log.debug("Parsed Closing WebObject (" + _contentText.length() + ") : " + _contentText);
}
if (_contentText.length() > 0) {
_parserDelegate.didParseClosingWebObjectTag(_NSStringUtilities.stringFromBuffer(_contentText), this);
_contentText.setLength(0);
}
}
}
private void didParseComment() {
if (_contentText != null) {
if (log.isDebugEnabled()) {
log.debug("Parsed Comment (" + _contentText.length() + ") : " + _contentText);
}
if (_contentText.length() > 0) {
_parserDelegate.didParseComment(_NSStringUtilities.stringFromBuffer(_contentText), this);
_contentText.setLength(0);
}
}
}
}