1 /* 2 * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 package com.sun.hotspot.igv.data; 25 26 import java.io.Serializable; 27 import java.lang.ref.WeakReference; 28 import java.util.*; 29 import java.util.Map.Entry; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 import java.util.regex.PatternSyntaxException; 33 34 /** 35 * 36 * @author Thomas Wuerthinger 37 */ 38 public class Properties implements Serializable, Iterable<Property> { 39 40 public static final long serialVersionUID = 1L; 41 protected String[] map = new String[4]; 42 43 public Properties() { 44 } 45 46 @Override 47 public boolean equals(java.lang.Object o) { 48 if (!(o instanceof Properties)) { 49 return false; 50 } 51 52 Properties p = (Properties) o; 53 54 for (Property prop : this) { 55 String value = p.get(prop.getName()); 56 if (value == null || !value.equals(prop.getValue())) { 57 return false; 58 } 59 } 60 61 for (Property prop : p) { 62 String value = this.get(prop.getName()); 63 if (value == null || !value.equals(prop.getValue())) { 64 return false; 65 } 66 } 67 68 return true; 69 } 70 71 @Override 72 public int hashCode() { 73 int hash = 5; 74 75 if (map != null) { 76 for (int i = 0; i < this.map.length; i++) { 77 if (map[i] == null) { 78 i++; 79 } else { 80 hash = hash * 83 + map[i].hashCode(); 81 } 82 } 83 } 84 return hash; 85 } 86 87 public Properties(String name, String value) { 88 this(); 89 this.setProperty(name, value); 90 } 91 92 public Properties(String name, String value, String name1, String value1) { 93 this(name, value); 94 this.setProperty(name1, value1); 95 } 96 97 public Properties(String name, String value, String name1, String value1, String name2, String value2) { 98 this(name, value, name1, value1); 99 this.setProperty(name2, value2); 100 } 101 102 public Properties(Properties p) { 103 map = new String[p.map.length]; 104 System.arraycopy(p.map, 0, map, 0, p.map.length); 105 } 106 107 protected Properties(String[] map) { 108 this.map = map; 109 } 110 111 static class SharedProperties extends Properties { 112 int hashCode; 113 114 SharedProperties(String[] map) { 115 super(map); 116 this.hashCode = Arrays.hashCode(map); 117 } 118 119 @Override 120 protected void setPropertyInternal(String name, String value) { 121 throw new UnsupportedOperationException(); 122 } 123 124 @Override 125 public boolean equals(Object other) { 126 if (this == other) { 127 return true; 128 } 129 if (!(other instanceof SharedProperties)) { 130 return super.equals(other); 131 } 132 SharedProperties props2 = (SharedProperties) other; 133 return Arrays.equals(map, props2.map); 134 } 135 136 @Override 137 public int hashCode() { 138 return hashCode; 139 } 140 } 141 142 private static class PropertyCache { 143 static WeakHashMap<SharedProperties, WeakReference<SharedProperties>> immutableCache = new WeakHashMap<>(); 144 145 static synchronized SharedProperties intern(Properties properties) { 146 String[] map = properties.map; 147 SharedProperties key = new SharedProperties(map); 148 WeakReference<SharedProperties> entry = immutableCache.get(key); 149 if (entry != null) { 150 SharedProperties props = entry.get(); 151 if (props != null) { 152 return props; 153 } 154 } 155 immutableCache.put(key, new WeakReference<>(key)); 156 return key; 157 } 158 } 159 160 public static class Entity implements Provider { 161 162 private Properties properties; 163 164 public Entity() { 165 properties = new Properties(); 166 } 167 168 public Entity(Properties.Entity object) { 169 properties = new Properties(object.getProperties()); 170 } 171 172 @Override 173 public Properties getProperties() { 174 return properties; 175 } 176 177 public void internProperties() { 178 properties = PropertyCache.intern(properties); 179 } 180 } 181 182 public interface PropertyMatcher { 183 184 String getName(); 185 186 boolean match(String value); 187 } 188 189 public static class InvertPropertyMatcher implements PropertyMatcher { 190 191 private PropertyMatcher matcher; 192 193 public InvertPropertyMatcher(PropertyMatcher matcher) { 194 this.matcher = matcher; 195 } 196 197 @Override 198 public String getName() { 199 return matcher.getName(); 200 } 201 202 @Override 203 public boolean match(String p) { 204 if (p == null) { 205 return false; 206 } 207 return !matcher.match(p); 208 } 209 } 210 211 public static class StringPropertyMatcher implements PropertyMatcher { 212 213 private String name; 214 private String value; 215 216 public StringPropertyMatcher(String name, String value) { 217 if (name == null) { 218 throw new IllegalArgumentException("Property name must not be null!"); 219 } 220 if (value == null) { 221 throw new IllegalArgumentException("Property value must not be null!"); 222 } 223 this.name = name; 224 this.value = value; 225 } 226 227 @Override 228 public String getName() { 229 return name; 230 } 231 232 @Override 233 public boolean match(String p) { 234 if (p == null) { 235 throw new IllegalArgumentException("Property value must not be null!"); 236 } 237 return p.equals(value); 238 } 239 } 240 241 public static class RegexpPropertyMatcher implements PropertyMatcher { 242 243 private String name; 244 private Pattern valuePattern; 245 246 public RegexpPropertyMatcher(String name, String value) { 247 this(name, value, 0); 248 } 249 250 public RegexpPropertyMatcher(String name, String value, int flags) { 251 252 if (name == null) { 253 throw new IllegalArgumentException("Property name must not be null!"); 254 } 255 256 if (value == null) { 257 throw new IllegalArgumentException("Property value pattern must not be null!"); 258 } 259 260 this.name = name; 261 262 try { 263 valuePattern = Pattern.compile(value, flags); 264 } catch (PatternSyntaxException e) { 265 throw new IllegalArgumentException("Bad pattern: " + value); 266 } 267 } 268 269 @Override 270 public String getName() { 271 return name; 272 } 273 274 @Override 275 public boolean match(String p) { 276 if (p == null) { 277 throw new IllegalArgumentException("Property value must not be null!"); 278 } 279 Matcher m = valuePattern.matcher(p); 280 return m.matches(); 281 } 282 } 283 284 public Property selectSingle(PropertyMatcher matcher) { 285 286 final String name = matcher.getName(); 287 String value = null; 288 for (int i = 0; i < map.length; i += 2) { 289 if (map[i] != null && name.equals(map[i])) { 290 value = map[i + 1]; 291 break; 292 } 293 } 294 if (value != null && matcher.match(value)) { 295 return new Property(name, value); 296 } else { 297 return null; 298 } 299 } 300 301 public interface Provider { 302 303 public Properties getProperties(); 304 } 305 306 @Override 307 public String toString() { 308 List<String[]> pairs = new ArrayList<>(); 309 for (int i = 0; i < map.length; i += 2) { 310 if (map[i + 1] != null) { 311 pairs.add(new String[]{map[i], map[i + 1]}); 312 } 313 } 314 315 Collections.sort(pairs, new Comparator<String[]>() { 316 @Override 317 public int compare(String[] o1, String[] o2) { 318 assert o1.length == 2; 319 assert o2.length == 2; 320 return o1[0].compareTo(o2[0]); 321 } 322 }); 323 324 StringBuilder sb = new StringBuilder(); 325 sb.append("["); 326 boolean first = true; 327 for (String[] p : pairs) { 328 if (first) { 329 first = false; 330 } else { 331 sb.append(", "); 332 } 333 sb.append(p[0]).append("=").append(p[1]); 334 } 335 return sb.append("]").toString(); 336 } 337 338 public static class PropertySelector<T extends Properties.Provider> { 339 340 private Collection<T> objects; 341 342 public PropertySelector(Collection<T> objects) { 343 this.objects = objects; 344 } 345 346 public T selectSingle(PropertyMatcher matcher) { 347 348 for (T t : objects) { 349 Property p = t.getProperties().selectSingle(matcher); 350 if (p != null) { 351 return t; 352 } 353 } 354 355 return null; 356 } 357 358 public List<T> selectMultiple(PropertyMatcher matcher) { 359 List<T> result = new ArrayList<>(); 360 361 for (T t : objects) { 362 Property p = t.getProperties().selectSingle(matcher); 363 if (p != null) { 364 result.add(t); 365 } 366 } 367 368 return result; 369 } 370 } 371 372 public String get(String key) { 373 for (int i = 0; i < map.length; i += 2) { 374 if (map[i] != null && map[i].equals(key)) { 375 return map[i + 1]; 376 } 377 } 378 return null; 379 } 380 381 public void setProperty(String name, String value) { 382 setPropertyInternal(name.intern(), value != null ? value.intern() : null); 383 } 384 385 protected void setPropertyInternal(String name, String value) { 386 for (int i = 0; i < map.length; i += 2) { 387 if (map[i] != null && map[i].equals(name)) { 388 String p = map[i + 1]; 389 if (value == null) { 390 // remove this property 391 map[i] = null; 392 map[i + 1] = null; 393 } else { 394 map[i + 1] = value; 395 } 396 return; 397 } 398 } 399 if (value == null) { 400 return; 401 } 402 for (int i = 0; i < map.length; i += 2) { 403 if (map[i] == null) { 404 map[i] = name; 405 map[i + 1] = value; 406 return; 407 } 408 } 409 String[] newMap = new String[map.length + 4]; 410 System.arraycopy(map, 0, newMap, 0, map.length); 411 newMap[map.length] = name; 412 newMap[map.length + 1] = value; 413 map = newMap; 414 } 415 416 public void add(Properties properties) { 417 for (Property p : properties) { 418 // Already interned 419 setPropertyInternal(p.getName(), p.getValue()); 420 } 421 } 422 423 private class PropertiesIterator implements Iterator<Property> { 424 425 int index; 426 427 @Override 428 public boolean hasNext() { 429 while (index < map.length && map[index + 1] == null) { 430 index += 2; 431 } 432 return index < map.length; 433 } 434 435 @Override 436 public Property next() { 437 if (index < map.length) { 438 index += 2; 439 return new Property(map[index - 2], map[index - 1]); 440 } 441 return null; 442 } 443 444 @Override 445 public void remove() { 446 throw new UnsupportedOperationException("Not supported yet."); 447 } 448 } 449 450 @Override 451 public Iterator<Property> iterator() { 452 return new PropertiesIterator(); 453 } 454 }