package railo.runtime.tag;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import railo.commons.lang.IDGenerator;
import railo.commons.lang.StringUtil;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.ext.tag.BodyTagImpl;
import railo.runtime.functions.dynamicEvaluation.DE;
import railo.runtime.functions.string.JSStringFormat;
import railo.runtime.net.http.ReqRspUtil;
import railo.runtime.op.Caster;
import railo.runtime.op.Decision;
import railo.runtime.tag.util.DeprecatedUtil;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.util.KeyConstants;
/**
* implementation of the form tag
*/
public final class Form extends BodyTagImpl {
public static final int FORMAT_HTML = 0;
public static final int FORMAT_FLASH = 1;
public static final int FORMAT_XML = 2;
private static final String DEFAULT_ARCHIVE = "/railo-context/railo-applet.cfm";
private static final int WMODE_WINDOW = 0;
private static final int WMODE_TRANSPARENT = 1;
private static final int WMODE_OPAQUE = 2;
//private static int _count=0;
private int count=0;
private String name;
private String action;
private boolean preserveData;
private String onsubmit;
private String onreset;
private String onload;
private String passthrough;
private String method="post";
private String scriptSrc;
private int format=FORMAT_HTML;
private Struct attributes=new StructImpl();
private Map inputs=new LinkedHashMap();
private String strSkin;
private String archive=null;
private String codebase=null; // TODO muss einen wert haben -> /railo - context/classes/cf-j2re-win.cab.cfm
private String height="100%";
private String width="100%";
private boolean preloader=true;
private int timeout=0;
private int wMode=WMODE_WINDOW;
private boolean accessible=false;
private String onError;
@Override
public void release() {
super.release();
name=null;
action=null;
preserveData=false;
attributes.clear();
onsubmit=null;
onreset=null;
onload=null;
passthrough=null;
method="post";
scriptSrc=null;
strSkin=null;
archive=null;
codebase=null;
height="100%";
width="100%";
preloader=true;
timeout=0;
wMode=WMODE_WINDOW;
accessible=false;
onError=null;
inputs.clear();
}
/**
* @param enablecab The enablecab to set.
* @throws ApplicationException
*/
public void setEnablecab(boolean enablecab) {
DeprecatedUtil.tagAttribute(pageContext,"Form", "enablecab");
}
/**
* @param method The method to set.
* @throws ApplicationException
*/
public void setMethod(String method) throws ApplicationException {
method=method.trim().toLowerCase();
if(method.equals("get") || method.equals("post"))
this.method = method;
else
throw new ApplicationException("invalid value for attribute method from tag form, attribute can have value [get,post] but now is ["+method+"]");
}
/**
* @param format the format to set
* @throws ApplicationException
*/
public void setFormat(String strFormat) throws ApplicationException {
strFormat=strFormat.trim().toLowerCase();
if("html".equals(strFormat)) format=FORMAT_HTML;
else if("xml".equals(strFormat)) format=FORMAT_XML;
else if("flash".equals(strFormat)) format=FORMAT_FLASH;
else throw new ApplicationException("invalid value ["+strFormat+"] for attribute format, for this attribute only the following values are supported " +
"[xml, html, flash]");
if(format!=FORMAT_HTML)
throw new ApplicationException("format ["+strFormat+"] is not supported, only the following formats are supported [html]");
// TODO support other formats
}
/**
* @param skin The skin to set.
*/
public void setSkin(String strSkin) {
this.strSkin=strSkin;
}
/**
* @param action The action to set.
*/
public void setAction(String action) {
this.action = action;
}
/**
* @param scriptSrc The scriptSrc to set.
*/
public void setScriptsrc(String scriptSrc) {
this.scriptSrc = scriptSrc;
}
/**
* @param archive The archive to set.
* @throws ApplicationException
*/
public void setArchive(String archive) {
archive=archive.trim().toLowerCase().replace('\\', '/');
if(!StringUtil.startsWith(archive, '/')) archive="/"+archive;
this.archive = archive;
}
/**
* @param codebase The codebase to set.
* @throws ApplicationException
*/
public void setCodebase(String codebase) {
this.codebase = codebase;
}
/**
* @param cssclass The cssclass to set.
*/
public void setClass(String cssclass) {
attributes.setEL("class",cssclass);
}
/**
* @param cssstyle The cssstyle to set.
*/
public void setStyle(String cssstyle) {
attributes.setEL("style",cssstyle);
}
/**
* @param enctype The enctype to set.
*/
public void setEnctype(String enctype) {
attributes.setEL("enctype",enctype);
}
/**
* @param id The id to set.
*/
public void setId(String id) {
attributes.setEL("id",id);
}
public void setAccept(String accept) {
attributes.setEL("accept",accept);
}
public void setAcceptcharset(String accept_charset) {
attributes.setEL("accept-charset",accept_charset);
}
public void setAccept_charset(String accept_charset) {
attributes.setEL("accept-charset",accept_charset);
}
/**
* @param name The name to set.
* @throws ApplicationException
*/
public void setName(String name) throws ApplicationException {
this.name=name;
checkName(name);
}
private static void checkName(String name) throws ApplicationException {
if(name.length()==0)return;
int len=name.length();
for(int pos=0;pos<len;pos++) {
char c=name.charAt(pos);
if((c>='a' && c<='z')||(c>='A' && c<='Z')||(c>='0' && c<='9')||(c=='_')||(c=='-')||(c==':')||(c=='.'))
continue;
throw new ApplicationException("value of attribute name ["+name+"] is invalid, only the following characters are allowed [a-z,A-Z,0-9,-,_,:,.]");
}
}
/**
* @param onreset The onreset to set.
*/
public void setOnreset(String onreset) {
this.onreset=onreset;
}
/**
* @param onreset The onreset to set.
*/
public void setOnload(String onload) {
this.onload=onload;
}
/**
* @param onsubmit The onsubmit to set.
*/
public void setOnsubmit(String onsubmit) {
this.onsubmit = onsubmit;
}
public void setOnerror(String onError) {
this.onError=onError;
}
public void setOnclick(String onclick) {
attributes.setEL("onclick",onclick);
}
public void setOndblclick(String ondblclick) {
attributes.setEL("ondblclick",ondblclick);
}
public void setOnmousedown(String onmousedown) {
attributes.setEL("onmousedown",onmousedown);
}
public void setOnmouseup(String onmouseup) {
attributes.setEL("onmouseup",onmouseup);
}
public void setOnmouseover(String onmouseover) {
attributes.setEL("onmouseover",onmouseover);
}
public void setOnmousemove(String onmousemove) {
attributes.setEL("onmousemove",onmousemove);
}
public void setOnmouseout(String onmouseout) {
attributes.setEL("onmouseout",onmouseout);
}
public void setOnkeypress(String onkeypress) {
attributes.setEL("onkeypress",onkeypress);
}
public void setOnkeydown(String onkeydown) {
attributes.setEL("onkeydown",onkeydown);
}
public void setOnkeyup(String onkeyup) {
attributes.setEL("onkeyup",onkeyup);
}
/**
* @param passthrough The passthrough to set.
* @throws PageException
*/
public void setPassthrough(Object passthrough) throws PageException {
if(passthrough instanceof Struct) {
Struct sct = (Struct) passthrough;
//railo.runtime.type.Collection.Key[] keys=sct.keys();
//railo.runtime.type.Collection.Key key;
Iterator<Entry<Key, Object>> it = sct.entryIterator();
Entry<Key, Object> e;
while(it.hasNext()) {
e = it.next();
attributes.setEL(e.getKey(),e.getValue());
}
}
else this.passthrough = Caster.toString(passthrough);
}
/**
* @param preserveData The preserveData to set.
* @throws ApplicationException
*/
public void setPreservedata(boolean preserveData) throws ApplicationException {
//this.preserveData = preserveData;
if(preserveData)throw new ApplicationException("attribute preserveData for tag form is not supported at the moment");
}
/**
* @param target The target to set.
*/
public void setTarget(String target) {
attributes.setEL("target",target);
}
public void setTitle(String title) {
attributes.setEL("title",title);
}
public void setDir(String dir) {
attributes.setEL("dir",dir);
}
public void setLang(String lang) {
attributes.setEL("lang",lang);
}
/**
* @param height the height to set
*/
public void setHeight(String height) {
this.height=height;
}
/**
* @param width the width to set
*/
public void setWidth(String width) {
this.width=width;
}
/**
* @param preloader the preloader to set
*/
public void setPreloader(boolean preloader) {
this.preloader=preloader;
}
/**
* @param timeout the timeout to set
*/
public void setTimeout(double timeout) {
this.timeout=(int)timeout;
}
/**
* @param strWMode the wmode to set
* @throws ApplicationException
*/
public void setWmode(String strWMode) throws ApplicationException {
strWMode=strWMode.toLowerCase().trim();
if("window".equals(strWMode)) wMode=WMODE_WINDOW;
else if("transparent".equals(strWMode)) wMode=WMODE_TRANSPARENT;
else if("opaque".equals(strWMode)) wMode=WMODE_OPAQUE;
else throw new ApplicationException("invalid value ["+strWMode+"] for attribute wmode, for this attribute only the following values are supported " +
"[window, transparent, opaque]");
}
/**
* @param strWMode the wmode to set
*/
public void setAccessible(boolean accessible) {
this.accessible=accessible;
}
@Override
public int doStartTag() throws PageException {
try {
return _doStartTag();
}
catch (IOException e) {
throw Caster.toPageException(e);
}
}
private int _doStartTag() throws PageException, IOException {
String contextPath = pageContext. getHttpServletRequest().getContextPath();
if(contextPath==null) contextPath="";
if(archive==null) {
archive=contextPath+DEFAULT_ARCHIVE;
}
count=IDGenerator.intId();
if(name==null) {
name="CFForm_"+count;
}
attributes.setEL(KeyConstants._name,name);
if(action==null) action=ReqRspUtil.self(pageContext. getHttpServletRequest());
attributes.setEL(KeyConstants._action,action);
String suffix=StringUtil.isEmpty(name)?""+count:StringUtil.toVariableName(name);
String funcName="railo_form_"+count;
String checkName="_CF_check"+suffix;
String resetName="_CF_reset"+suffix;
String loadName="_CF_load"+suffix;
//boolean hasListener=false;
if(onsubmit==null) attributes.setEL("onsubmit","return "+funcName+".check();");
else {
attributes.setEL("onsubmit","return "+checkName+"();");
//hasListener=true;
}
if(onreset!=null) {
attributes.setEL("onreset",resetName+"();");
//hasListener=true;
}
if(onload!=null) {
attributes.setEL("onload",loadName+"();");
//hasListener=true;
}
if(scriptSrc==null)scriptSrc=contextPath+"/railo-context/form.cfm";
attributes.setEL("method",method);
pageContext.forceWrite("<script language = \"JavaScript\" type=\"text/javascript\" src=\""+scriptSrc+"\"></script>");
//if(hasListener) {
pageContext.forceWrite("<script language = \"JavaScript\" type=\"text/javascript\">\n");
if(onsubmit!=null)pageContext.forceWrite("function "+checkName+"() { if("+funcName+".check()){"+onsubmit+"\nreturn true;}else {return false;}}\n");
else pageContext.forceWrite("function "+checkName+"() { return "+funcName+".check();}\n");
if(onreset!=null)pageContext.forceWrite("function "+resetName+"() {"+onreset+"\n}\n");
if(onload!=null)pageContext.forceWrite("function "+loadName+"() {"+onload+"\n}\n");
pageContext.forceWrite("\n</script>");
//}
pageContext.forceWrite("<form");
Iterator<Entry<Key, Object>> it = attributes.entryIterator();
Entry<Key, Object> e;
while(it.hasNext()) {
e = it.next();
pageContext.forceWrite(" ");
pageContext.forceWrite(e.getKey().getString());
pageContext.forceWrite("=");
pageContext.forceWrite(de(Caster.toString(e.getValue())));
}
if(passthrough!=null) {
pageContext.forceWrite(" ");
pageContext.forceWrite(passthrough);
}
pageContext.forceWrite(">");
return EVAL_BODY_INCLUDE;
}
@Override
public int doEndTag() throws PageException {
String funcName="railo_form_"+count;
try {
pageContext.forceWrite("</form><!-- name:"+name+" --><script>\n");
pageContext.forceWrite(funcName+"=new RailoForms("+js(name)+","+js(onError)+");\n");
Iterator it = inputs.keySet().iterator();
while(it.hasNext()) {
InputBean input=(InputBean) inputs.get(it.next());
pageContext.forceWrite(funcName+".addInput("+js(input.getName())+","+input.isRequired()+
","+input.getType()+","+input.getValidate()+
","+(input.getPattern())+","+js(input.getMessage())+
","+js(input.getOnError())+","+js(input.getOnValidate())+
","+range(input.getRangeMin())+","+range(input.getRangeMax())+
","+(input.getMaxLength())+
");\n");
}
pageContext.forceWrite("</script>");
}
catch (IOException e) {
throw Caster.toPageException(e);
}
return EVAL_PAGE;
}
private String range(double range) {
if(!Decision.isValid(range)) return "null";
return Caster.toString(range);
}
private String de(String str) {
try {
return DE.call(pageContext,str);
} catch (ExpressionException e) {
return "\"\"";
}
}
private String js(String str) {
if(str==null) return "null";
return "'"+JSStringFormat.call(pageContext,str)+"'";
}
/**
* @param input
* @throws ApplicationException
*/
public void setInput(InputBean input) throws ApplicationException {
if(input.getType()==Input.TYPE_TEXT || input.getType()==Input.TYPE_PASSWORD) {
InputBean i=(InputBean)inputs.get(input.getName().toLowerCase());
if(i!=null && (i.getType()==Input.TYPE_TEXT || i.getType()==Input.TYPE_PASSWORD)) {
throw new ApplicationException("duplicate input field ["+i.getName()+"] for form","a text or password field must be unique");
}
}
//if(StringUtil.isEmpty(input.getOnError(),true) && !StringUtil.isEmpty(onError,true))
// input.setOnError(onError);
inputs.put(input.getName().toLowerCase(),input);
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @return Returns the onsubmit.
*/
public String getOnsubmit() {
return onsubmit;
}
public int getFormat() {
return format;
}
public String getArchive() {
return archive;
}
public String getCodebase() {
return codebase;
}
}