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