/*
* HTML.Template: A module for using HTML Templates with java
*
* Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
*
* This module is free software; you can redistribute it
* and/or modify it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option)
* any later version, or
*
* b) the "Artistic License" which comes with this module.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See either the GNU General Public License or the
* Artistic License for more details.
*
* You should have received a copy of the Artistic License
* with this module, in the file ARTISTIC. If not, I'll be
* glad to provide one.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package HTML.Tmpl.Parsers;
import ewe.util.*;
import HTML.Tmpl.Element.*;
import HTML.Tmpl.Util;
public class Parser
{
private boolean case_sensitive=false;
private boolean strict=true;
private boolean loop_context_vars=false;
private boolean global_vars=false;
public Parser()
{
}
public Parser(String [] args)
throws ArrayIndexOutOfBoundsException,
IllegalArgumentException
{
if(args.length%2 != 0)
throw new ArrayIndexOutOfBoundsException("odd number of arguments passed");
for(int i=0; i<args.length; i+=2) {
if(args[i].equals("case_sensitive")) {
String cs = args[i+1];
if(cs.equals("") || cs.equals("0"))
this.case_sensitive=false;
else
this.case_sensitive=true;
} else if(args[i].equals("strict")) {
String s = args[i+1];
if(s.equals("") || s.equals("0"))
this.strict=false;
else
this.strict=true;
} else if(args[i].equals("loop_context_vars")) {
String s = args[i+1];
if(s.equals("") || s.equals("0"))
this.loop_context_vars=false;
else
this.loop_context_vars=true;
} else if(args[i].equals("global_vars")) {
String s = args[i+1];
if(s.equals("") || s.equals("0"))
this.global_vars=false;
else
this.global_vars=true;
} else {
throw new IllegalArgumentException(args[i]);
}
}
}
public Element getElement(Properties p)
throws NoSuchElementException
{
String type = p.getProperty("type");
if(type.equals("if"))
return new If(p.getProperty("name"));
else if(type.equals("unless"))
return new Unless(p.getProperty("name"));
else if(type.equals("loop"))
return new Loop(p.getProperty("name"),
loop_context_vars, global_vars);
else
throw new NoSuchElementException(type);
}
public Vector parseLine(String line)
throws IllegalArgumentException
{
//int pos=0, endpos;
Vector parts = new Vector();
char [] c = line.toCharArray();
int i=0;
StringBuffer temp = new StringBuffer();
for(i=0; i<c.length; i++) {
if(c[i] != '<') {
temp.append(c[i]);
} else {
// found a tag
Util.debug_print("line so far: " + temp);
StringBuffer tag = new StringBuffer();
for(; i<c.length && c[i] != '>'; i++) {
tag.append(c[i]);
}
// > is not allowed inside a template tag
// so we can be sure that if this is a
// template tag, it ends with a >
// add the closing > as well
if(i<c.length)
tag.append(c[i]);
// if this contains more < inside it,
// then it could possibly be a template
// tag inside a html tag
// so remove external tag parts
while(tag.toString().substring(1).indexOf("<")
> -1)
{
do {
temp.append(tag.charAt(0));
tag=new StringBuffer(
tag.toString().substring(1));
} while(tag.charAt(0) != '<');
}
Util.debug_print("tag: " + tag);
String test_tag = tag.toString().toLowerCase();
// if it doesn't contain tmpl_ it is not
// a template tag
if(test_tag.indexOf("tmpl_") < 0) {
String dummy = new String();
dummy = tag.toString();
temp.append(dummy);
continue;
}
// may be a template tag
// check if it starts with tmpl_
test_tag = cleanTag(test_tag);
Util.debug_print("clean: " + test_tag);
// check if it is a closing tag
if(test_tag.startsWith("/"))
test_tag = test_tag.substring(1);
// if it still doesn't start with tmpl_
// then it is not a template tag
if(!test_tag.startsWith("tmpl_")) {
temp.append(tag);
continue;
}
// now it must be a template tag
String tag_type=getTagType(test_tag);
if(tag_type == null) {
if(strict)
throw new
IllegalArgumentException(
tag.toString());
else
temp.append(tag);
}
Util.debug_print("type: " + tag_type);
// if this was an invalid key and we've
// reached so far, then next iteration
if(tag_type == null)
continue;
// now, push the previous stuff
// into the Vector
if(temp.length()>0) {
parts.addElement(temp.toString());
temp = new StringBuffer();
}
// it is a valid template tag
// get its properties
Util.debug_print("Checking: " + tag);
Properties tag_props =
getTagProps(tag.toString());
if(tag_props.containsKey("name"))
Util.debug_print("name: " +
tag_props.getProperty("name"));
else
Util.debug_print("no name");
parts.addElement(tag_props);
}
}
if(temp.length()>0)
parts.addElement(temp.toString());
return parts;
}
private String cleanTag(String tag)
throws IllegalArgumentException
{
String test_tag = new String(tag);
// first remove < and >
if(test_tag.startsWith("<"))
test_tag = test_tag.substring(1);
if(test_tag.endsWith(">"))
test_tag = test_tag.substring(0, test_tag.length()-1);
else
throw new IllegalArgumentException("Tags must start " +
"and end on the same line");
// remove any leading !-- and trailing
// -- in case of comment style tags
if(test_tag.startsWith("!--")) {
test_tag=test_tag.substring(3);
}
if(test_tag.endsWith("--")) {
test_tag=test_tag.substring(0, test_tag.length()-2);
}
// then leading and trailing spaces
test_tag = test_tag.trim();
return test_tag;
}
private String getTagType(String tag)
{
int sp = tag.indexOf(" ");
String tag_type="";
if(sp < 0) {
tag_type = tag.toLowerCase();
} else {
tag_type = tag.substring(0, sp).toLowerCase();
}
if(tag_type.startsWith("tmpl_"))
tag_type=tag_type.substring(5);
Util.debug_print("tag_type: " + tag_type);
if(tag_type.equals("var") ||
tag_type.equals("if") ||
tag_type.equals("unless") ||
tag_type.equals("loop") ||
tag_type.equals("include") ||
tag_type.equals("else")) {
return tag_type;
} else {
return null;
}
}
private Properties getTagProps(String tag)
throws IllegalArgumentException,
NullPointerException
{
Properties p = new Properties();
tag = cleanTag(tag);
Util.debug_print("clean: " + tag);
if(tag.startsWith("/")) {
p.put("close", "true");
tag=tag.substring(1);
} else {
p.put("close", "");
}
Util.debug_print("close: " + p.getProperty("close"));
p.put("type", getTagType(tag));
Util.debug_print("type: " + p.getProperty("type"));
if(p.getProperty("type").equals("else") ||
p.getProperty("close").equals("true"))
return p;
if(p.getProperty("type").equals("var"))
p.put("escape", "");
int sp = tag.indexOf(" ");
// if we've got so far, this must succeed
tag = tag.substring(sp).trim();
Util.debug_print("checking params: " + tag);
// now, we should have either name=value pairs
// or name space escape in case of old style vars
if(tag.indexOf("=") < 0) {
// no = means old style
// first will be var name
// second if any will be escape
sp = tag.toLowerCase().indexOf(" escape");
if(sp < 0) {
// no escape
p.put("name", tag);
p.put("escape", "0");
} else {
tag = tag.substring(0, sp);
p.put("name", tag);
p.put("escape", "html");
}
} else {
// = means name=value pairs.
// use a StringTokenizer
StringTokenizer st = new StringTokenizer(tag, " =");
while(st.hasMoreTokens()) {
String key, value;
key = st.nextToken().toLowerCase();
if(st.hasMoreTokens())
value = st.nextToken();
else if(key.equals("escape"))
value = "html";
else
throw new NullPointerException(
"parameter " + key + " has no value");
if(value.startsWith("\"") &&
value.endsWith("\""))
value = value.substring(1,
value.length()-1);
else if(value.startsWith("'") &&
value.endsWith("'"))
value = value.substring(1,
value.length()-1);
if(value.length()==0)
throw new NullPointerException(
"parameter " + key + " has no value");
if(key.equals("escape"))
value=value.toLowerCase();
p.put(key, value);
}
}
String name = p.getProperty("name");
// if not case sensitive, and not special variable, flatten case
// never flatten case for includes
if(!case_sensitive && !p.getProperty("type").equals("include")
&& !( name.startsWith("__") && name.endsWith("__") ))
{
p.put("name", name.toLowerCase());
}
if(!Util.isNameChar(name))
throw new IllegalArgumentException(
"parameter name may only contain " +
"letters, digits, ., /, +, -, _");
// __var__ is allowed in the template, but not in the
// code. this is so that people can reference __FIRST__,
// etc
return p;
}
}