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.Iterator;
022import java.util.Set;
023
024import lucee.commons.collection.HashMapPro;
025import lucee.commons.collection.LinkedHashMapPro;
026import lucee.commons.collection.MapFactory;
027import lucee.commons.collection.MapPro;
028import lucee.commons.collection.MapProWrapper;
029import lucee.commons.collection.SyncMap;
030import lucee.commons.collection.WeakHashMapPro;
031import lucee.commons.lang.ExceptionUtil;
032import lucee.commons.lang.SerializableObject;
033import lucee.runtime.config.NullSupportHelper;
034import lucee.runtime.exp.ExpressionException;
035import lucee.runtime.exp.PageException;
036import lucee.runtime.op.Duplicator;
037import lucee.runtime.op.ThreadLocalDuplication;
038import lucee.runtime.type.it.StringIterator;
039import lucee.runtime.type.util.StructSupport;
040import lucee.runtime.type.util.StructUtil;
041
042import org.apache.commons.collections.map.ReferenceMap;
043
044/**
045 * CFML data type struct
046 */
047public class StructImpl extends StructSupport {
048        private static final long serialVersionUID = 1421746759512286393L;
049
050        public static final int TYPE_UNDEFINED = -1;// FUTURE add to interface Struct 
051
052        private MapPro<Collection.Key,Object> map;
053        
054        /**
055         * default constructor
056         */
057        public StructImpl() {
058                this(StructImpl.TYPE_UNDEFINED,HashMapPro.DEFAULT_INITIAL_CAPACITY);//asx
059        }
060        
061        /**
062     * This implementation spares its clients from the unspecified, 
063     * generally chaotic ordering provided by normally Struct , 
064     * without incurring the increased cost associated with TreeMap. 
065     * It can be used to produce a copy of a map that has the same order as the original
066     * @param type
067     */
068    public StructImpl(int type) {
069        this(type,HashMapPro.DEFAULT_INITIAL_CAPACITY);
070    }
071        
072    /**
073     * This implementation spares its clients from the unspecified, 
074     * generally chaotic ordering provided by normally Struct , 
075     * without incurring the increased cost associated with TreeMap. 
076     * It can be used to produce a copy of a map that has the same order as the original
077     * @param type
078     * @param initialCapacity initial capacity - MUST be a power of two.
079     */
080    public StructImpl(int type, int initialCapacity) {
081        if(type==TYPE_WEAKED)   map=new SyncMap<Collection.Key, Object>(new WeakHashMapPro<Collection.Key,Object>(initialCapacity));
082        else if(type==TYPE_SOFT)        map=new SyncMap<Collection.Key, Object>(new MapProWrapper<Collection.Key, Object>(new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT,initialCapacity,0.75f),new SerializableObject()));
083        else if(type==TYPE_LINKED)              map=new SyncMap<Collection.Key, Object>(new LinkedHashMapPro<Collection.Key,Object>(initialCapacity));
084        else                                            map=MapFactory.getConcurrentMap(initialCapacity);
085    }
086    
087    
088    
089    
090    public int getType(){
091        return StructUtil.getType(map);
092    }
093    
094    
095    
096    
097        
098        @Override
099        public Object get(Collection.Key key, Object defaultValue) {
100                if(NullSupportHelper.full())return map.g(key, defaultValue);
101                
102                Object rtn=map.get(key);
103                if(rtn!=null) return rtn;
104                return defaultValue;
105        }
106        
107
108        public Object g(Collection.Key key, Object defaultValue) {
109                return map.g(key, defaultValue);
110        }
111        public Object g(Collection.Key key) throws PageException {
112                return map.g(key);
113        }
114
115        @Override
116        public Object get(Collection.Key key) throws PageException {
117                if(NullSupportHelper.full()) return map.g(key);
118                
119                Object rtn=map.get(key);
120                if(rtn!=null) return rtn;
121                throw StructSupport.invalidKey(null,this,key);
122        }
123        
124        @Override
125        public Object set(Collection.Key key, Object value) throws PageException {
126                map.put(key,value);
127                return value;
128        }
129        
130        @Override
131        public Object setEL(Collection.Key key, Object value) {
132                map.put(key,value);
133                return value;
134        }
135
136        @Override
137        public int size() {
138                return map.size();
139        }
140        
141        public Collection.Key[] keys() {
142                try     {
143                        return map.keySet().toArray(new Key[map.size()]);
144                }
145                catch(Throwable t) {
146                        ExceptionUtil.rethrowIfNecessary(t);
147                        MapPro<Key, Object> old = map;
148                        try{    
149                                map = new lucee.commons.collection.SyncMap(map);
150                                Set<Key> set = map.keySet();
151                                Collection.Key[] keys = new Collection.Key[size()];
152                                synchronized(map){
153                                        Iterator<Key> it = set.iterator();
154                                        int count=0;
155                                        while(it.hasNext() && keys.length>count) {
156                                                keys[count++]=KeyImpl.toKey(it.next(), null);
157                                        }
158                                        return keys;
159                                }
160                        }
161                        finally {
162                                map=old;
163                        }
164                }
165        }
166
167        @Override
168        public Object remove(Collection.Key key) throws PageException {
169                if(NullSupportHelper.full())return map.r(key);
170                Object obj= map.remove(key);
171                if(obj==null) throw new ExpressionException("can't remove key ["+key+"] from struct, key doesn't exist");
172                return obj;
173        }
174        
175        @Override
176        public Object removeEL(Collection.Key key) {
177                return map.remove(key);
178        }
179        
180        public Object remove(Collection.Key key, Object defaultValue) {
181                if(NullSupportHelper.full())return map.r(key,defaultValue);
182                Object obj= map.remove(key);
183                if(obj==null) return defaultValue;
184                return obj;
185        }
186        
187        
188        @Override
189        public void clear() {
190                map.clear();
191        }
192
193        
194        @Override
195        public Collection duplicate(boolean deepCopy) {
196                Struct sct=new StructImpl(getType());
197                copy(this,sct,deepCopy);
198                return sct;
199        }
200        
201        public static void copy(Struct src,Struct trg,boolean deepCopy) {
202                boolean inside=ThreadLocalDuplication.set(src,trg);
203                try{
204                        Iterator<Entry<Key, Object>> it = src.entryIterator();
205                        Entry<Key, Object> e;
206                        while(it.hasNext()) {
207                                e = it.next();
208                                if(!deepCopy) trg.setEL(e.getKey(),e.getValue());
209                                else trg.setEL(e.getKey(),Duplicator.duplicate(e.getValue(),deepCopy));
210                        }
211                }
212                finally {
213                        if(!inside)ThreadLocalDuplication.reset();
214                }       
215        }
216        
217        
218        
219        @Override
220        public Iterator<Collection.Key> keyIterator() {
221                return map.keySet().iterator();
222        }
223    
224        @Override
225        public Iterator<String> keysAsStringIterator() {
226                return new StringIterator(keys());
227        }
228        
229
230        public Iterator<Entry<Key, Object>> entryIterator() {
231                return this.map.entrySet().iterator();
232        }
233        
234        @Override
235        public Iterator<Object> valueIterator() {
236                return map.values().iterator();
237        }
238
239    @Override
240    public boolean containsKey(Collection.Key key) {
241        return map.containsKey(key);
242    }
243    
244        @Override
245        public boolean containsValue(Object value) {
246                return map.containsValue(value);
247        }
248        
249        @Override
250        public java.util.Collection<Object> values() {
251                return map.values();
252        }
253        
254        @Override
255        public int hashCode() {
256                return map.hashCode();
257        }
258        
259        @Override
260        public boolean equals(Object obj) {
261                return map.equals(obj);
262        }
263}