001    package railo.runtime.type;
002    
003    import java.util.Collections;
004    import java.util.HashMap;
005    import java.util.Iterator;
006    import java.util.LinkedHashMap;
007    import java.util.Map;
008    import java.util.Set;
009    
010    import org.apache.commons.collections.map.ReferenceMap;
011    
012    import railo.commons.collections.HashTable;
013    import railo.runtime.exp.ExpressionException;
014    import railo.runtime.exp.PageException;
015    import railo.runtime.op.Caster;
016    import railo.runtime.op.Duplicator;
017    import railo.runtime.op.ThreadLocalDuplication;
018    import railo.runtime.type.it.KeyIterator;
019    import railo.runtime.type.util.StructSupport;
020    
021    /**
022     * cold fusion data type struct
023     */
024    public class StructImpl extends StructSupport {
025            private static final long serialVersionUID = 1421746759512286393L;
026    
027            public static final int TYPE_SOFT=4;//FUTURE move to Struct interface
028            
029            private Map<Collection.Key,Object> map;
030            
031            /**
032             * default constructor
033             */
034            public StructImpl() {
035                    this(TYPE_REGULAR);//asx
036            }
037            
038        /**
039         * This implementation spares its clients from the unspecified, 
040         * generally chaotic ordering provided by normally Struct , 
041         * without incurring the increased cost associated with TreeMap. 
042         * It can be used to produce a copy of a map that has the same order as the original
043         * @param doubleLinked
044         */
045        public StructImpl(int type) {
046            if(type==TYPE_LINKED)           map=new LinkedHashMap<Collection.Key,Object>();
047            else if(type==TYPE_WEAKED)      map=new java.util.WeakHashMap<Collection.Key,Object>(); 
048            else if(type==TYPE_SOFT)        map=new ReferenceMap();
049            else if(type==TYPE_SYNC)        map=Collections.synchronizedMap(new HashMap<Collection.Key,Object>());
050            else                                            map=new HashMap<Collection.Key,Object>();
051        }
052        
053        private int getType(){
054            if(map instanceof LinkedHashMap) return TYPE_LINKED;
055            if(map instanceof java.util.WeakHashMap) return TYPE_WEAKED;
056            if(map instanceof ReferenceMap) return TYPE_SOFT;
057            if(map instanceof HashTable) return TYPE_SYNC;
058            return TYPE_REGULAR;
059        }
060        
061            
062            /**
063             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key, java.lang.Object)
064             */
065            public Object get(Collection.Key key, Object defaultValue) {
066                    Object rtn=map.get(key);
067                    if(rtn!=null) return rtn;
068                    return defaultValue;
069            }
070    
071            /**
072             *
073             * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key)
074             */
075            public Object get(Collection.Key key) throws PageException {//print.out("k:"+(kcount++));
076                    Object rtn=map.get(key);
077                    if(rtn!=null) return rtn;
078                    throw invalidKey(key);
079            }
080            
081            /**
082             * @see railo.runtime.type.Collection#set(railo.runtime.type.Collection.Key, java.lang.Object)
083             */
084            public Object set(Collection.Key key, Object value) throws PageException {
085                    map.put(key,value);
086                    return value;
087            }
088            
089            /**
090             * @see railo.runtime.type.Collection#setEL(railo.runtime.type.Collection.Key, java.lang.Object)
091             */
092            public Object setEL(Collection.Key key, Object value) {
093                    map.put(key,value);
094                    return value;
095            }
096    
097            /**
098             * @see railo.runtime.type.Collection#size()
099             */
100            public int size() {
101                    return map.size();
102            }
103            
104            public Collection.Key[] keys() {
105                    try     {
106                            //Collection.Key[] keys = new Collection.Key[size()];
107                            return map.keySet().toArray(new Key[map.size()]);
108                            /*Iterator<Key> it = map.keySet().iterator();
109                            int count=0;
110                            while(it.hasNext() && keys.length>count) {
111                                    keys[count++]=KeyImpl.toKey(it.next(), null);
112                            }
113                            return keys;*/
114                    }
115                    catch(Throwable t) {
116                            Map<Key, Object> old = map;
117                            try{    
118                                    map=Collections.synchronizedMap(map);
119                                    Set<Key> set = map.keySet();
120                                    Collection.Key[] keys = new Collection.Key[size()];
121                                    synchronized(map){
122                                            Iterator<Key> it = set.iterator();
123                                            int count=0;
124                                            while(it.hasNext() && keys.length>count) {
125                                                    keys[count++]=KeyImpl.toKey(it.next(), null);
126                                            }
127                                            return keys;
128                                    }
129                            }
130                            finally {
131                                    map=old;
132                            }
133                    }
134            }
135    
136            
137            /**
138             * @see railo.runtime.type.Collection#keysAsString()
139             */
140            public String[] keysAsString() {
141                    try     {
142                            //if(true)throw new RuntimeException("");
143                            String[] keys = new String[size()];
144                            Iterator<Key> it = map.keySet().iterator();
145                            int count=0;
146                            while(it.hasNext() && keys.length>count) {
147                                    keys[count++]=Caster.toString(it.next(), "");
148                            }
149                            return keys;
150                    }
151                    catch(Throwable t) {
152                            Map<Key, Object> old = map;
153                            try{    
154                                    map=Collections.synchronizedMap(map);
155                                    Object[] arr = map.keySet().toArray();
156                                    String[] keys = new String[arr.length];
157                                    for(int i=0;i<arr.length;i++){
158                                            keys[i]=Caster.toString(arr[i], "");
159                                    }       
160                                    return keys;
161                            }
162                            finally {
163                                    map=old;
164                            }
165                    }
166            }
167    
168            /**
169             * @see railo.runtime.type.Collection#remove(java.lang.String)
170             */
171            public Object remove(String key) throws PageException {
172                    Object obj= map.remove(KeyImpl.init(key));
173                    if(obj==null) throw new ExpressionException("can't remove key ["+key+"] from struct, key doesn't exists");
174                    return obj;
175            }
176    
177            /**
178             * @see railo.runtime.type.Collection#remove(railo.runtime.type.Collection.Key)
179             */
180            public Object remove(Collection.Key key) throws PageException {
181                    Object obj= map.remove(key);
182                    if(obj==null) throw new ExpressionException("can't remove key ["+key+"] from struct, key doesn't exists");
183                    return obj;
184            }
185            
186            /**
187             *
188             * @see railo.runtime.type.Collection#removeEL(railo.runtime.type.Collection.Key)
189             */
190            public Object removeEL(Collection.Key key) {
191                    return map.remove(key);
192            }
193            
194            /**
195             * @see railo.runtime.type.Collection#clear()
196             */
197            public void clear() {
198                    map.clear();
199            }
200    
201            
202            /**
203             * @see railo.runtime.type.Collection#duplicate(boolean)
204             */
205            public Collection duplicate(boolean deepCopy) {
206                    Struct sct=new StructImpl(getType());
207                    copy(this,sct,deepCopy);
208                    return sct;
209            }
210            
211            public static void copy(Struct src,Struct trg,boolean deepCopy) {
212                    ThreadLocalDuplication.set(src,trg);
213                    try{
214                            Key[] keys = src.keys();
215                            Key key;
216                            for(int i=0;i<keys.length;i++) {
217                                    key=keys[i];
218                                    if(!deepCopy) trg.setEL(key,src.get(key,null));
219                                    else trg.setEL(key,Duplicator.duplicate(src.get(key,null),deepCopy));
220                            }
221                    }
222                    finally {
223                            ThreadLocalDuplication.remove(src);
224                    }       
225            }
226            
227            
228            
229            /**
230             * @see railo.runtime.type.Collection#keyIterator()
231             */
232            public Iterator keyIterator() {
233                    return new KeyIterator(keys());
234            }
235            
236            /**
237             * @see railo.runtime.type.Iteratorable#iterator()
238             */
239            public Iterator valueIterator() {
240                    return values().iterator();
241            }
242    
243        /**
244         * @see railo.runtime.type.Collection#_contains(java.lang.String)
245         */
246        public boolean containsKey(Collection.Key key) {
247            return map.containsKey(key);
248        }
249        
250            /**
251             * @see java.util.Map#containsValue(java.lang.Object)
252             */
253            public boolean containsValue(Object value) {
254                    return map.containsValue(value);
255            }
256            
257            /**
258             * @see java.util.Map#values()
259             */
260            public java.util.Collection values() {
261                    return map.values();
262            }
263    
264            /**
265             * @return the map
266             */
267            protected Map<Collection.Key,Object> getMap() {
268                    return map;
269            }
270    }