001/* 002 * Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025package Torello.Java.ReadOnly; 026 027import java.util.*; 028import java.io.*; 029 030import java.nio.charset.Charset; 031import java.util.function.BiConsumer; 032import java.util.function.BiFunction; 033import java.util.function.Function; 034 035/** 036 * A Copy of Java's {@code Properties} class; used for building a {@link ReadOnlyProperties}. 037 * Maintains <I>an internal and inaccessible {@code Properties} instance</I>. 038 * 039 * <EMBED CLASS=globalDefs DATA-JDK=Properties> 040 * <EMBED CLASS='external-html' DATA-A_AN=a DATA-FILE-ID=BUILDERS> 041 * @see ReadOnlyProperties 042 */ 043@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_BUILDER") 044public final class ROPropertiesBuilder 045 extends Properties 046 implements Cloneable, java.io.Serializable 047{ 048 // ******************************************************************************************** 049 // ******************************************************************************************** 050 // Fields & Builder 051 // ******************************************************************************************** 052 // ******************************************************************************************** 053 054 055 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 056 protected static final long serialVersionUID = 1; 057 058 // Exception messages need this 059 private static final String ROP = "Properties"; 060 061 private boolean built = false; 062 063 // Mimics the C++ Concept of "Friend Class". This badge is utilized by the "build()" method 064 // directly below this inner-class declaration. It is the only place where this declaration is 065 // actually used anywhere. This prohibits access to the constructor that is used to anybody 066 // else 067 068 static class AccessBadge { private AccessBadge() { } } 069 private static final AccessBadge friendClassBadge = new AccessBadge(); 070 071 /** 072 * Simply transfers {@code 'this'} instance' internal {@code Properties} to the 073 * {@code ReadOnlyProperties} Wrapper-Class. 074 * 075 * @return a newly constructed {@code ReadOnlyProperties} "Wrapper-Class", shielding the 076 * internal {@code 'properties'} private-field from any modification. 077 */ 078 public ReadOnlyProperties build() 079 { 080 this.built = true; 081 082 return (size() == 0) 083 ? ReadOnlyProperties.emptyROP() 084 : new ReadOnlyProperties(this, friendClassBadge); 085 } 086 087 088 // ******************************************************************************************** 089 // ******************************************************************************************** 090 // Modified Constructors 091 // ******************************************************************************************** 092 // ******************************************************************************************** 093 094 095 /** 096 * Creates an empty {@code ROPropertiesBuilder} (property list) with no default values. 097 * 098 * <BR /><BR />The initial capacity of a {@code Properties} object created 099 * with this constructor is unspecified. 100 */ 101 public ROPropertiesBuilder() 102 { super(); } 103 104 /** 105 * Creates an empty {@code ROPropertiesBuilder} (property list) with no default values, and 106 * with an initial size accommodating the specified number of elements without the need to 107 * dynamically resize. 108 * 109 * @param initialCapacity the {@code Properties} will be sized to accommodate this many 110 * elements 111 * 112 * @throws IllegalArgumentException if the initial capacity is less than zero. 113 */ 114 public ROPropertiesBuilder(int initialCapacity) 115 { super(initialCapacity); } 116 117 /** 118 * Creates an empty {@code ROPropertiesBuilder} (property list) with the specified defaults. 119 * 120 * <BR /><BR />The initial capacity of a {@code Properties} object created with this 121 * constructor is unspecified. 122 * 123 * @param defaults the defaults. 124 */ 125 public ROPropertiesBuilder(Properties defaults) 126 { super(defaults); } 127 128 129 // ******************************************************************************************** 130 // ******************************************************************************************** 131 // Original JDK Methods 132 // ******************************************************************************************** 133 // ******************************************************************************************** 134 135 136 /** 137 * Calls the {@code Hashtable} method {@code put}. Provided for parallelism with the 138 * {@code getProperty} method. Enforces use of {@code String's} for property keys and values. 139 * The value returned is the result of the {@code Hashtable} call to {@code put}. 140 * 141 * @param key the key to be placed into this property list. 142 * @param value the value corresponding to {@code key}. 143 * 144 * @return the previous value of the specified key in this property list, or {@code null} if it 145 * did not have one. 146 * 147 * @see #getProperty 148 */ 149 public synchronized Object setProperty(String key, String value) 150 { 151 if (this.built) throw new AttemptedModificationException(ROP); 152 return super.setProperty(key, value); 153 } 154 155 /** 156 * Reads a property list (key and element pairs) from the input character stream in a simple 157 * line-oriented format. 158 * 159 * <EMBED CLASS='external-html' DATA-FILE-ID=LOAD_DESC_READER> 160 * 161 * @param reader the input character stream. 162 * @throws IOException if an error occurred when reading from the input stream. 163 * @throws IllegalArgumentException if a malformed Unicode escape appears in the input. 164 * @throws NullPointerException if {@code reader} is null. 165 */ 166 public synchronized void load(Reader reader) throws IOException 167 { 168 if (this.built) throw new AttemptedModificationException(ROP); 169 super.load(reader); 170 } 171 172 /** 173 * Reads a property list (key and element pairs) from the input byte stream. The input stream 174 * is in a simple line-oriented format as specified in 175 * {@link #load(java.io.Reader) load(Reader)} and is assumed to use the ISO 8859-1 character 176 * encoding; that is each byte is one Latin1 character. Characters not in Latin1, and certain 177 * special characters, are represented in keys and elements using Unicode escapes as defined in 178 * section {@code 3.3} of <cite>The Java Language Specification</cite>. 179 * 180 * <BR /><BR />The specified stream remains open after this method returns. 181 * 182 * @param inStream the input stream. 183 * @throws IOException if an error occurred when reading from the input stream. 184 * 185 * @throws IllegalArgumentException if the input stream contains a malformed Unicode escape 186 * sequence. 187 * 188 * @throws NullPointerException if {@code inStream} is null. 189 */ 190 public synchronized void load(InputStream inStream) throws IOException 191 { 192 if (this.built) throw new AttemptedModificationException(ROP); 193 super.load(inStream); 194 } 195 196 /** 197 * Loads all of the properties represented by the XML document on the specified input stream 198 * into this properties table. 199 * 200 * <EMBED CLASS='external-html' DATA-FILE-ID=LOADXML_DESC_INPUT_STRM> 201 * 202 * @param in the input stream from which to read the XML document. 203 * 204 * @throws IOException if reading from the specified input stream results in an 205 * {@code IOException}. 206 * 207 * @throws java.io.UnsupportedEncodingException if the document's encoding declaration can be 208 * read and it specifies an encoding that is not supported 209 * 210 * @throws InvalidPropertiesFormatException Data on input stream does not constitute a valid 211 * XML document with the mandated document type. 212 * 213 * @throws NullPointerException if {@code in} is null. 214 * 215 * @spec https://www.w3.org/TR/xml Extensible Markup Language (XML) 1.0 (Fifth Edition) 216 * @see #storeToXML(OutputStream, String, String) 217 * @see <a href="http://www.w3.org/TR/REC-xml/#charencoding">Character Encoding in Entities</a> 218 */ 219 public synchronized void loadFromXML(InputStream in) 220 throws IOException, InvalidPropertiesFormatException 221 { 222 if (this.built) throw new AttemptedModificationException(ROP); 223 super.loadFromXML(in); 224 } 225 226 227 // ******************************************************************************************** 228 // ******************************************************************************************** 229 // Hashtable methods overridden and delegated to internal 'properties' instance 230 // ******************************************************************************************** 231 // ******************************************************************************************** 232 233 234 @Override 235 public synchronized Object put(Object key, Object value) 236 { 237 if (this.built) throw new AttemptedModificationException(ROP); 238 return super.put(key, value); 239 } 240 241 @Override 242 public synchronized Object remove(Object key) 243 { 244 if (this.built) throw new AttemptedModificationException(ROP); 245 return super.remove(key); 246 } 247 248 @Override 249 public synchronized void putAll(Map<?, ?> t) 250 { 251 if (this.built) throw new AttemptedModificationException(ROP); 252 super.putAll(t); 253 } 254 255 @Override 256 public synchronized void clear() 257 { 258 if (this.built) throw new AttemptedModificationException(ROP); 259 super.clear(); 260 } 261 262 @Override 263 public synchronized void replaceAll(BiFunction<Object, Object, ?> function) 264 { 265 if (this.built) throw new AttemptedModificationException(ROP); 266 super.replaceAll(function); 267 } 268 269 @Override 270 public synchronized Object putIfAbsent(Object key, Object value) 271 { 272 if (this.built) throw new AttemptedModificationException(ROP); 273 return super.putIfAbsent(key, value); 274 } 275 276 @Override 277 public synchronized boolean remove(Object key, Object value) 278 { 279 if (this.built) throw new AttemptedModificationException(ROP); 280 return super.remove(key, value); 281 } 282 283 @Override 284 public synchronized boolean replace(Object key, Object oldValue, Object newValue) 285 { 286 if (this.built) throw new AttemptedModificationException(ROP); 287 return super.replace(key, oldValue, newValue); 288 } 289 290 @Override 291 public synchronized Object replace(Object key, Object value) 292 { 293 if (this.built) throw new AttemptedModificationException(ROP); 294 return super.replace(key, value); 295 } 296 297 @Override 298 public synchronized Object computeIfAbsent 299 (Object key, Function<Object, ?> mappingFunction) 300 { 301 if (this.built) throw new AttemptedModificationException(ROP); 302 return super.computeIfAbsent(key, mappingFunction); 303 } 304 305 @Override 306 public synchronized Object computeIfPresent 307 (Object key, BiFunction<Object, Object, ?> remappingFunction) 308 { 309 if (this.built) throw new AttemptedModificationException(ROP); 310 return super.computeIfPresent(key, remappingFunction); 311 } 312 313 @Override 314 public synchronized Object compute 315 (Object key, BiFunction<Object, Object, ?> remappingFunction) 316 { 317 if (this.built) throw new AttemptedModificationException(ROP); 318 return super.compute(key, remappingFunction); 319 } 320 321 @Override 322 public synchronized Object merge 323 (Object key, Object value, BiFunction<Object, Object, ?> remappingFunction) 324 { 325 if (this.built) throw new AttemptedModificationException(ROP); 326 return super.merge(key, value, remappingFunction); 327 } 328 329 330 // ******************************************************************************************** 331 // ******************************************************************************************** 332 // Methods inherited from class java.util.Properties 333 // ******************************************************************************************** 334 // ******************************************************************************************** 335 336 337 // contains, containsKey, containsValue, elements, entrySet, forEach, get, getOrDefault, 338 // getProperty, getProperty, hashCode, isEmpty, keys, keySet, list, list, propertyNames, 339 // rehash, save, size, store, store, storeToXML, storeToXML, storeToXML, stringPropertyNames, 340 // toString, values 341 // 342 // Introspection Only: contains, containsKey, containsValue, elements, forEach, get, 343 // getOrDefault, getProperty, getProperty, hashCode, isEmpty, keys, list, 344 // list, propertyNames, rehash, save, size, store, store, storeToXML, 345 // storeToXML, storeToXML, stringPropertyNames, toString 346 // 347 // Potential Mutator: entrySet, keySet, values 348 349 /** 350 * Restricts a back-door into the underlying data-structure. 351 * <EMBED CLASS='external-html'DATA-RET_TYPE=Set DATA-FILE-ID=UNMODIFIABLE> 352 * @return a {@code java.util.Set} that cannot modify this {@code Properties-Builder} 353 */ 354 public Set<Map.Entry<Object, Object>> entrySet() 355 { return Collections.unmodifiableSet(super.entrySet()); } 356 357 Set<Map.Entry<Object, Object>> _entrySet(ReadOnlyProperties.AccessBadge friendClassBadge) 358 { return super.entrySet(); } 359 360 /** 361 * Restricts a back-door into the underlying data-structure. 362 * <EMBED CLASS='external-html'DATA-RET_TYPE=Set DATA-FILE-ID=UNMODIFIABLE> 363 * @return a {@code java.util.Set} that cannot modify this {@code Properties-Builder} 364 */ 365 public Set<Object> keySet() 366 { return Collections.unmodifiableSet(super.keySet()); } 367 368 Set<Object> _keySet(ReadOnlyProperties.AccessBadge friendClassBadge) 369 { return super.keySet(); } 370 371 /** 372 * Restricts a back-door into the underlying data-structure. 373 * <EMBED CLASS='external-html'DATA-RET_TYPE=Collection DATA-FILE-ID=UNMODIFIABLE> 374 * @return a {@code java.util.Collection} that cannot modify this {@code Properties-Builder} 375 */ 376 public Collection<Object> values() 377 { return Collections.unmodifiableCollection(super.values()); } 378 379 Collection<Object> _values(ReadOnlyProperties.AccessBadge friendClassBadge) 380 { return super.values(); } 381 382 383 // ******************************************************************************************** 384 // ******************************************************************************************** 385 // java.lang.Object 386 // ******************************************************************************************** 387 // ******************************************************************************************** 388 389 390 /** 391 * Compares the specified Object with this Builder for equality, as per the definition in the 392 * private and internal field {@code 'properties'}. 393 * 394 * @param o object to be compared for equality with this {@code ROPropertiesBuilder} instance 395 * @return true if the specified Object is equal to this Builder 396 */ 397 public boolean equals(Object o) 398 { 399 if (this == o) return true; 400 if (! (o instanceof ROPropertiesBuilder)) return false; 401 return super.equals((Properties) o); 402 } 403 404 /** 405 * Clones this instance' of {@code ROPropertiesBuilder}. 406 * 407 * <BR /><BR />The clone that's returned has had it's internal {@code 'built'} boolean-flag set 408 * {@code FALSE} 409 * 410 * @return a clone of this builder 411 */ 412 public synchronized ROPropertiesBuilder clone() 413 { return new ROPropertiesBuilder(this); } 414 415 private ROPropertiesBuilder(ROPropertiesBuilder rohtb) 416 { super(rohtb); this.built=false; } 417}