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 java.util.HashMap;
022import java.util.Map;
023
024import lucee.commons.lang.CFTypes;
025import lucee.commons.lang.StringUtil;
026import lucee.runtime.ComponentImpl;
027import lucee.runtime.PageContext;
028import lucee.runtime.component.Property;
029import lucee.runtime.exp.ExpressionException;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.type.Collection.Key;
032import lucee.runtime.type.util.CollectionUtil;
033import lucee.runtime.type.util.KeyConstants;
034import lucee.runtime.type.util.PropertyFactory;
035import lucee.runtime.type.util.UDFUtil;
036
037public final class UDFAddProperty extends UDFGSProperty {
038
039        private final Property prop;
040        //private ComponentScope scope;
041        
042        private final Key propName;
043        
044        private static final Object NULL=new Object();
045
046        public UDFAddProperty(ComponentImpl component,Property prop)  {
047                super(component,"add"+StringUtil.ucFirst(PropertyFactory.getSingularName(prop)),getFunctionArgument(prop),CFTypes.TYPE_ANY,"wddx");
048                this.prop=prop;
049                this.propName=KeyImpl.getInstance(prop.getName());
050        } 
051
052        private static FunctionArgument[] getFunctionArgument(Property prop) {
053                String t = PropertyFactory.getType(prop);
054                FunctionArgument value = new FunctionArgumentImpl(KeyImpl.init(PropertyFactory.getSingularName(prop)),"any",CFTypes.TYPE_ANY,true);
055                if("struct".equalsIgnoreCase(t)){
056                        FunctionArgument key = new FunctionArgumentImpl(KeyConstants._key,"string",CFTypes.TYPE_STRING,true);
057                        return new FunctionArgument[]{key,value};
058                }
059                return new FunctionArgument[]{value};
060        }       
061 
062        public UDF duplicate() {
063                return new UDFAddProperty(component,prop);
064        }
065        
066        @Override
067        public Object call(PageContext pageContext, Object[] args,boolean doIncludePath) throws PageException {
068                // struct
069                if(this.arguments.length==2) {
070                        if(args.length<2)
071                                throw new ExpressionException("The function "+getFunctionName()+" need 2 arguments, only "+args.length+" argment"+(args.length==1?" is":"s are")+" passed in.");
072                        return _call(pageContext, args[0], args[1]);
073                }
074                // array
075                else if(this.arguments.length==1) {
076                        if(args.length<1)
077                                throw new ExpressionException("The parameter "+this.arguments[0].getName()+" to function "+getFunctionName()+" is required but was not passed in.");
078                        return _call(pageContext, null, args[0]);
079                }
080                
081                // never reached
082                return component;
083                
084        }
085
086        @Override
087        public Object callWithNamedValues(PageContext pageContext, Struct values,boolean doIncludePath) throws PageException {
088                UDFUtil.argumentCollection(values,getFunctionArguments());
089                
090                
091                // struct
092                if(this.arguments.length==2) {
093                        Key keyName = arguments[0].getName();
094                        Key valueName = arguments[1].getName();
095                        Object key = values.get(keyName,null);
096                        Object value = values.get(valueName,null);
097                        if(key==null)
098                                throw new ExpressionException("The parameter "+keyName+" to function "+getFunctionName()+" is required but was not passed in.");
099                        if(value==null)
100                                throw new ExpressionException("The parameter "+valueName+" to function "+getFunctionName()+" is required but was not passed in.");
101                        
102                        return _call(pageContext, key, value);
103                }
104                // array
105                else if(this.arguments.length==1) {
106                        Key valueName = arguments[0].getName();
107                        Object value = values.get(valueName,null);
108                        if(value==null){
109                                Key[] keys = CollectionUtil.keys(values);
110                                if(keys.length==1) {
111                                        value=values.get(keys[0]);
112                                }
113                                else throw new ExpressionException("The parameter "+valueName+" to function "+getFunctionName()+" is required but was not passed in.");
114                        }
115                        return _call(pageContext, null, value);
116                }
117
118                // never reached
119                return component;
120        }
121        
122        
123        private Object _call(PageContext pageContext, Object key, Object value) throws PageException {
124                
125                
126                Object propValue = component.getComponentScope().get(propName,null);
127                
128                // struct
129                if(this.arguments.length==2) {
130                        key=cast(pageContext,arguments[0],key,1);
131                        value=cast(pageContext,arguments[1],value,2);
132                        if(propValue==null){
133                                HashMap map=new HashMap();
134                                component.getComponentScope().setEL(propName,map);
135                                propValue=map;
136                        }       
137                        if(propValue instanceof Struct) {
138                                ((Struct)propValue).set(KeyImpl.toKey(key), value);
139                        }
140                        else if(propValue instanceof Map) {
141                                ((Map)propValue).put(key, value);
142                        }
143                }
144                else {
145                        value=cast(pageContext,arguments[0],value,1);
146                        if(propValue==null){
147                                /* jira2049
148                                PageContext pc = ThreadLocalPageContext.get();
149                                ORMSession sess = ORMUtil.getSession(pc);
150                                SessionImpl s=(SessionImpl) sess.getRawSession();
151                                propValue=new PersistentList(s);
152                                component.getComponentScope().setEL(propName,propValue);*/
153                                Array arr=new ArrayImpl();
154                                component.getComponentScope().setEL(propName,arr);
155                                propValue=arr;
156                        }       
157                        if(propValue instanceof Array) {
158                                ((Array)propValue).appendEL(value);
159                        }
160                        else if(propValue instanceof java.util.List) {
161                                ((java.util.List)propValue).add(value);
162                        }
163                }
164                return component;
165        }
166
167        @Override
168        public Object implementation(PageContext pageContext) throws Throwable {
169                return null;
170        }
171        
172        @Override
173        public Object getDefaultValue(PageContext pc, int index) throws PageException {
174                return prop.getDefault();
175        }
176        
177        @Override
178        public Object getDefaultValue(PageContext pc, int index, Object defaultValue) throws PageException {
179                return prop.getDefault();
180        }
181
182        @Override
183        public String getReturnTypeAsString() {
184                return "any";
185        }
186}