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.Iterator;
023import java.util.LinkedHashMap;
024import java.util.Map;
025
026import lucee.commons.collection.MapFactory;
027import lucee.runtime.PageContext;
028import lucee.runtime.dump.DumpData;
029import lucee.runtime.dump.DumpProperties;
030import lucee.runtime.exp.ExpressionException;
031import lucee.runtime.exp.PageException;
032import lucee.runtime.op.Duplicator;
033import lucee.runtime.op.ThreadLocalDuplication;
034import lucee.runtime.type.dt.DateTime;
035import lucee.runtime.type.util.StructUtil;
036 
037/**
038 * CFML data type struct
039 */
040public final class StructImplString extends StructImpl implements Struct {
041
042        public static final int TYPE_WEAKED=0;
043        public static final int TYPE_LINKED=1;
044        public static final int TYPE_SYNC=2;
045        public static final int TYPE_REGULAR=3;
046        
047        private Map<Collection.Key,Object> map;
048        //private static  int scount=0;
049        //private static int kcount=0;
050        
051        /**
052         * default constructor
053         */
054        public StructImplString() {
055                map=new HashMap<Collection.Key, Object>();
056        }
057        
058    /**
059     * This implementation spares its clients from the unspecified, 
060     * generally chaotic ordering provided by normally Struct , 
061     * without incurring the increased cost associated with TreeMap. 
062     * It can be used to produce a copy of a map that has the same order as the original
063     * @param doubleLinked
064     */
065    public StructImplString(int type) {
066        if(type==TYPE_LINKED)           map=new LinkedHashMap<Collection.Key, Object>();
067        else if(type==TYPE_WEAKED)      map=new java.util.WeakHashMap<Collection.Key, Object>();
068        else if(type==TYPE_SYNC)        map=MapFactory.<Collection.Key,Object>getConcurrentMap();
069        else                                            map=new HashMap<Collection.Key, Object>();
070    }
071    
072        /**
073         * @see lucee.runtime.type.Collection#get(lucee.runtime.type.Collection.Key, java.lang.Object)
074         */
075        public Object get(Collection.Key key, Object defaultValue) {
076                Object rtn=map.get(key.getLowerString());
077                if(rtn!=null) return rtn;
078                return defaultValue;
079        }
080
081        /**
082         *
083         * @see lucee.runtime.type.Collection#get(lucee.runtime.type.Collection.Key)
084         */
085        public Object get(Collection.Key key) throws PageException {
086                Object rtn=map.get(key.getLowerString());
087                if(rtn!=null) return rtn;
088                throw invalidKey(key.getString());
089        }
090
091        /**
092         * @see lucee.runtime.type.Collection#set(lucee.runtime.type.Collection.Key, java.lang.Object)
093         */
094        public Object set(Collection.Key key, Object value) throws PageException {
095                map.put(key,value);
096                return value;
097        }
098        
099        /**
100         * @see lucee.runtime.type.Collection#setEL(lucee.runtime.type.Collection.Key, java.lang.Object)
101         */
102        public Object setEL(Collection.Key key, Object value) {
103        map.put(key,value);
104                return value;
105        }
106
107        /**
108         * @see lucee.runtime.type.Collection#size()
109         */
110        public int size() {
111                return map.size();
112        }
113
114        public Collection.Key[] keys() {
115                Iterator<Key> it = map.keySet().iterator();
116                Collection.Key[] keys = new Collection.Key[size()];
117                int count=0;
118                while(it.hasNext()) {
119                        keys[count++]=it.next();
120                }
121                return keys;
122        }
123
124        /**
125         * @see lucee.runtime.type.Collection#remove(lucee.runtime.type.Collection.Key)
126         */
127        public Object remove(Collection.Key key) throws PageException {
128                Object obj= map.remove(key.getLowerString());
129                if(obj==null) throw new ExpressionException("can't remove key ["+key+"] from struct, key doesn't exist");
130                return obj;
131        }
132        
133        /**
134         *
135         * @see lucee.runtime.type.Collection#removeEL(lucee.runtime.type.Collection.Key)
136         */
137        public Object removeEL(Collection.Key key) {
138                return map.remove(key.getLowerString());
139        }
140        
141        /**
142         * @see lucee.runtime.type.Collection#clear()
143         */
144        public void clear() {
145                map.clear();
146        }
147        
148        /**
149         *
150         * @see lucee.runtime.dump.Dumpable#toDumpData(lucee.runtime.PageContext, int)
151         */
152        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
153            return StructUtil.toDumpTable(this, "struct", pageContext, maxlevel, dp);
154                /*Iterator it=map.keySet().iterator();
155                
156                DumpTable table = new DumpTable("struct","#9999ff","#ccccff","#000000");
157                table.setTitle("Struct");
158                maxlevel--;
159                while(it.hasNext()) {
160                        Object key=it.next();
161                        if(DumpUtil.keyValid(dp, maxlevel,key.toString()))
162                                table.appendRow(1,new SimpleDumpData(key.toString()),DumpUtil.toDumpData(map.get(key), pageContext,maxlevel,dp));
163                }
164                return table;*/
165        }
166
167        /**
168         * throw exception for invalid key
169         * @param key Invalid key
170         * @return returns an invalid key Exception
171         */
172        protected ExpressionException invalidKey(String key) {
173                return new ExpressionException("key ["+key+"] doesn't exist in struct");
174        }
175
176        /**
177         * @see lucee.runtime.type.Collection#duplicate(boolean)
178         */
179        public Collection duplicate(boolean deepCopy) {
180                Struct sct=new StructImplString();
181                copy(this,sct,deepCopy);
182                return sct;
183        }
184        
185        public static void copy(Struct src,Struct trg,boolean deepCopy) {
186                Iterator<Entry<Key, Object>> it = src.entryIterator();
187                Entry<Key, Object> e;
188                boolean inside=ThreadLocalDuplication.set(src, trg);
189                try {
190                        while(it.hasNext()) {
191                                e = it.next();
192                                if(!deepCopy) trg.setEL(e.getKey(),e.getValue());
193                                else trg.setEL(e.getKey(),Duplicator.duplicate(e.getValue(),deepCopy));
194                        }
195                }
196                finally {
197                        if(!inside)ThreadLocalDuplication.reset();
198                }
199        }
200
201        /**
202         * @see lucee.runtime.type.Collection#keyIterator()
203         */
204        public Iterator<Collection.Key> keyIterator() {
205                return map.keySet().iterator();
206        }
207        
208        /**
209         * @see lucee.runtime.type.Iteratorable#iterator()
210         */
211        public Iterator valueIterator() {
212                return map.values().iterator();
213        }
214
215    /**
216     * @see lucee.runtime.type.Collection#_contains(java.lang.String)
217     */
218    public boolean containsKey(Collection.Key key) {
219        return map.containsKey(key.getLowerString());
220    }
221
222    /**
223     * @see lucee.runtime.op.Castable#castToString()
224     */
225    public String castToString() throws ExpressionException {
226        throw new ExpressionException("Can't cast Complex Object Type Struct to String",
227          "Use Built-In-Function \"serialize(Struct):String\" to create a String from Struct");
228    }
229
230        /**
231         * @see lucee.runtime.type.util.StructSupport#castToString(java.lang.String)
232         */
233        public String castToString(String defaultValue) {
234                return defaultValue;
235        }
236
237    /**
238     * @see lucee.runtime.op.Castable#castToBooleanValue()
239     */
240    public boolean castToBooleanValue() throws ExpressionException {
241        throw new ExpressionException("can't cast Complex Object Type Struct to a boolean value");
242    }
243    
244    /**
245     * @see lucee.runtime.op.Castable#castToBoolean(java.lang.Boolean)
246     */
247    public Boolean castToBoolean(Boolean defaultValue) {
248        return defaultValue;
249    }
250
251
252    /**
253     * @see lucee.runtime.op.Castable#castToDoubleValue()
254     */
255    public double castToDoubleValue() throws ExpressionException {
256        throw new ExpressionException("can't cast Complex Object Type Struct to a number value");
257    }
258    
259    /**
260     * @see lucee.runtime.op.Castable#castToDoubleValue(double)
261     */
262    public double castToDoubleValue(double defaultValue) {
263        return defaultValue;
264    }
265
266
267    /**
268     * @see lucee.runtime.op.Castable#castToDateTime()
269     */
270    public DateTime castToDateTime() throws ExpressionException {
271        throw new ExpressionException("can't cast Complex Object Type Struct to a Date");
272    }
273    
274    /**
275     * @see lucee.runtime.op.Castable#castToDateTime(lucee.runtime.type.dt.DateTime)
276     */
277    public DateTime castToDateTime(DateTime defaultValue) {
278        return defaultValue;
279    }
280
281        /**
282         * @see lucee.runtime.op.Castable#compare(boolean)
283         */
284        public int compareTo(boolean b) throws ExpressionException {
285                throw new ExpressionException("can't compare Complex Object Type Struct with a boolean value");
286        }
287
288        /**
289         * @see lucee.runtime.op.Castable#compareTo(lucee.runtime.type.dt.DateTime)
290         */
291        public int compareTo(DateTime dt) throws PageException {
292                throw new ExpressionException("can't compare Complex Object Type Struct with a DateTime Object");
293        }
294
295        /**
296         * @see lucee.runtime.op.Castable#compareTo(double)
297         */
298        public int compareTo(double d) throws PageException {
299                throw new ExpressionException("can't compare Complex Object Type Struct with a numeric value");
300        }
301
302        /**
303         * @see lucee.runtime.op.Castable#compareTo(java.lang.String)
304         */
305        public int compareTo(String str) throws PageException {
306                throw new ExpressionException("can't compare Complex Object Type Struct with a String");
307        }
308
309        public boolean containsValue(Object value) {
310                return map.containsValue(value);
311        }
312
313        
314        public java.util.Collection values() {
315                return map.values();
316        }
317
318}