001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.type;
020
021import lucee.commons.lang.CFTypes;
022import lucee.commons.lang.StringUtil;
023import lucee.runtime.Component;
024import lucee.runtime.ComponentImpl;
025import lucee.runtime.Page;
026import lucee.runtime.PageContext;
027import lucee.runtime.PageContextImpl;
028import lucee.runtime.PageSource;
029import lucee.runtime.component.MemberSupport;
030import lucee.runtime.dump.DumpData;
031import lucee.runtime.dump.DumpProperties;
032import lucee.runtime.exp.DeprecatedException;
033import lucee.runtime.exp.ExpressionException;
034import lucee.runtime.exp.PageException;
035import lucee.runtime.exp.PageRuntimeException;
036import lucee.runtime.exp.UDFCasterException;
037import lucee.runtime.functions.decision.IsValid;
038import lucee.runtime.op.Caster;
039import lucee.runtime.op.Decision;
040import lucee.runtime.type.Collection.Key;
041import lucee.runtime.type.util.ComponentUtil;
042import lucee.runtime.type.util.KeyConstants;
043import lucee.runtime.type.util.UDFUtil;
044
045public abstract class UDFGSProperty extends MemberSupport implements UDFPlus {
046
047        private static final Collection.Key MIN_LENGTH = KeyImpl.intern("minLength");
048        private static final Collection.Key MAX_LENGTH = KeyImpl.intern("maxLength");
049        
050        protected final FunctionArgument[] arguments;
051        protected final String name;
052        protected ComponentImpl component;
053        private UDFPropertiesImpl properties;
054
055        public UDFGSProperty(ComponentImpl component,String name,FunctionArgument[] arguments,short rtnType,String rtnFormat) {
056                super(Component.ACCESS_PUBLIC);
057                properties=new UDFPropertiesImpl(
058                                component.getPageSource(),
059                                arguments,
060                                -1,
061                                name,
062                                rtnType,
063                                rtnFormat,
064                                false,
065                                Component.ACCESS_PUBLIC,
066                                true,
067                                "",
068                                "",
069                                "",
070                                Boolean.FALSE,
071                                Boolean.FALSE,
072                                null,
073                                null,
074                                null
075                                
076                );
077                                
078                this.name=name;
079                this.arguments=arguments;
080                this.component=component;
081        }
082
083
084        @Override
085        public FunctionArgument[] getFunctionArguments() {
086                return arguments;
087        }
088
089        @Override
090        public String getFunctionName() {
091                return name;
092        }
093
094        @Override
095        public PageSource getPageSource() {
096                return component.getPageSource();
097        }
098
099        @Override
100        public int getIndex() {
101                return -1;
102        }
103
104        @Override
105        public Component getOwnerComponent() {
106                return component;
107        }
108
109        public void setOwnerComponent(ComponentImpl component) {
110                this.component = component;
111        }
112        
113        public Page getPage() {
114                throw new PageRuntimeException(new DeprecatedException("method getPage():Page is no longer suppoted, use instead getPageSource():PageSource"));
115    }
116
117        @Override
118        public boolean getOutput() {
119                return false;
120        }
121 
122        public UDF duplicate(boolean deep) {
123                return duplicate(); // deep has no influence here, because a UDF is not a collection
124        }
125
126        @Override
127        public String getDisplayName() {
128                return "";
129        }
130
131        @Override
132        public String getDescription() {
133                return "";
134        }
135
136        @Override
137        public String getHint() {
138                return "";
139        }
140
141        @Override
142        public int getReturnFormat() {
143                return UDF.RETURN_FORMAT_WDDX;
144        }
145
146        @Override
147        public int getReturnFormat(int defaultValue) {
148                return defaultValue;
149        }
150
151        @Override
152        public int getReturnType() {
153                return CFTypes.toShortStrict(getReturnTypeAsString(),CFTypes.TYPE_UNKNOW);
154        }
155
156        @Override
157        public Object getValue() {
158                return this;
159        }
160        
161        @Override
162        public Boolean getSecureJson() {
163                return null;
164        }
165
166        @Override
167        public Boolean getVerifyClient() {
168                return null;
169        }
170
171        @Override
172        public DumpData toDumpData(PageContext pageContext, int maxlevel,DumpProperties properties) {
173                return UDFUtil.toDumpData(pageContext, maxlevel, properties, this,UDFUtil.TYPE_UDF);
174        }
175        
176        @Override
177        public Struct getMetaData(PageContext pc) throws PageException {
178                return ComponentUtil.getMetaData(pc, properties);
179        }
180        
181        
182
183        final Object cast(PageContext pc,FunctionArgument arg,Object value, int index) throws PageException {
184                if(value==null || Decision.isCastableTo(pc,arg.getType(),arg.getTypeAsString(),value)) 
185                        return value;
186                throw new UDFCasterException(this,arg,value,index);
187        }
188
189        final static void validate(String validate, Struct validateParams, Object obj) throws PageException {
190                if(StringUtil.isEmpty(validate,true)) return;
191                validate=validate.trim().toLowerCase();
192                
193                if(!validate.equals("regex") && !Decision.isValid(validate, obj))
194                        throw new ExpressionException(createMessage(validate, obj));
195                
196                
197                // range
198                if(validateParams==null) return;
199
200                if(validate.equals("integer") || validate.equals("numeric") || validate.equals("number")){
201                        double min=Caster.toDoubleValue(validateParams.get(KeyConstants._min,null),false,Double.NaN);
202                        double max=Caster.toDoubleValue(validateParams.get(KeyConstants._max,null),false,Double.NaN);
203                        double d=Caster.toDoubleValue(obj);
204                        if(!Double.isNaN(min) && d<min)
205                                throw new ExpressionException(validate+" ["+Caster.toString(d)+"] is out of range, value must be more than or equal to ["+min+"]");
206                        if(!Double.isNaN(max) && d>max)
207                                throw new ExpressionException(validate+" ["+Caster.toString(d)+"] is out of range, value must be less than or equal to ["+max+"]");
208                }
209                else if(validate.equals("string")){
210                        double min=Caster.toDoubleValue(validateParams.get(MIN_LENGTH,null),false,Double.NaN);
211                        double max=Caster.toDoubleValue(validateParams.get(MAX_LENGTH,null),false,Double.NaN);
212                        String str=Caster.toString(obj);
213                        int l=str.length();
214                        if(!Double.isNaN(min) && l<((int)min))
215                                throw new ExpressionException("string ["+str+"] is to short ["+l+"], the string must be at least ["+min+"] characters");
216                        if(!Double.isNaN(max) && l>((int)max))
217                                throw new ExpressionException("string ["+str+"] is to long ["+l+"], the string can have a maximum length of ["+max+"] characters");
218                }
219                else if(validate.equals("regex")){
220                        String pattern=Caster.toString(validateParams.get(KeyConstants._pattern,null),null);
221                        String value=Caster.toString(obj);
222                        if(!StringUtil.isEmpty(pattern,true) && !IsValid.regex(value, pattern))
223                                throw new ExpressionException("the string ["+value+"] does not match the regular expression pattern ["+pattern+"]");
224                }
225        }
226
227        
228        @Override
229        public Object callWithNamedValues(PageContext pc, Key calledName, Struct values, boolean doIncludePath) throws PageException {
230                PageContextImpl pci = ((PageContextImpl)pc);
231                Key old =pci.getActiveUDFCalledName();
232                pci.setActiveUDFCalledName(calledName);
233                try{
234                        return callWithNamedValues(pci, values, doIncludePath);
235                }
236                finally{
237                        pci.setActiveUDFCalledName(old);
238                }
239        }
240
241        @Override
242        public Object call(PageContext pc, Key calledName, Object[] args, boolean doIncludePath) throws PageException {
243                PageContextImpl pci = ((PageContextImpl)pc);
244                Key old =pci.getActiveUDFCalledName();
245                pci.setActiveUDFCalledName(calledName);
246                try{
247                        return call(pci, args, doIncludePath);
248                }
249                finally{
250                        pci.setActiveUDFCalledName(old);
251                }
252        }
253
254        private static String createMessage(String format, Object value) {
255        if(Decision.isSimpleValue(value)) return "the value ["+Caster.toString(value,null)+"] is not in  ["+format+"] format";
256        return "cannot convert object from type ["+Caster.toTypeName(value)+"] to a ["+format+"] format";
257    }   
258        
259}