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.util;
020
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.HashSet;
024import java.util.Iterator;
025import java.util.LinkedHashSet;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Set;
029
030import lucee.commons.collection.LinkedHashMapPro;
031import lucee.commons.collection.MapPro;
032import lucee.commons.collection.MapProWrapper;
033import lucee.commons.collection.SyncMap;
034import lucee.commons.collection.WeakHashMapPro;
035import lucee.commons.digest.HashUtil;
036import lucee.commons.lang.SizeOf;
037import lucee.commons.lang.StringUtil;
038import lucee.runtime.PageContext;
039import lucee.runtime.dump.DumpProperties;
040import lucee.runtime.dump.DumpTable;
041import lucee.runtime.dump.DumpUtil;
042import lucee.runtime.dump.SimpleDumpData;
043import lucee.runtime.exp.PageException;
044import lucee.runtime.op.Caster;
045import lucee.runtime.op.Duplicator;
046import lucee.runtime.type.Collection;
047import lucee.runtime.type.Collection.Key;
048import lucee.runtime.type.KeyImpl;
049import lucee.runtime.type.Struct;
050import lucee.runtime.type.StructImpl;
051import lucee.runtime.type.comparator.TextComparator;
052
053/**
054 * 
055 */
056public final class StructUtil {
057
058    /**
059     * copy data from source struct to target struct
060     * @param source 
061     * @param target 
062     * @param overwrite overwrite data if exist in target
063     */
064    public static void copy(Struct source, Struct target, boolean overwrite) {
065        Iterator<Entry<Key, Object>> it = source.entryIterator();
066        Entry<Key, Object> e;
067        while(it.hasNext()) {
068                e = it.next();
069            if(overwrite || !target.containsKey(e.getKey())) 
070                target.setEL(e.getKey(),e.getValue());
071        }
072    }
073
074    public static lucee.runtime.type.Collection.Key[] toCollectionKeys(String[] skeys) {
075        lucee.runtime.type.Collection.Key[] keys = new lucee.runtime.type.Collection.Key[skeys.length];
076                for(int i=0;i<keys.length;i++) {
077                        keys[i]=KeyImpl.init(skeys[i]);
078                }
079            return keys;
080    }
081    
082        /**
083         * @param sct
084         * @return
085         */
086        public static Struct duplicate(Struct sct,boolean deepCopy) {
087
088                Struct rtn=new StructImpl();
089                //lucee.runtime.type.Collection.Key[] keys=sct.keys();
090                //lucee.runtime.type.Collection.Key key;
091                Iterator<Entry<Key, Object>> it = sct.entryIterator();
092        Entry<Key, Object> e;
093        while(it.hasNext()) {
094                e=it.next();
095                        rtn.setEL(e.getKey(),Duplicator.duplicate(e.getValue(),deepCopy));
096                }
097                return rtn;
098        }
099
100        public static void putAll(Struct struct, Map map) {
101                Iterator<Entry> it = map.entrySet().iterator();
102                Map.Entry entry;
103                while(it.hasNext()) {
104                        entry= it.next();
105                        struct.setEL(KeyImpl.toKey(entry.getKey(),null), entry.getValue());
106                }
107        }
108
109        public static Set<Entry<String, Object>> entrySet(Struct sct) {
110                boolean linked=sct instanceof StructImpl && ((StructImpl)sct).getType()==Struct.TYPE_LINKED;
111                Iterator<Entry<Key, Object>> it = sct.entryIterator();
112                Entry<Key, Object> e;
113                Set<Entry<String, Object>> set=linked?new LinkedHashSet<Entry<String, Object>>():new HashSet<Entry<String, Object>>();
114                while(it.hasNext()){
115                        e= it.next();
116                        set.add(new StructMapEntry(sct,e.getKey(),e.getValue()));
117                }
118                return set;
119        }
120        
121        public static Set<String> keySet(Struct sct) {
122                boolean linked=sct instanceof StructImpl && ((StructImpl)sct).getType()==Struct.TYPE_LINKED;
123                Iterator<Key> it = sct.keyIterator();
124                Set<String> set=linked?new LinkedHashSet<String>():new HashSet<String>();
125                while(it.hasNext()){
126                        set.add(it.next().getString());
127                }
128                return set;
129        }
130
131        
132        public static DumpTable toDumpTable(Struct sct,String title,PageContext pageContext, int maxlevel, DumpProperties dp) {
133                Key[] keys = order(sct,CollectionUtil.keys(sct));
134                DumpTable table = new DumpTable("struct","#9999ff","#ccccff","#000000");// "#9999ff","#ccccff","#000000"
135
136                int maxkeys=dp.getMaxKeys();
137                if(maxkeys < sct.size()) {
138                        table.setComment("Entries: "+sct.size() + " (showing top " + maxkeys + ")");
139                }
140                else if(sct.size()>10 && dp.getMetainfo()) {
141                        table.setComment("Entries: "+sct.size());
142                }
143                
144                // advanced
145                /*Map<Key, FunctionLibFunction> members = MemberUtil.getMembers(pageContext, CFTypes.TYPE_STRUCT);
146                if(members!=null) {
147                        StringBuilder sb=new StringBuilder("This Struct is supporting the following Object functions:");
148                        Iterator<Entry<Key, FunctionLibFunction>> it = members.entrySet().iterator();
149                        Entry<Key, FunctionLibFunction> e;
150                        while(it.hasNext()){
151                                e = it.next();
152                                sb.append("\n   .")
153                                .append(e.getKey())
154                                .append('(');
155                                
156                                
157                                ArrayList<FunctionLibFunctionArg> args = e.getValue().getArg();
158                                int optionals = 0;
159                                for(int i=1;i<args.size();i++) {
160                                        FunctionLibFunctionArg arg=args.get(i);
161                                        if(i!=0)sb.append(", ");
162                                        if(!arg.getRequired()) {
163                                                sb.append("[");
164                                                optionals++;
165                                        }
166                                        sb.append(arg.getName());
167                                        sb.append(":");
168                                        sb.append(arg.getTypeAsString());
169                                }
170                                for(int i=0;i<optionals;i++)
171                                        sb.append("]");
172                                sb.append("):"+e.getValue().getReturnTypeAsString());
173                                
174                                
175                        }
176                        table.setComment(sb.toString());
177                }*/
178                
179                
180                
181                if(!StringUtil.isEmpty(title))table.setTitle(title);
182                maxlevel--;
183                int index=0;
184                for(int i=0;i<keys.length;i++) {
185                        if(DumpUtil.keyValid(dp,maxlevel,keys[i])){
186                                if(maxkeys<=index++)break;
187                                table.appendRow(1,
188                                                new SimpleDumpData(keys[i].toString()),
189                                                DumpUtil.toDumpData(sct.get(keys[i],null), 
190                                                pageContext,maxlevel,dp));
191                        }
192                }
193                return table;
194        }
195        
196        
197
198        private static Key[] order(Struct sct, Key[] keys) {
199                if(sct instanceof StructImpl && ((StructImpl)sct).getType()==Struct.TYPE_LINKED) return keys;
200                
201                TextComparator comp=new TextComparator(true,true);
202                Arrays.sort(keys,comp);
203                return keys;
204        }
205
206        /**
207         * create a value return value out of a struct
208         * @param sct
209         * @return
210         */
211        public static java.util.Collection<?> values(Struct sct) {
212                ArrayList<Object> arr = new ArrayList<Object>();
213                //Key[] keys = sct.keys();
214                Iterator<Object> it = sct.valueIterator();
215                while(it.hasNext()) {
216                        arr.add(it.next());
217                }
218                return arr;
219        }
220
221        public static Struct copyToStruct(Map map) throws PageException {
222        Struct sct = new StructImpl();
223        Iterator it=map.entrySet().iterator();
224        Map.Entry entry;
225        while(it.hasNext()) {
226            entry=(Entry) it.next();
227            sct.setEL(Caster.toString(entry.getKey()),entry.getValue());
228        }
229        return sct;
230        }
231
232        /**
233         * return the size of given struct, size of values + keys
234         * @param sct
235         * @return
236         */
237        public static long sizeOf(Struct sct) {
238                Iterator<Entry<Key, Object>> it = sct.entryIterator();
239                Entry<Key, Object> e;
240                long size = 0;
241                while(it.hasNext()) {
242                        e = it.next();
243                        size+=SizeOf.size(e.getKey());
244                        size+=SizeOf.size(e.getValue());
245                }
246                return size;
247        }
248
249        public static void setELIgnoreWhenNull(Struct sct, String key, Object value) {
250                setELIgnoreWhenNull(sct, KeyImpl.init(key), value);
251        }
252        public static void setELIgnoreWhenNull(Struct sct, Collection.Key key, Object value) {
253                if(value!=null)sct.setEL(key, value);
254        }
255
256        /**
257         * remove every entry hat has this value
258         * @param map
259         * @param obj
260         */
261        public static void removeValue(Map map, Object value) {
262                Iterator it = map.entrySet().iterator();
263                Map.Entry entry;
264                while(it.hasNext()){
265                        entry=(Entry) it.next();
266                        if(entry.getValue()==value)it.remove();
267                }
268        }
269
270        
271    public static Struct merge(Struct[] scts) { 
272                Struct sct=new StructImpl();
273                
274                for(int i=scts.length-1;i>=0;i--){
275                        Iterator<Entry<Key, Object>> it = scts[i].entryIterator();
276                        Entry<Key, Object> e;
277                        while(it.hasNext()){
278                                e = it.next();
279                                sct.setEL(e.getKey(), e.getValue());
280                        }
281                }
282                return sct;
283        }
284    
285    public static int getType(MapPro m){
286        if(m instanceof SyncMap)
287                return ((SyncMap)m).getType();
288        
289        if(m instanceof LinkedHashMapPro) return Struct.TYPE_LINKED;
290        if(m instanceof WeakHashMapPro) return Struct.TYPE_WEAKED;
291        //if(map instanceof SyncMap) return TYPE_SYNC;
292        if(m instanceof MapProWrapper) return Struct.TYPE_SOFT;
293        return Struct.TYPE_REGULAR;
294    }
295
296    /**
297     * creates a hash based on the keys of the Map/Struct
298     * @param map
299     * @return 
300     */
301        public static String keyHash(Struct sct) {
302                Key[] keys;
303                Arrays.sort(keys=CollectionUtil.keys(sct));
304                
305                StringBuilder sb=new StringBuilder();
306                for(int i=0;i<keys.length;i++){
307                        sb.append(keys[i].getString()).append(';');
308                }
309                return Long.toString(HashUtil.create64BitHash(sb),Character.MAX_RADIX);
310        }
311}