/**
* Copyright (c) 2014, the Railo Company Ltd.
* Copyright (c) 2015, Lucee Assosication Switzerland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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 the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
package lucee.runtime.type;
import java.security.NoSuchAlgorithmException;
import lucee.commons.digest.Hash;
import lucee.commons.lang.CFTypes;
import lucee.commons.lang.StringUtil;
import lucee.runtime.Component;
import lucee.runtime.Page;
import lucee.runtime.PageContext;
import lucee.runtime.PageContextImpl;
import lucee.runtime.PageSource;
import lucee.runtime.component.MemberSupport;
import lucee.runtime.dump.DumpData;
import lucee.runtime.dump.DumpProperties;
import lucee.runtime.exp.DeprecatedException;
import lucee.runtime.exp.ExpressionException;
import lucee.runtime.exp.PageException;
import lucee.runtime.exp.PageRuntimeException;
import lucee.runtime.exp.UDFCasterException;
import lucee.runtime.functions.decision.IsValid;
import lucee.runtime.op.Caster;
import lucee.runtime.op.Decision;
import lucee.runtime.type.Collection.Key;
import lucee.runtime.type.util.ComponentUtil;
import lucee.runtime.type.util.KeyConstants;
import lucee.runtime.type.util.UDFUtil;
public abstract class UDFGSProperty extends MemberSupport implements UDFPlus {
private static final long serialVersionUID = 285652503901488683L;
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 Component component;
private UDFPropertiesBase properties;
private String id;
public UDFGSProperty(Component component,String name,FunctionArgument[] arguments,short rtnType) {
super(Component.ACCESS_PUBLIC);
properties=UDFProperties(null,
component.getPageSource(),
arguments,
name,
rtnType
);
this.name=name;
this.arguments=arguments;
this.component=component;
}
private static UDFPropertiesBase UDFProperties(
Page page,
PageSource pageSource,
FunctionArgument[] arguments,
String functionName,
short returnType) {
return new UDFPropertiesLight(page,
pageSource,
arguments,
functionName,
returnType);
}
@Override
public FunctionArgument[] getFunctionArguments() {
return arguments;
}
@Override
public String getFunctionName() {
return name;
}
/*@Override
public PageSource getPageSource() {
return component.getPageSource();
}*/
@Override
public boolean equals(Object other) {
if(!(other instanceof UDF)) return false;
return UDFImpl.equals(this, (UDF) other);
}
@Override
public String getSource() {
PageSource ps = component.getPageSource();
if(ps!=null) return ps.getDisplayPath();
return "";
}
@Override
public String id() {
if(id==null) {
try {
id=Hash.md5(component.id()+":"+getFunctionName());
}
catch (NoSuchAlgorithmException e) {
id=component.id()+":"+getFunctionName();
}
}
return id;
}
@Override
public int getIndex() {
return -1;
}
@Override
public Component getOwnerComponent() {
return component;
}
@Override
public void setOwnerComponent(Component 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;
}
@Override
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,UDFUtil.TYPE_UDF);
}
@Override
public Struct getMetaData(PageContext pc) throws PageException {
return ComponentUtil.getMetaData(pc, properties);
}
final Object cast(PageContext pc,FunctionArgument arg,Object value, int index) throws PageException {
if(value==null || Decision.isCastableTo(pc,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),false,Double.NaN);
double max=Caster.toDoubleValue(validateParams.get(KeyConstants._max,null),false,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),false,Double.NaN);
double max=Caster.toDoubleValue(validateParams.get(MAX_LENGTH,null),false,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";
}
@Override
public PageSource getPageSource() {
return this.properties.getPageSource();
}
@Override
public boolean getBufferOutput(PageContext pc) {
return pc.getApplicationContext().getBufferOutput();
}
}