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}