//{HEADER
/**
* This class is part of jnex 'Nexirius Application Framework for Java'
*
* Copyright (C) Nexirius GmbH, CH-4450 Sissach, Switzerland (www.nexirius.ch)
*
* <p>This library is free software; you can redistribute it and/or<br>
* modify it under the terms of the GNU Lesser General Public<br>
* License as published by the Free Software Foundation; either<br>
* version 2.1 of the License, or (at your option) any later version.</p>
*
* <p>This library is distributed in the hope that it will be useful,<br>
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
* Lesser General Public License for more details.</p>
*
* <p>You should have received a copy of the GNU Lesser General Public<br>
* License along with this library; if not, write to the Free Software<br>
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
* </blockquote>
*
* <p>
* Nexirius GmbH, hereby disclaims all copyright interest in<br>
* the library jnex' 'Nexirius Application Framework for Java' written<br>
* by Marcel Baumann.</p>
*/
//}HEADER
package com.nexirius.framework.htmlview;
import com.nexirius.framework.datamodel.DataModel;
import com.nexirius.framework.htmlview.function.HTMLFunction;
import com.nexirius.util.TextToken;
import com.nexirius.util.StringVector;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PushbackInputStream;
/**
* This class translates a HTML file which contains special functions into a real HTML output stream<BR>
* special functions:
* <PRE>
* $!include("template")
* $!includeBody("template")
* $!field("field name", "edit|view", "template")
* $!popup("childname", view|edit, "template", "link text", "event", "true|false") if parameter 6 is true, then the actual model is duplicated in the actual state with HTMLState.startDuplicatePopup()
* $!event("event", "link text", "&moreParameter=value")
* $!iconEvent("event", "iconName", "&moreParameter=value")
* $!button("event")
* $!array("field name of the array", "edit|view", "template")
* $!flag("field name", "flag", "on text", "off text")
* $!select("field name")
* $!define("name", "value")
* $!formStart()
* $!formEnd()
* </PRE>
*/
public class HTMLParser {
public static final String NULL = "null";
public static final String EDIT = "edit";
public static final String VIEW = "view";
public static final String CHECKED = "CHECKED";
DataModel model;
boolean isEditor;
HTMLResolver resolver;
OutputStream out;
int translateCount = 0;
/**
* creates a new instance of HTMLParser
*
* @param model the actual model which is assotiated to the input stream (see parse)
* @param isEditor the actual general translation rule
* @param resolver the resolver wich created this parser (and which is used to handle subsequent nested function calls
* @param out the actual output stream
* @throws Exception
*/
public HTMLParser(DataModel model, boolean isEditor, HTMLResolver resolver, OutputStream out)
throws Exception {
this.model = model;
this.isEditor = isEditor;
this.resolver = resolver;
this.out = out;
}
public DataModel getModel() {
return model;
}
public boolean isEditor() {
return isEditor;
}
public HTMLResolver getResolver() {
return resolver;
}
/**
* Return the model which belongs to the actual state
* @return
*/
public DataModel getRootModel() {
return resolver.getRootModel();
}
public OutputStream getOut() {
return out;
}
public void setOut(OutputStream out) {
this.out = out;
}
/**
* parses the input stream and calls parseFunction when the $! is found
*
* @param sessionVariable
* @param in the actual input stream
* @return the number of functions which are translated
* @throws Exception
*/
public int parse(HTMLSessionVariable sessionVariable, PushbackInputStream in)
throws Exception {
int state = 0;
translateCount = 0;
while (true) {
int c = in.read();
if (c < 0) {
break;
}
switch (state) {
case 0:
if (c == '$') {
state = 1;
} else {
out.write(c);
}
break;
case 1:
if (c == '!') {
state = 0;
try {
HTMLFunction f = parseFunction(sessionVariable, in);
String[] arguments = parseFunctionArguments(sessionVariable, in);
f.translate(sessionVariable, this, arguments);
++translateCount;
} catch (Exception ex) {
ex.printStackTrace(new PrintStream(out));
}
} else {
out.write('$');
out.write(c);
state = 0;
}
break;
}
}
return translateCount;
}
/**
* reads the input stream and parses !$function() calls
*
* @param in the input stream from which functions are extracted
* @return the actual function
* @throws Exception when syntax of the function is not correct
*/
public HTMLFunction parseFunction(HTMLSessionVariable sessionVariable, PushbackInputStream in)
throws Exception {
HTMLFunction ret = null;
TextToken token = TextToken.nextToken(in);
if (token == null) {
throw new Exception("Expecting function name at end of stream");
}
if (token.isIdentifier()) {
ret = resolver.getFunction(token.getString());
} else {
throw new Exception("Expecting function name but have " + token.debugString());
}
if (ret == null) {
throw new Exception("Unknown function name " + token.debugString());
}
return ret;
}
public String[] parseFunctionArguments(HTMLSessionVariable sessionVariable, PushbackInputStream in) throws Exception {
StringVector functionArgumentList = new StringVector();
TextToken token;
token = TextToken.nextToken(in);
if (token == null) {
throw new Exception("Expecting '(' after function name");
}
if (token.isChar('(')) {
while (true) {
token = TextToken.nextToken(in);
if (token.isChar(')')) {
break;
}
if (token.isString()) {
String s = token.getString();
s = resolve(sessionVariable, s);
functionArgumentList.append(s);
} else if (token.isInt()) {
functionArgumentList.append(Integer.toString(token.getInt()));
} else if (token.isIdentifier(NULL)) {
functionArgumentList.append((String)null);
} else if (token.isIdentifier()) {
functionArgumentList.append(token.getString());
} else {
throw new Exception("Unexpected token in parameter list:" + token.debugString());
}
token = TextToken.nextToken(in);
if (token.isChar(',')) {
// expected
} else if (token.isChar(')')) {
break;
} else {
throw new Exception("Unexpected token in parameter list:" + token.debugString());
}
}
} else {
throw new Exception("Expecting '(' but have " + token.debugString());
}
return functionArgumentList.getArray();
}
private String resolve(HTMLSessionVariable sessionVariable, String s) throws Exception {
byte[] bytes = getResolver().resolve(sessionVariable, model, new ByteArrayInputStream(s.getBytes()), isEditor);
return new String(bytes);
}
/**
* access the number of translated functions
*
* @return
*/
public int getNumberOfTranslations() {
return translateCount;
}
}