001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2005 Cougaar Software, Inc
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: * Created on September 12, 2001, 10:55 AM
026: */
027:
028: package org.cougaar.bootstrap;
029:
030: import java.io.FileReader;
031: import java.io.InputStream;
032: import java.io.IOException;
033: import java.io.LineNumberReader;
034: import java.io.OutputStream;
035: import java.io.PrintStream;
036: import java.io.PrintWriter;
037: import java.util.ArrayList;
038: import java.util.Collection;
039: import java.util.Enumeration;
040: import java.util.HashMap;
041: import java.util.HashSet;
042: import java.util.Iterator;
043: import java.util.List;
044: import java.util.Map;
045: import java.util.Properties;
046: import java.util.Set;
047: import java.util.regex.Matcher;
048: import java.util.regex.Pattern;
049: import java.security.PrivilegedAction;
050: import java.security.AccessController;
051:
052: /**
053: * Utility class to access system properties.
054: * <p>
055: *
056: * @property org.cougaar.properties.expand=true
057: * Set to true to enable system property expansion.
058: * Set to false to disable property expansion.
059: */
060: public class SystemProperties {
061:
062: /**
063: * A Pattern used to expand System properties.
064: * <p>
065: * When the value of a system property contains a sub-string with the
066: * following format:
067: * <pre>${property_name}</pre>
068: * Then that sub-string will be expanded to the value of the property
069: * named "property_name".
070: */
071: private static final Pattern expandPattern = Pattern
072: .compile("\\$\\{([^\\$\\{\\}]*)\\}");
073:
074: /**
075: * A Pattern used to prevent system property expansion.
076: * <p>
077: * When property expansion is enabled, but the user wants to disable
078: * property expansion locally, the following pattern should be used:
079: * <pre>\$\{property_name\}</pre>
080: */
081: private static final Pattern escapePattern = Pattern
082: .compile("\\\\\\$\\\\\\{([^\\$\\{\\}]*)\\\\\\}");
083:
084: private static Map env = null;
085:
086: // we don't need to synchronized because this is called very early on,
087: // before we're multithreaded.
088: private static Properties sysProps = null;
089: private static boolean sysPropsFinalized = false;
090:
091: /**
092: * Replace the System properties with alternate properties.
093: * <p>
094: * This method allow us to:<ol>
095: * <li>Define properties in secure environments where System properties
096: * access is restricted, e.g. Applets.</li>
097: * <li>Define properties when multiple Nodes will run in the same JVM.
098: * A possible solution in that environment is to use Classloader-scoped
099: * properties, as in<pre>
100: * org.apache.commons.discovery.tools.ManagedProperties
101: * </pre></li>
102: * </ol>
103: * @see #finalizeProperties
104: */
105: public static void overrideProperties(Properties props) {
106: if (sysPropsFinalized) {
107: throw new RuntimeException("Already finalized properties");
108: }
109: sysProps = props;
110: }
111:
112: /**
113: * Prevent future calls to {@link #overrideProperties} -- this method
114: * is called early on by the Node.
115: */
116: public static boolean finalizeProperties() {
117: boolean ret = sysPropsFinalized;
118: if (!ret) {
119: sysPropsFinalized = true;
120: }
121: return ret;
122: }
123:
124: private static String _getProperty(String name, String deflt) {
125: Properties p = sysProps;
126: return (p == null ? System.getProperty(name, deflt) : p
127: .getProperty(name, deflt));
128: }
129:
130: private static Object _setProperty(String name, String value) {
131: Properties p = sysProps;
132: return (p == null ? System.setProperty(name, value) : p
133: .setProperty(name, value));
134: }
135:
136: private static Enumeration _getPropertyNames() {
137: Properties p = sysProps;
138: if (p == null)
139: p = System.getProperties();
140: return p.propertyNames();
141: }
142:
143: /**
144: * System property getter methods.
145: * <p>
146: * @see #overrideProperties notes on why these methods should be used instead
147: * of "System.getProperty(..)" / "Boolean.getBoolean(..)" / etc.
148: */
149: public static String getProperty(String name, String deflt) {
150: return _getProperty(name, deflt);
151: }
152:
153: public static String getProperty(String name) {
154: return getProperty(name, null);
155: }
156:
157: public static boolean getBoolean(String name) {
158: return getBoolean(name, false);
159: }
160:
161: public static boolean getBoolean(String name, boolean deflt) {
162: String s = getProperty(name);
163: return (s == null ? deflt : s.equalsIgnoreCase("true"));
164: }
165:
166: public static int getInt(String name, int deflt) {
167: String s = getProperty(name);
168: return (s == null ? deflt : Integer.parseInt(s));
169: }
170:
171: public static int getInt(String name, int deflt,
172: boolean catchFormatError) {
173: return (catchFormatError ? getInt(name, deflt, deflt) : getInt(
174: name, deflt));
175: }
176:
177: public static int getInt(String name, int deflt, int parseDeflt) {
178: try {
179: return getInt(name, deflt);
180: } catch (NumberFormatException nfe) {
181: return parseDeflt;
182: }
183: }
184:
185: public static long getLong(String name, long deflt) {
186: String s = getProperty(name);
187: return (s == null ? deflt : Long.parseLong(s));
188: }
189:
190: public static long getLong(String name, long deflt,
191: boolean catchFormatError) {
192: return (catchFormatError ? getLong(name, deflt, deflt)
193: : getLong(name, deflt));
194: }
195:
196: public static long getLong(String name, long deflt, long parseDeflt) {
197: try {
198: return getLong(name, deflt);
199: } catch (NumberFormatException nfe) {
200: return parseDeflt;
201: }
202: }
203:
204: public static float getFloat(String name, float deflt) {
205: String s = getProperty(name);
206: return (s == null ? deflt : Float.parseFloat(s));
207: }
208:
209: public static float getFloat(String name, float deflt,
210: boolean catchFormatError) {
211: return (catchFormatError ? getFloat(name, deflt, deflt)
212: : getFloat(name, deflt));
213: }
214:
215: public static float getFloat(String name, float deflt,
216: float parseDeflt) {
217: try {
218: return getFloat(name, deflt);
219: } catch (NumberFormatException nfe) {
220: return parseDeflt;
221: }
222: }
223:
224: public static double getDouble(String name, double deflt) {
225: String s = getProperty(name);
226: return (s == null ? deflt : Double.parseDouble(s));
227: }
228:
229: public static double getDouble(String name, double deflt,
230: boolean catchFormatError) {
231: return (catchFormatError ? getDouble(name, deflt, deflt)
232: : getDouble(name, deflt));
233: }
234:
235: public static double getDouble(String name, double deflt,
236: double parseDeflt) {
237: try {
238: return getDouble(name, deflt);
239: } catch (NumberFormatException nfe) {
240: return parseDeflt;
241: }
242: }
243:
244: /**
245: * @see #getSystemPropertiesWithPrefix
246: */
247: public static Enumeration getPropertyNames() {
248: return (Enumeration) AccessController
249: .doPrivileged(new PrivilegedAction() {
250: public Object run() {
251: return _getPropertyNames();
252: }
253: });
254: }
255:
256: /**
257: * Set a system property.
258: * @see #overrideProperties
259: */
260: public static Object setProperty(String name, String value) {
261: return _setProperty(name, value);
262: }
263:
264: /**
265: * @return a limited System properties wrapper.
266: * @see #overrideProperties
267: */
268: public static Properties getProperties() {
269: return PropertiesImpl.INSTANCE;
270: }
271:
272: /**
273: * Returns standard Java properies without the need for write privileges.
274: * <p>
275: * This method retrieve system properties without requiring write access
276: * privileges (which could be a potential security vulnerability).
277: */
278: public static Properties getStandardSystemProperties() {
279: String[] propname = { "java.version", "java.vendor",
280: "java.vendor.url", "java.class.version", "os.name",
281: "os.version", "os.arch", "file.separator",
282: "path.separator", "line.separator",
283: "java.specification.version",
284: "java.specification.vendor", "java.specification.name",
285: "java.vm.specification.version",
286: "java.vm.specification.vendor",
287: "java.vm.specification.name", "java.vm.version",
288: "java.vm.vendor", "java.vm.name" };
289:
290: Properties props = new Properties();
291: for (int i = 0; i < propname.length; i++) {
292: // Make a copy of the system properties.
293: props.setProperty(propname[i], getProperty(propname[i]));
294: }
295: return props;
296: }
297:
298: /**
299: * Return a Map of system properties.
300: * <p>
301: *
302: * This method return a Map of all properties that start with the specified
303: * prefix.
304: * Unlike the System.getProperties() method, this method does not require
305: * "write property" privileges.
306: *
307: * @param prefix Used to return property names that start with this specified prefix.
308: * @return A Map of system properties.
309: */
310: public static Properties getSystemPropertiesWithPrefix(String prefix) {
311: Properties props = new Properties();
312: for (Enumeration en = getPropertyNames(); en.hasMoreElements();) {
313: String key = (String) en.nextElement();
314: if (key.startsWith(prefix)) {
315: try {
316: // Make sure this property can be read. Check against the security policy.
317: // The following line will throw a security exception if the thread
318: // does not have the permission to read that property.
319: String value = getProperty(key);
320:
321: props.setProperty(key, value);
322: } catch (SecurityException e) {
323: // Don't add the property if we cannot read it.
324: }
325: }
326: }
327: return props;
328: }
329:
330: /**
331: * Expand System properties.
332: * <p>
333: * The purpose of the property expansion is to make Java properties
334: * clearer and easier to maintain. Use the "${}" tag to introduce
335: * substitutable parameters, so they can be expanded to values
336: * indicated with tag names during property retrieval at runtime.
337: * Properties may be nested as shown in the example below:<pre>
338: * a = "foo"
339: * a.subA = "bob"
340: * c = "subA"
341: * b = "${a} ${a.${c}}/smith" => b = "foo bob/smith" after
342: * property expansion.
343: * </pre>
344: */
345: public static void expandProperties() {
346: expandProperties(getProperties());
347: }
348:
349: /** @see #expandProperties() */
350: public static void expandProperties(Properties props) {
351: boolean expandProperties = Boolean.valueOf(
352: props.getProperty("org.cougaar.properties.expand",
353: "true")).booleanValue();
354:
355: if (expandProperties) {
356: Enumeration en = props.propertyNames();
357: while (en.hasMoreElements()) {
358: String key = (String) en.nextElement();
359: Set references = new HashSet();
360: expandProperty(props, key, references);
361: }
362: }
363: }
364:
365: /**
366: * Expand a system property using variable substitution.
367: * <p>
368: *
369: * @param props A Map of system properties.
370: * @param key The name of the property that should be expanded.
371: * @param references A Set used to deal with forward and circular references.
372: * @return the value of the expanded property.
373: */
374: private static String expandProperty(Properties props, String key,
375: Set references) {
376: String originalValue = props.getProperty(key);
377: String value = originalValue;
378: boolean done = false;
379: while (!done) {
380: Matcher m = expandPattern.matcher(value);
381: StringBuffer sb = new StringBuffer();
382: done = true;
383: while (m.find()) {
384: done = false;
385: String pKey = m.group(1);
386: /* The replaceAll is needed to handle the backslash character
387: * in directory names, e.g. c:\cougaar
388: * Otherwise the resulting string would be "c:cougaar"
389: */
390: String pVal = props.getProperty(pKey);
391: if (pVal == null) {
392: throw new IllegalArgumentException(
393: "Unresolved property: " + pKey);
394: }
395: pVal = pVal.replaceAll("\\\\", "\\\\\\\\");
396: if (expandPattern.matcher(pVal).find()) {
397: // This is a forward reference
398: if (references.contains(pKey)) {
399: // This is a circular reference.
400: throw new IllegalArgumentException(
401: "Circular reference at " + pKey + " = "
402: + pVal);
403: }
404: references.add(pKey);
405: pVal = expandProperty(props, pKey, references)
406: .replaceAll("\\\\", "\\\\\\\\");
407: }
408: m.appendReplacement(sb, pVal);
409: }
410: m.appendTail(sb);
411: value = sb.toString();
412: }
413: value = escapePattern.matcher(value).replaceAll("\\$\\{$1\\}");
414: if (!value.equals(originalValue)) {
415: props.setProperty(key, value);
416: }
417: return value;
418: }
419:
420: /**
421: * Get an environment variable.
422: * <p>
423: * This is easy in JDK 1.5, but in JDK 1.4 we need this workaround.
424: */
425: public static synchronized String getenv(String name) {
426: if (env != null) {
427: return (String) env.get(name);
428: }
429: try {
430: return System.getenv(name); // not deprecated in JDK 1.5!
431: } catch (Throwable e) {
432: String s = e.getMessage();
433: if (s == null
434: || (!s.startsWith("getenv no longer supported") && !s
435: .startsWith("access denied"))) {
436: throw new RuntimeException("getenv(" + name
437: + ") failed?", e);
438: }
439: }
440: env = new HashMap();
441: // build table of environment variables
442: //
443: // JDK 1.4 dropped "System.getenv" but it'll be back in JDK 1.5
444: // (bug 4199068). For now, we use this Linux-only approach
445: // instead of JNI. The following code is from:
446: // http://intgat.tigress.co.uk/rmy/java/getenv/pure.html
447: // Windows users will need 1.5 or, as a workaround, they can
448: // create a dummy "/proc/self/environ" file.
449: LineNumberReader reader = null;
450: try {
451: reader = new LineNumberReader(new FileReader(
452: "/proc/self/environ"));
453: while (true) {
454: String s = reader.readLine();
455: if (s == null)
456: break;
457: String[] lines = s.split("\000");
458: for (int i = 0; i < lines.length; i++) {
459: String line = lines[i];
460: int n = line.indexOf('=');
461: if (n >= 0)
462: env.put(line.substring(0, n), line
463: .substring(n + 1));
464: }
465: }
466: } catch (Throwable e) {
467: String s = e.getMessage();
468: if (s == null || !s.startsWith("access denied")) {
469: String os = getProperty("os.name");
470: if (!"Linux".equals(os)) {
471: throw new UnsupportedOperationException(
472: "Unable to read environment variables in "
473: + os);
474: }
475: throw new RuntimeException(
476: "I/O exception reading environment variables",
477: e);
478: }
479: } finally {
480: try {
481: if (reader != null)
482: reader.close();
483: } catch (IOException ioe) {
484: // ignore
485: }
486: }
487: return (String) env.get(name);
488: }
489:
490: /**
491: * Resolve a string like the shell would resolve it, which includes
492: * {@link #resolveVariables} and Linux "\" removal.
493: * <p>
494: * This is a decent approximation that handles the common cases, but it
495: * doesn't handle all the oddities..
496: */
497: public static String resolveEnv(String orig_value, boolean windows) {
498: String value = orig_value;
499: if (value == null) {
500: return "";
501: }
502: // unquote
503: if (value.startsWith("\'") && value.endsWith("\'")) {
504: return value.substring(1, value.length() - 1);
505: }
506: if (value.startsWith("\"") && value.endsWith("\"")) {
507: value = value.substring(1, value.length() - 1);
508: }
509: value = resolveVariables(value, windows, null, true);
510: if (!windows && value.indexOf('\\') >= 0) {
511: // remove "\"s, e.g.:
512: // a\\b\c --> a\bc
513: // it's important to do this after the variable expansion,
514: // to support:
515: // \$x --> $x
516: StringBuffer buf = new StringBuffer();
517: for (int i = 0; i < value.length(); i++) {
518: char ch = value.charAt(i);
519: if (ch == '\\') {
520: if (++i > value.length()) {
521: break;
522: }
523: ch = value.charAt(i);
524: }
525: buf.append(ch);
526: }
527: value = buf.toString();
528: }
529: return value;
530: }
531:
532: /**
533: * Resolve environment variables.
534: * <p>
535: * For example, on Linux, this will resolve:<br>
536: * <code>a/$USER/b</code><br>
537: * to (say):<br>
538: * <code>a/root/b</code><br>
539: *
540: * @param env_override optional {@link #getenv} override map, which can
541: * be null
542: * @param default_to_getenv if a variable is not found in the "env_override"
543: * and this parameter is set to true, then look in {@link #getenv}
544: */
545: public static String resolveVariables(String value,
546: boolean windows, Map env_override, boolean default_to_getenv) {
547: if (value == null) {
548: return "";
549: }
550: char varCh = (windows ? '%' : '$');
551: if (value.indexOf(varCh) < 0) {
552: return value;
553: }
554: StringBuffer buf = new StringBuffer();
555: int i = 0;
556: while (true) {
557: int j = value.indexOf(varCh, i);
558: if (j < 0) {
559: buf.append(value.substring(i));
560: break;
561: }
562: boolean escape = false;
563: if (!windows) {
564: for (int k = j - 1; k >= i && value.charAt(k) == '\\'; k--) {
565: escape = !escape;
566: }
567: }
568: buf.append(value.substring(i, j));
569: i = j;
570: if (escape) {
571: buf.append(varCh);
572: i++;
573: continue;
574: }
575: boolean keepLastChar = false;
576: if (windows) {
577: j = value.indexOf('%', i + 1);
578: } else if (value.charAt(i + 1) == '{') {
579: i++;
580: j = value.indexOf('}', i + 1);
581: } else {
582: keepLastChar = true;
583: for (j = i + 1; j < value.length(); j++) {
584: char ch = value.charAt(j);
585: if (!Character.isLetterOrDigit(ch) && ch != '_'
586: && ch != '-') {
587: break;
588: }
589: }
590: }
591: String envKey = value.substring(i + 1, j);
592: String envValue = null;
593: if (env_override != null) {
594: envValue = (String) env_override.get(envKey);
595: }
596: if (envValue == null && default_to_getenv) {
597: envValue = getenv(envKey);
598: }
599: if (envValue == null) {
600: envValue = "";
601: }
602: buf.append(envValue);
603: if (keepLastChar && j < value.length()) {
604: buf.append(value.charAt(j));
605: }
606: i = j + 1;
607: if (i > value.length()) {
608: break;
609: }
610: }
611: return buf.toString();
612: }
613:
614: private static final class PropertiesImpl extends Properties {
615:
616: // use a singleton
617: private static final PropertiesImpl INSTANCE = new PropertiesImpl();
618:
619: private PropertiesImpl() {
620: }
621:
622: // these methods do all the work:
623: public String getProperty(String name, String deflt) {
624: return SystemProperties.getProperty(name, deflt);
625: }
626:
627: public Object setProperty(String name, String value) {
628: return SystemProperties.setProperty(name, value);
629: }
630:
631: public Enumeration propertyNames() {
632: return SystemProperties.getPropertyNames();
633: }
634:
635: //
636: // the remaining methods are entirely based on the above methods
637: //
638:
639: // from Properties:
640: public void load(InputStream inStream) throws IOException {
641: super .load(inStream); // this only calls our methods
642: }
643:
644: // save calls "store()". Here we comment-out this method to avoid a
645: // compile-time deprecation warning.
646: //public void save(OutputStream o, String s) { super.save(o, s); }
647: public void store(OutputStream out, String header)
648: throws IOException {
649: super .store(out, header); // this only calls our methods
650: }
651:
652: public String getProperty(String name) {
653: return getProperty(name, null);
654: }
655:
656: public void list(PrintStream out) {
657: out.println("-- listing properties --");
658: for (Iterator iter = entrySet().iterator(); iter.hasNext();) {
659: Map.Entry me = (Map.Entry) iter.next();
660: String key = (String) me.getKey();
661: String val = (String) me.getValue();
662: if (val.length() > 40) {
663: val = val.substring(0, 37) + "...";
664: }
665: out.println(key + "=" + val);
666: }
667: }
668:
669: public void list(PrintWriter out) {
670: out.println("-- listing properties --");
671: for (Iterator iter = entrySet().iterator(); iter.hasNext();) {
672: Map.Entry me = (Map.Entry) iter.next();
673: String key = (String) me.getKey();
674: String val = (String) me.getValue();
675: if (val.length() > 40) {
676: val = val.substring(0, 37) + "...";
677: }
678: out.println(key + "=" + val);
679: }
680: }
681:
682: // from Hashtable:
683: public int size() {
684: int ret = 0;
685: for (Enumeration en = propertyNames(); en.hasMoreElements();) {
686: ret++;
687: }
688: return ret;
689: }
690:
691: public boolean isEmpty() {
692: return propertyNames().hasMoreElements();
693: }
694:
695: public Enumeration keys() {
696: return propertyNames();
697: }
698:
699: public Enumeration elements() {
700: final Iterator iter = values().iterator();
701: return new Enumeration() {
702: public boolean hasMoreElements() {
703: return iter.hasNext();
704: }
705:
706: public Object nextElement() {
707: return iter.next();
708: }
709: };
710: }
711:
712: public boolean contains(Object o) {
713: for (Enumeration en = propertyNames(); en.hasMoreElements();) {
714: String key = (String) en.nextElement();
715: String value = getProperty(key);
716: if (value.equals(o))
717: return true;
718: }
719: return false;
720: }
721:
722: public boolean containsValue(Object o) {
723: return contains(o);
724: }
725:
726: public boolean containsKey(Object o) {
727: return (get(o) != null);
728: }
729:
730: public Object get(Object o) {
731: String s = (o instanceof String ? ((String) o) : null);
732: return getProperty(s);
733: }
734:
735: public Object put(Object name, Object value) {
736: return setProperty((String) name, (String) value);
737: }
738:
739: public Object remove(Object o) {
740: die();
741: return null;
742: }
743:
744: public void putAll(Map m) {
745: for (Iterator iter = m.entrySet().iterator(); iter
746: .hasNext();) {
747: Map.Entry me = (Map.Entry) iter.next();
748: put(me.getKey(), me.getValue());
749: }
750: }
751:
752: public void clear() {
753: for (Iterator iter = keySet().iterator(); iter.hasNext();) {
754: remove(iter.next());
755: }
756: }
757:
758: public Object clone() {
759: die();
760: return null;
761: }
762:
763: public String toString() {
764: boolean first = true;
765: StringBuffer buf = new StringBuffer("{");
766: for (Enumeration en = getPropertyNames(); en
767: .hasMoreElements();) {
768: String key = (String) en.nextElement();
769: String value = getProperty(key);
770: if (first) {
771: first = false;
772: buf.append(", ");
773: }
774: buf.append(key).append("=").append(value);
775: }
776: buf.append("}");
777: return buf.toString();
778: }
779:
780: public Set keySet() {
781: Set ret = new HashSet();
782: for (Enumeration en = propertyNames(); en.hasMoreElements();) {
783: ret.add(en.nextElement());
784: }
785: return ret;
786: }
787:
788: public Set entrySet() {
789: Set ret = new HashSet();
790: for (Enumeration en = propertyNames(); en.hasMoreElements();) {
791: final String key = (String) en.nextElement();
792: final String val = getProperty(key);
793: Map.Entry me = new Map.Entry() {
794: private String value = val;
795:
796: public Object getKey() {
797: return key;
798: }
799:
800: public Object getValue() {
801: return value;
802: }
803:
804: public Object setValue(Object value) {
805: Object oldValue = this .value;
806: this .value = (String) value;
807: setProperty(key, this .value);
808: return oldValue;
809: }
810:
811: public boolean equals(Object o) {
812: if (!(o instanceof Map.Entry))
813: return false;
814: Map.Entry e = (Map.Entry) o;
815: return eq(key, e.getKey())
816: && eq(value, e.getValue());
817: }
818:
819: public int hashCode() {
820: return ((key == null) ? 0 : key.hashCode())
821: ^ ((value == null) ? 0 : value
822: .hashCode());
823: }
824:
825: public String toString() {
826: return key + "=" + value;
827: }
828:
829: private boolean eq(Object o1, Object o2) {
830: return (o1 == null ? o2 == null : o1.equals(o2));
831: }
832: };
833: ret.add(me);
834: }
835: return ret;
836: }
837:
838: public Collection values() {
839: List ret = new ArrayList();
840: for (Enumeration en = propertyNames(); en.hasMoreElements();) {
841: ret.add(getProperty((String) en.nextElement()));
842: }
843: return ret;
844: }
845:
846: public boolean equals(Object o) {
847: die();
848: return false;
849: }
850:
851: public int hashCode() {
852: die();
853: return 0;
854: }
855:
856: // block serialization
857: private void writeObject(java.io.ObjectOutputStream s) {
858: die();
859: }
860:
861: private void readObject(java.io.ObjectInputStream s) {
862: die();
863: }
864:
865: // some methods are currently not supported
866: private void die() {
867: throw new UnsupportedOperationException();
868: }
869: }
870: }
|