/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.runtime.type.util; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import lucee.commons.collection.LinkedHashMapPro; import lucee.commons.collection.MapPro; import lucee.commons.collection.MapProWrapper; import lucee.commons.collection.SyncMap; import lucee.commons.collection.WeakHashMapPro; import lucee.commons.digest.HashUtil; import lucee.commons.lang.SizeOf; import lucee.commons.lang.StringUtil; import lucee.runtime.PageContext; import lucee.runtime.dump.DumpProperties; import lucee.runtime.dump.DumpTable; import lucee.runtime.dump.DumpUtil; import lucee.runtime.dump.SimpleDumpData; import lucee.runtime.exp.PageException; import lucee.runtime.op.Caster; import lucee.runtime.op.Duplicator; import lucee.runtime.type.Collection; import lucee.runtime.type.Collection.Key; import lucee.runtime.type.KeyImpl; import lucee.runtime.type.Struct; import lucee.runtime.type.StructImpl; import lucee.runtime.type.comparator.TextComparator; /** * */ public final class StructUtil { /** * copy data from source struct to target struct * @param source * @param target * @param overwrite overwrite data if exist in target */ public static void copy(Struct source, Struct target, boolean overwrite) { Iterator<Entry<Key, Object>> it = source.entryIterator(); Entry<Key, Object> e; while(it.hasNext()) { e = it.next(); if(overwrite || !target.containsKey(e.getKey())) target.setEL(e.getKey(),e.getValue()); } } public static lucee.runtime.type.Collection.Key[] toCollectionKeys(String[] skeys) { lucee.runtime.type.Collection.Key[] keys = new lucee.runtime.type.Collection.Key[skeys.length]; for(int i=0;i<keys.length;i++) { keys[i]=KeyImpl.init(skeys[i]); } return keys; } /** * @param sct * @return */ public static Struct duplicate(Struct sct,boolean deepCopy) { Struct rtn=new StructImpl(); //lucee.runtime.type.Collection.Key[] keys=sct.keys(); //lucee.runtime.type.Collection.Key key; Iterator<Entry<Key, Object>> it = sct.entryIterator(); Entry<Key, Object> e; while(it.hasNext()) { e=it.next(); rtn.setEL(e.getKey(),Duplicator.duplicate(e.getValue(),deepCopy)); } return rtn; } public static void putAll(Struct struct, Map map) { Iterator<Entry> it = map.entrySet().iterator(); Map.Entry entry; while(it.hasNext()) { entry= it.next(); struct.setEL(KeyImpl.toKey(entry.getKey(),null), entry.getValue()); } } public static Set<Entry<String, Object>> entrySet(Struct sct) { boolean linked=sct instanceof StructImpl && ((StructImpl)sct).getType()==Struct.TYPE_LINKED; Iterator<Entry<Key, Object>> it = sct.entryIterator(); Entry<Key, Object> e; Set<Entry<String, Object>> set=linked?new LinkedHashSet<Entry<String,Object>>() : new HashSet<Entry<String, Object>>(); while(it.hasNext()){ e= it.next(); set.add(new StructMapEntry(sct,e.getKey(),e.getValue())); } return set; } public static Set<String> keySet(Struct sct) { boolean linked=sct instanceof StructSupport && ((StructSupport)sct).getType()==Struct.TYPE_LINKED; Iterator<Key> it = sct.keyIterator(); Set<String> set=linked?new LinkedHashSet<String>():new HashSet<String>(); while(it.hasNext()){ set.add(it.next().getString()); } return set; } public static DumpTable toDumpTable(Struct sct,String title,PageContext pageContext, int maxlevel, DumpProperties dp) { Key[] keys = CollectionUtil.keys(sct); if(!(sct instanceof StructSupport) || ((StructSupport)sct).getType()!=Struct.TYPE_LINKED) keys = order(sct,CollectionUtil.keys(sct)); DumpTable table = new DumpTable("struct","#9999ff","#ccccff","#000000");// "#9999ff","#ccccff","#000000" int maxkeys=dp.getMaxKeys(); if(maxkeys < sct.size()) { table.setComment("Entries: "+sct.size() + " (showing top " + maxkeys + ")"); } else if(sct.size()>10 && dp.getMetainfo()) { table.setComment("Entries: "+sct.size()); } // advanced /*Map<Key, FunctionLibFunction> members = MemberUtil.getMembers(pageContext, CFTypes.TYPE_STRUCT); if(members!=null) { StringBuilder sb=new StringBuilder("This Struct is supporting the following Object functions:"); Iterator<Entry<Key, FunctionLibFunction>> it = members.entrySet().iterator(); Entry<Key, FunctionLibFunction> e; while(it.hasNext()){ e = it.next(); sb.append("\n .") .append(e.getKey()) .append('('); ArrayList<FunctionLibFunctionArg> args = e.getValue().getArg(); int optionals = 0; for(int i=1;i<args.size();i++) { FunctionLibFunctionArg arg=args.get(i); if(i!=0)sb.append(", "); if(!arg.getRequired()) { sb.append("["); optionals++; } sb.append(arg.getName()); sb.append(":"); sb.append(arg.getTypeAsString()); } for(int i=0;i<optionals;i++) sb.append("]"); sb.append("):"+e.getValue().getReturnTypeAsString()); } table.setComment(sb.toString()); }*/ if(!StringUtil.isEmpty(title))table.setTitle(title); maxlevel--; int index=0; for(int i=0;i<keys.length;i++) { if(DumpUtil.keyValid(dp,maxlevel,keys[i])){ if(maxkeys<=index++)break; table.appendRow(1, new SimpleDumpData(keys[i].toString()), DumpUtil.toDumpData(sct.get(keys[i],null), pageContext,maxlevel,dp)); } } return table; } private static Key[] order(Struct sct, Key[] keys) { if(sct instanceof StructImpl && ((StructImpl)sct).getType()==Struct.TYPE_LINKED) return keys; TextComparator comp=new TextComparator(true,true); Arrays.sort(keys,comp); return keys; } /** * create a value return value out of a struct * @param sct * @return */ public static java.util.Collection<?> values(Struct sct) { ArrayList<Object> arr = new ArrayList<Object>(); //Key[] keys = sct.keys(); Iterator<Object> it = sct.valueIterator(); while(it.hasNext()) { arr.add(it.next()); } return arr; } public static Struct copyToStruct(Map map) throws PageException { Struct sct = new StructImpl(); Iterator it=map.entrySet().iterator(); Map.Entry entry; while(it.hasNext()) { entry=(Entry) it.next(); sct.setEL(Caster.toString(entry.getKey()),entry.getValue()); } return sct; } /** * return the size of given struct, size of values + keys * @param sct * @return */ public static long sizeOf(Struct sct) { Iterator<Entry<Key, Object>> it = sct.entryIterator(); Entry<Key, Object> e; long size = 0; while(it.hasNext()) { e = it.next(); size+=SizeOf.size(e.getKey()); size+=SizeOf.size(e.getValue()); } return size; } public static void setELIgnoreWhenNull(Struct sct, String key, Object value) { setELIgnoreWhenNull(sct, KeyImpl.init(key), value); } public static void setELIgnoreWhenNull(Struct sct, Collection.Key key, Object value) { if(value!=null)sct.setEL(key, value); } /** * remove every entry hat has this value * @param map * @param obj */ public static void removeValue(Map map, Object value) { Iterator it = map.entrySet().iterator(); Map.Entry entry; while(it.hasNext()){ entry=(Entry) it.next(); if(entry.getValue()==value)it.remove(); } } public static Struct merge(Struct[] scts) { Struct sct=new StructImpl(); for(int i=scts.length-1;i>=0;i--){ Iterator<Entry<Key, Object>> it = scts[i].entryIterator(); Entry<Key, Object> e; while(it.hasNext()){ e = it.next(); sct.setEL(e.getKey(), e.getValue()); } } return sct; } public static int getType(MapPro m){ if(m instanceof SyncMap) return ((SyncMap)m).getType(); if(m instanceof LinkedHashMapPro) return Struct.TYPE_LINKED; if(m instanceof WeakHashMapPro) return Struct.TYPE_WEAKED; //if(map instanceof SyncMap) return TYPE_SYNC; if(m instanceof MapProWrapper) return Struct.TYPE_SOFT; return Struct.TYPE_REGULAR; } public static int getType(Map m){ if(m instanceof MapPro) return getType(m); if(m instanceof LinkedHashMap) return Struct.TYPE_LINKED; if(m instanceof WeakHashMap) return Struct.TYPE_WEAKED; if(m instanceof ConcurrentHashMap) return Struct.TYPE_SYNC; return Struct.TYPE_REGULAR; } public static String toType(int type, String defaultValue){ if(Struct.TYPE_LINKED==type) return "ordered"; if(Struct.TYPE_WEAKED==type) return "weak"; if(Struct.TYPE_REGULAR==type) return "regular"; if(Struct.TYPE_REGULAR==type) return "regular"; if(Struct.TYPE_SOFT==type) return "soft"; if(Struct.TYPE_SYNC==type) return "synchronized"; if(Struct.TYPE_UNDEFINED==type) return "undefined"; return defaultValue; } /** * creates a hash based on the keys of the Map/Struct * @param map * @return */ public static String keyHash(Struct sct) { Key[] keys; Arrays.sort(keys=CollectionUtil.keys(sct)); StringBuilder sb=new StringBuilder(); for(int i=0;i<keys.length;i++){ sb.append(keys[i].getString()).append(';'); } return Long.toString(HashUtil.create64BitHash(sb),Character.MAX_RADIX); } }