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.scope;
020
021import java.io.UnsupportedEncodingException;
022
023import lucee.commons.lang.StringList;
024import lucee.commons.lang.StringUtil;
025import lucee.commons.net.URLDecoder;
026import lucee.commons.net.URLItem;
027import lucee.runtime.PageContext;
028import lucee.runtime.dump.DumpData;
029import lucee.runtime.dump.DumpProperties;
030import lucee.runtime.exp.ExpressionException;
031import lucee.runtime.op.Caster;
032import lucee.runtime.security.ScriptProtect;
033import lucee.runtime.type.Array;
034import lucee.runtime.type.ArrayImpl;
035import lucee.runtime.type.CastableStruct;
036import lucee.runtime.type.Collection;
037import lucee.runtime.type.KeyImpl;
038import lucee.runtime.type.Sizeable;
039import lucee.runtime.type.Struct;
040import lucee.runtime.type.StructImpl;
041import lucee.runtime.type.util.ListUtil;
042import lucee.runtime.type.util.StructUtil;
043
044
045
046/**
047 * Simple standart implementation of a Scope, for standart use.
048 */
049public abstract class ScopeSupport extends StructImpl implements Scope,Sizeable {
050        
051        private static final long serialVersionUID = -4185219623238374574L;
052        
053        private String name;
054    private String dspName;
055        private static int _id=0;
056        private int id=0;
057    private static final byte[] EMPTY="".getBytes();
058        
059        
060        /**
061         * Field <code>isInit</code>
062         */
063        protected boolean isInit;
064    private int type;
065
066    /**
067     * constructor for the Simple class
068     * @param name name of the scope
069     * @param type scope type (SCOPE_APPLICATION,SCOPE_COOKIE use)
070     */
071    public ScopeSupport(boolean sync,String name, int type) {
072        super(sync?StructImpl.TYPE_SYNC:StructImpl.TYPE_UNDEFINED);
073        this.name=name;
074        this.type=type;
075        
076        id=++_id;
077    }
078    /**
079     * constructor for ScopeSupport
080     * @param name name of the scope
081     * @param type scope type (SCOPE_APPLICATION,SCOPE_COOKIE use)
082     * @param doubleLinked mean that the struct has predictable iteration order this make the input order fix
083     */
084    public ScopeSupport(String name, int type, int mapType) {
085        super(mapType);
086        this.name=name;
087        this.type=type;
088        
089        id=++_id;
090    }
091        
092    @Override
093
094        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
095                return toDumpData(pageContext, maxlevel, dp, this, dspName);
096        }
097        
098        public static DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp, Struct sct,String dspName) {
099                if(StringUtil.isEmpty(dspName))dspName="Scope";
100                
101                return StructUtil.toDumpTable(sct, dspName, pageContext, maxlevel, dp);
102                
103        }
104                
105        protected ExpressionException invalidKey(String key) {
106                return new ExpressionException("variable ["+key+"] doesn't exist in "+StringUtil.ucFirst(name)+" Scope (keys:"+ListUtil.arrayToList(keys(), ",")+")");
107        }
108
109    /**
110     * write parameter defined in a query string (name1=value1&name2=value2) to the scope
111     * @param qs Query String
112     * @return parsed name value pair
113     */
114        protected static URLItem[] setFromQueryString(String str) {
115                return setFrom___(str, '&');
116        }
117
118        protected static URLItem[] setFromTextPlain(String str) {
119                return setFrom___(str, '\n');
120        }
121        protected static URLItem[] setFrom___(String tp,char delimiter) {
122        if(tp==null) return new URLItem[0];
123        Array arr=ListUtil.listToArrayRemoveEmpty(tp,delimiter);
124        URLItem[] pairs=new URLItem[arr.size()];
125        
126        //Array item;
127        int index;
128        String name;
129        
130        for(int i=1;i<=pairs.length;i++) {
131            name=Caster.toString(arr.get(i,""),"");
132            //if(name.length()==0) continue;
133            
134            index=name.indexOf('=');
135            if(index!=-1) pairs[i-1]=new URLItem(name.substring(0,index),name.substring(index+1),true);
136            else pairs[i-1]=new URLItem(name,"",true);
137          
138        }
139        return pairs;
140    }
141
142    protected static byte[] getBytes(String str) {
143        return str.getBytes();
144    }
145    protected static byte[] getBytes(String str,String encoding) {
146        try {
147            return str.getBytes(encoding);
148        } catch (UnsupportedEncodingException e) {
149            return EMPTY;
150        }
151    }
152    
153    protected void fillDecodedEL(URLItem[] raw, String encoding, boolean scriptProteced, boolean sameAsArray) {
154        try {
155                        fillDecoded(raw, encoding,scriptProteced,sameAsArray);
156                } catch (UnsupportedEncodingException e) {
157                        try {
158                                fillDecoded(raw, "iso-8859-1",scriptProteced,sameAsArray);
159                        } catch (UnsupportedEncodingException e1) {}
160                }
161    }
162        
163    
164    /**
165     * fill th data from given strut and decode it
166     * @param raw
167     * @param encoding
168     * @throws UnsupportedEncodingException
169     */
170    protected void fillDecoded(URLItem[] raw, String encoding, boolean scriptProteced, boolean sameAsArray) throws UnsupportedEncodingException {
171        clear();
172        String name,value;
173        //Object curr;
174        for(int i=0;i<raw.length;i++) {
175            name=raw[i].getName();
176            value=raw[i].getValue();
177            if(raw[i].isUrlEncoded()) {
178                name=URLDecoder.decode(name,encoding,true);
179                value=URLDecoder.decode(value,encoding,true);
180            }
181            // MUST valueStruct
182            if(name.indexOf('.')!=-1) {
183                StringList list=ListUtil.listToStringListRemoveEmpty(name,'.');
184                Struct parent=this;
185                while(list.hasNextNext()) {
186                    parent=_fill(parent,list.next(),new CastableStruct(),false,scriptProteced,sameAsArray);
187                }
188                _fill(parent,list.next(),value,true,scriptProteced,sameAsArray);
189            } 
190            //else 
191                _fill(this,name,value,true,scriptProteced,sameAsArray);
192        }
193    }
194    
195    private Struct _fill(Struct parent, String name, Object value, boolean isLast, boolean scriptProteced, boolean sameAsArray) {
196        Object curr;
197        boolean isArrayDef=sameAsArray;
198        Collection.Key key=KeyImpl.init(name);
199        
200        // script protect
201        if(scriptProteced && value instanceof String) {
202                value=ScriptProtect.translate((String)value);
203        }
204        
205        if(name.length() >2 && name.endsWith("[]")) {
206            isArrayDef=true;
207            name=name.substring(0,name.length()-2);
208            key=KeyImpl.getInstance(name);
209            curr=parent.get(key,null);                
210        }
211        else {
212            curr=parent.get(key,null);
213        }
214        
215        if(curr==null) {
216                if(isArrayDef) {
217                        Array arr = new ArrayImpl();
218                        arr.appendEL(value);
219                        parent.setEL(key,arr);
220                }
221            else parent.setEL(key,value); 
222        }
223        else if(curr instanceof Array){
224            ((Array) curr).appendEL(value);
225        }
226        else if(curr instanceof CastableStruct){
227                if(isLast) ((CastableStruct)curr).setValue(value);
228            else return (Struct) curr;
229                
230        }
231        else if(curr instanceof Struct){
232            if(isLast) parent.setEL(key,value);
233            else return (Struct) curr;
234        }
235        else if(curr instanceof String){
236            if(isArrayDef) {
237                Array arr = new ArrayImpl();
238                arr.appendEL(curr);
239                arr.appendEL(value);
240                parent.setEL(key,arr);
241            }
242            else if(value instanceof Struct) {
243                parent.setEL(key,value);
244            }
245            else {
246                if(!StringUtil.isEmpty(value)) {
247                        String existing=Caster.toString(curr,"");
248                        if(StringUtil.isEmpty(existing))
249                                parent.setEL(key,value);
250                        else 
251                                parent.setEL(key,Caster.toString(curr,"")+','+value);
252                }
253            }
254        }
255        if(!isLast) {
256            return (Struct)value;
257        }
258        return null;
259    }
260    
261    /*
262    private String decode(Object value,String encoding) throws UnsupportedEncodingException {
263        return URLDecoder.decode(new String(Caster.toString(value,"").getBytes("ISO-8859-1"),encoding),encoding);
264    }*/
265    
266    @Override
267        public boolean isInitalized() {
268                return isInit;
269        }
270
271        @Override
272        public void initialize(PageContext pc) {
273        isInit=true;
274        }
275
276        @Override
277        public void release() {
278                clear();
279                isInit=false;
280        }
281        
282        @Override
283        public void release(PageContext pc) {
284                clear();
285                isInit=false;
286        }
287
288
289    /**
290     * @return Returns the id.
291     */
292    public int _getId() {
293        return id;
294    }
295    
296    /**
297     * display name for dump
298     * @param dspName
299     */
300    protected void setDisplayName(String dspName) {
301        this.dspName=dspName;
302    }
303    
304    @Override
305    public int getType() {
306        return type;
307    }
308    
309    @Override
310    public String getTypeAsString() {
311        return name;
312    }
313        public long sizeOf() {
314                return StructUtil.sizeOf(this);
315        }
316    
317}