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.wrap;
020
021import java.util.Iterator;
022import java.util.Map;
023
024import lucee.commons.lang.StringUtil;
025import lucee.runtime.PageContext;
026import lucee.runtime.dump.DumpData;
027import lucee.runtime.dump.DumpProperties;
028import lucee.runtime.dump.DumpUtil;
029import lucee.runtime.exp.ExpressionException;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.op.Caster;
032import lucee.runtime.op.Duplicator;
033import lucee.runtime.type.Collection;
034import lucee.runtime.type.KeyImpl;
035import lucee.runtime.type.Struct;
036import lucee.runtime.type.dt.DateTime;
037import lucee.runtime.type.it.EntryIterator;
038import lucee.runtime.type.it.KeyIterator;
039import lucee.runtime.type.it.StringIterator;
040import lucee.runtime.type.it.ValueIterator;
041import lucee.runtime.type.util.StructSupport;
042
043/**
044 * 
045 */
046public class MapAsStruct extends StructSupport implements Struct {
047    
048    Map map;
049        private boolean caseSensitive;
050
051    /**
052     * constructor of the class
053     * @param map
054     * @param caseSensitive 
055     */
056    private MapAsStruct(Map map, boolean caseSensitive) {
057        this.map=map;
058        this.caseSensitive=caseSensitive;
059    }
060    
061
062    public static Struct toStruct(Map map) {
063        return toStruct(map,false);
064        }
065
066    public static Struct toStruct(Map map, boolean caseSensitive) {
067        if(map instanceof Struct) return ((Struct)map);
068                return new MapAsStruct(map,caseSensitive);
069        }
070    
071    @Override
072    public int size() {
073       return map.size();
074    }
075
076    @Override
077    public synchronized Collection.Key[] keys() {
078        int len=size();
079        Collection.Key[] k=new Collection.Key[len];
080        Iterator it = map.keySet().iterator();
081        int count=0;
082        while(it.hasNext()) {
083            k[count++]=KeyImpl.init(StringUtil.toStringNative(it.next(),""));
084        }
085        return k;
086    }
087
088        public static String getCaseSensitiveKey(Map map,String key) {
089        Iterator it = map.keySet().iterator();
090                String strKey;
091        while(it.hasNext()) {
092                strKey=Caster.toString(it.next(),"");
093                if(strKey.equalsIgnoreCase(key)) return strKey;
094        }
095                return null;
096        }
097
098    @Override
099    public synchronized Object remove(Collection.Key key) throws ExpressionException {
100        Object obj= map.remove(key.getString());
101        if(obj==null) {
102                if(map.containsKey(key.getString())) return null;
103                if(!caseSensitive){
104                        String csKey = getCaseSensitiveKey(map,key.getString());
105                        if(csKey!=null)obj= map.remove(csKey);
106                        if(obj!=null)return obj;
107                }
108                throw new ExpressionException("can't remove key ["+key.getString()+"] from map, key doesn't exist");
109        }
110        return obj;
111    }
112    
113    @Override
114    public synchronized Object removeEL(Collection.Key key) {
115        Object obj= map.remove(key.getString());
116        if(!caseSensitive && obj==null) {
117                String csKey = getCaseSensitiveKey(map,key.getString());
118                if(csKey!=null)obj= map.remove(csKey);
119        }
120        return obj;
121    }
122
123    @Override
124    public synchronized void clear() {
125        map.clear();
126    }
127
128    @Override
129    public synchronized Object get(Collection.Key key) throws ExpressionException {
130        Object o=map.get(key.getString());
131        if(o==null) {
132                if(map.containsKey(key.getString())) return null;
133                if(!caseSensitive){
134                        String csKey = getCaseSensitiveKey(map,key.getString());
135                        if(csKey!=null)o= map.get(csKey);
136                        if(o!=null || map.containsKey(csKey)) return o;
137                }
138                throw new ExpressionException("key "+key.getString()+" doesn't exist in "+Caster.toClassName(map));
139        }
140        return o;
141    }
142
143    @Override
144    public synchronized Object get(Collection.Key key, Object defaultValue) {
145        Object obj=map.get(key.getString());
146        if(obj==null) {
147                if(map.containsKey(key.getString())) return null;
148                if(!caseSensitive){
149                        String csKey = getCaseSensitiveKey(map,key.getString());
150                        if(csKey!=null)obj= map.get(csKey);
151                        if(obj!=null || map.containsKey(csKey)) return obj; 
152                }
153            return defaultValue;
154        }
155        return obj;
156    }
157
158    @Override
159    public synchronized Object set(Collection.Key key, Object value) throws PageException {
160        return map.put(key.getString(),value);
161    }
162
163    @Override
164    public synchronized Object setEL(Collection.Key key, Object value) {
165        return map.put(key.getString(),value);
166    }
167    
168    @Override
169        public synchronized Iterator<Collection.Key> keyIterator() {
170        return new KeyIterator(keys());
171    }
172
173        @Override
174        public Iterator<String> keysAsStringIterator() {
175                return new StringIterator(keys());
176        }
177        
178        @Override
179        public Iterator<Entry<Key, Object>> entryIterator() {
180                return new EntryIterator(this,keys());
181        }
182        
183        @Override
184        public Iterator<Object> valueIterator() {
185                return new ValueIterator(this,keys());
186        }
187        
188        
189    
190    @Override
191        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
192            return DumpUtil.toDumpData(map, pageContext,maxlevel,dp); 
193    }
194   
195    @Override
196    public synchronized Collection duplicate(boolean deepCopy) {
197        return new MapAsStruct(Duplicator.duplicateMap(map,deepCopy),caseSensitive);
198    }
199
200        
201
202    @Override
203    public boolean containsKey(Collection.Key key) {
204        
205        //return map.containsKey(key.getString());
206        
207        boolean contains = map.containsKey(key.getString());
208        if(contains) return true; 
209        if(!caseSensitive)return map.containsKey(getCaseSensitiveKey(map,key.getString()));
210        return false;
211    }
212
213    @Override
214    public String castToString() throws ExpressionException {
215        throw new ExpressionException("Can't cast Complex Object Type Struct ["+getClass().getName()+"] to String",
216          "Use Built-In-Function \"serialize(Struct):String\" to create a String from Struct");
217    }
218        @Override
219        public String castToString(String defaultValue) {
220                return defaultValue;
221        }
222
223
224    @Override
225    public boolean castToBooleanValue() throws ExpressionException {
226        throw new ExpressionException("Can't cast Complex Object Type Struct ["+getClass().getName()+"] to a boolean value");
227    }
228    
229    @Override
230    public Boolean castToBoolean(Boolean defaultValue) {
231        return defaultValue;
232    }
233
234
235    @Override
236    public double castToDoubleValue() throws ExpressionException {
237        throw new ExpressionException("Can't cast Complex Object Type Struct ["+getClass().getName()+"] to a number value");
238    }
239    
240    @Override
241    public double castToDoubleValue(double defaultValue) {
242        return defaultValue;
243    }
244
245
246    @Override
247    public DateTime castToDateTime() throws ExpressionException {
248        throw new ExpressionException("Can't cast Complex Object Type Struct ["+getClass().getName()+"] to a Date");
249    }
250    
251    @Override
252    public DateTime castToDateTime(DateTime defaultValue) {
253        return defaultValue;
254    }
255
256        @Override
257        public int compareTo(boolean b) throws ExpressionException {
258                throw new ExpressionException("can't compare Complex Object Type Struct ["+getClass().getName()+"] with a boolean value");
259        }
260
261        @Override
262        public int compareTo(DateTime dt) throws PageException {
263                throw new ExpressionException("can't compare Complex Object Type Struct ["+getClass().getName()+"] with a DateTime Object");
264        }
265
266        @Override
267        public int compareTo(double d) throws PageException {
268                throw new ExpressionException("can't compare Complex Object Type Struct ["+getClass().getName()+"] with a numeric value");
269        }
270
271        @Override
272        public int compareTo(String str) throws PageException {
273                throw new ExpressionException("can't compare Complex Object Type Struct ["+getClass().getName()+"] with a String");
274        }
275
276        @Override
277        public boolean containsValue(Object value) {
278                return map.containsValue(value);
279        }
280
281        @Override
282        public java.util.Collection values() {
283                return map.values();
284        }
285}