package railo.runtime.type;
import railo.commons.lang.CFTypes;
import railo.commons.lang.StringUtil;
import railo.runtime.Component;
import railo.runtime.ComponentImpl;
import railo.runtime.Page;
import railo.runtime.PageContext;
import railo.runtime.PageContextImpl;
import railo.runtime.PageSource;
import railo.runtime.component.MemberSupport;
import railo.runtime.dump.DumpData;
import railo.runtime.dump.DumpProperties;
import railo.runtime.exp.DeprecatedException;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.exp.PageRuntimeException;
import railo.runtime.exp.UDFCasterException;
import railo.runtime.functions.decision.IsValid;
import railo.runtime.op.Caster;
import railo.runtime.op.Decision;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.util.ComponentUtil;
import railo.runtime.type.util.KeyConstants;
import railo.runtime.type.util.UDFUtil;
public abstract class UDFGSProperty extends MemberSupport implements UDFPlus {
private static final Collection.Key MIN_LENGTH = KeyImpl.intern("minLength");
private static final Collection.Key MAX_LENGTH = KeyImpl.intern("maxLength");
protected final FunctionArgument[] arguments;
protected final String name;
protected ComponentImpl component;
private UDFPropertiesImpl properties;
public UDFGSProperty(ComponentImpl component,String name,FunctionArgument[] arguments,short rtnType,String rtnFormat) {
super(Component.ACCESS_PUBLIC);
properties=UDFProperties(
component.getPageSource(),
arguments,
-1,
name,
rtnType,
rtnFormat,
false,
true,
Component.ACCESS_PUBLIC,
"",
"",
"",
Boolean.FALSE,
Boolean.FALSE,
0L,
null,
new StructImpl()
);
this.name=name;
this.arguments=arguments;
this.component=component;
}
private static UDFPropertiesImpl UDFProperties(PageSource pageSource,
FunctionArgument[] arguments,
int index,
String functionName,
short returnType,
String strReturnFormat,
boolean output,
Boolean bufferOutput,
int access,
String displayName,
String description,
String hint,
Boolean secureJson,
Boolean verifyClient,
long cachedWithin,
Integer localMode,
StructImpl meta) {
return new UDFPropertiesImpl( pageSource,
arguments,
index,
functionName,
returnType,
strReturnFormat,
output,
access,
bufferOutput,
displayName,
description,
hint,
secureJson,
verifyClient,
cachedWithin,
localMode,
meta);
}
@Override
public FunctionArgument[] getFunctionArguments() {
return arguments;
}
@Override
public String getFunctionName() {
return name;
}
@Override
public PageSource getPageSource() {
return component.getPageSource();
}
@Override
public int getIndex() {
return -1;
}
@Override
public Component getOwnerComponent() {
return component;
}
public void setOwnerComponent(ComponentImpl component) {
this.component = component;
}
public Page getPage() {
throw new PageRuntimeException(new DeprecatedException("method getPage():Page is no longer suppoted, use instead getPageSource():PageSource"));
}
@Override
public boolean getOutput() {
return false;
}
public UDF duplicate(boolean deep) {
return duplicate(); // deep has no influence here, because a UDF is not a collection
}
@Override
public String getDisplayName() {
return "";
}
@Override
public String getDescription() {
return "";
}
@Override
public String getHint() {
return "";
}
@Override
public int getReturnFormat() {
return UDF.RETURN_FORMAT_WDDX;
}
@Override
public int getReturnFormat(int defaultValue) {
return defaultValue;
}
@Override
public int getReturnType() {
return CFTypes.toShortStrict(getReturnTypeAsString(),CFTypes.TYPE_UNKNOW);
}
@Override
public Object getValue() {
return this;
}
@Override
public Boolean getSecureJson() {
return null;
}
@Override
public Boolean getVerifyClient() {
return null;
}
@Override
public DumpData toDumpData(PageContext pageContext, int maxlevel,DumpProperties properties) {
return UDFUtil.toDumpData(pageContext, maxlevel, properties, this,false);
}
@Override
public Struct getMetaData(PageContext pc) throws PageException {
return ComponentUtil.getMetaData(pc, properties);
}
final Object cast(FunctionArgument arg,Object value, int index) throws PageException {
if(value==null || Decision.isCastableTo(arg.getType(),arg.getTypeAsString(),value))
return value;
throw new UDFCasterException(this,arg,value,index);
}
final static void validate(String validate, Struct validateParams, Object obj) throws PageException {
if(StringUtil.isEmpty(validate,true)) return;
validate=validate.trim().toLowerCase();
if(!validate.equals("regex") && !Decision.isValid(validate, obj))
throw new ExpressionException(createMessage(validate, obj));
// range
if(validateParams==null) return;
if(validate.equals("integer") || validate.equals("numeric") || validate.equals("number")){
double min=Caster.toDoubleValue(validateParams.get(KeyConstants._min,null),Double.NaN);
double max=Caster.toDoubleValue(validateParams.get(KeyConstants._max,null),Double.NaN);
double d=Caster.toDoubleValue(obj);
if(!Double.isNaN(min) && d<min)
throw new ExpressionException(validate+" ["+Caster.toString(d)+"] is out of range, value must be more than or equal to ["+min+"]");
if(!Double.isNaN(max) && d>max)
throw new ExpressionException(validate+" ["+Caster.toString(d)+"] is out of range, value must be less than or equal to ["+max+"]");
}
else if(validate.equals("string")){
double min=Caster.toDoubleValue(validateParams.get(MIN_LENGTH,null),Double.NaN);
double max=Caster.toDoubleValue(validateParams.get(MAX_LENGTH,null),Double.NaN);
String str=Caster.toString(obj);
int l=str.length();
if(!Double.isNaN(min) && l<((int)min))
throw new ExpressionException("string ["+str+"] is to short ["+l+"], the string must be at least ["+min+"] characters");
if(!Double.isNaN(max) && l>((int)max))
throw new ExpressionException("string ["+str+"] is to long ["+l+"], the string can have a maximum length of ["+max+"] characters");
}
else if(validate.equals("regex")){
String pattern=Caster.toString(validateParams.get(KeyConstants._pattern,null),null);
String value=Caster.toString(obj);
if(!StringUtil.isEmpty(pattern,true) && !IsValid.regex(value, pattern))
throw new ExpressionException("the string ["+value+"] does not match the regular expression pattern ["+pattern+"]");
}
}
@Override
public Object callWithNamedValues(PageContext pc, Key calledName, Struct values, boolean doIncludePath) throws PageException {
PageContextImpl pci = ((PageContextImpl)pc);
Key old =pci.getActiveUDFCalledName();
pci.setActiveUDFCalledName(calledName);
try{
return callWithNamedValues(pci, values, doIncludePath);
}
finally{
pci.setActiveUDFCalledName(old);
}
}
@Override
public Object call(PageContext pc, Key calledName, Object[] args, boolean doIncludePath) throws PageException {
PageContextImpl pci = ((PageContextImpl)pc);
Key old =pci.getActiveUDFCalledName();
pci.setActiveUDFCalledName(calledName);
try{
return call(pci, args, doIncludePath);
}
finally{
pci.setActiveUDFCalledName(old);
}
}
private static String createMessage(String format, Object value) {
if(Decision.isSimpleValue(value)) return "the value ["+Caster.toString(value,null)+"] is not in ["+format+"] format";
return "cannot convert object from type ["+Caster.toTypeName(value)+"] to a ["+format+"] format";
}
}