Index: include/java_lang_VMSystem.h =================================================================== RCS file: /cvsroot/classpath/classpath/include/java_lang_VMSystem.h,v retrieving revision 1.4.2.2 diff -u -3 -p -u -r1.4.2.2 java_lang_VMSystem.h --- include/java_lang_VMSystem.h 16 Jan 2005 15:15:11 -0000 1.4.2.2 +++ include/java_lang_VMSystem.h 20 Jan 2005 14:59:04 -0000 @@ -16,6 +16,7 @@ JNIEXPORT void JNICALL Java_java_lang_VM JNIEXPORT void JNICALL Java_java_lang_VMSystem_setOut (JNIEnv *env, jclass, jobject); JNIEXPORT void JNICALL Java_java_lang_VMSystem_setErr (JNIEnv *env, jclass, jobject); JNIEXPORT jlong JNICALL Java_java_lang_VMSystem_currentTimeMillis (JNIEnv *env, jclass); +JNIEXPORT jobject JNICALL Java_java_lang_VMSystem_environ (JNIEnv *env, jclass); JNIEXPORT jstring JNICALL Java_java_lang_VMSystem_getenv (JNIEnv *env, jclass, jstring); #ifdef __cplusplus Index: java/lang/System.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/lang/System.java,v retrieving revision 1.38.2.8 diff -u -3 -p -u -r1.38.2.8 System.java --- java/lang/System.java 16 Jan 2005 15:15:12 -0000 1.38.2.8 +++ java/lang/System.java 20 Jan 2005 14:59:04 -0000 @@ -44,7 +44,12 @@ import gnu.classpath.VMStackWalker; import java.io.InputStream; import java.io.PrintStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Properties; import java.util.PropertyPermission; @@ -99,6 +104,11 @@ public final class System public static final PrintStream err = VMSystem.makeStandardErrorStream(); /** + * A cached copy of the environment variable map. + */ + private static Map environmentMap; + + /** * This class is uninstantiable. */ private System() @@ -135,8 +145,7 @@ public final class System { SecurityManager sm = SecurityManager.current; // Be thread-safe. if (sm != null) - sm.checkPermission(new RuntimePermission("setIO")); - + sm.checkPermission(new RuntimePermission("setIO")); VMSystem.setOut(out); } @@ -448,7 +457,35 @@ public final class System } /** - * FIXME: document + *

+ * Returns an unmodifiable view of the system environment variables. + * If the underlying system does not support environment variables, + * an empty map is returned. + *

+ *

+ * The returned map is read-only and does not accept queries using + * null keys or values, or those of a type other than String. + * Attempts to modify the map will throw an + * UnsupportedOperationException, while attempts + * to pass in a null value will throw a + * NullPointerException. Types other than String + * throw a ClassCastException. + *

+ *

+ * As the returned map is generated using data from the underlying + * platform, it may not comply with the equals() + * and hashCode() contracts. It is also likely that + * the keys of this map will be case-sensitive. + *

+ *

+ * Use of this method may require a security check for the + * RuntimePermission "getenv.*". + *

+ * + * @return a map of the system environment variables. + * @throws SecurityException if the checkPermission method of + * an installed security manager prevents access to + * the system environment variables. * @since 1.5 */ public static Map getenv() @@ -456,7 +493,180 @@ public final class System SecurityManager sm = SecurityManager.current; // Be thread-safe. if (sm != null) sm.checkPermission(new RuntimePermission("getenv.*")); - return VMSystem.getenv(); + if (environmentMap == null) + { + List environ = VMSystem.environ(); + Map variables = new HashMap(); + for (String pair : environ) + { + String[] parts; + + parts = pair.split("="); + variables.put(parts[0], parts[1]); + } + environmentMap = new HashMap(variables) + { + /** + * Blocks the removal of all mappings from the map. + * + * @throws UnsupportedOperationException as mappings + * cannot be removed from this map. + */ + public void clear() + { + throw new + UnsupportedOperationException("This map does not " + + "allow the removal of mappings."); + } + + /** + * Blocks queries containing a null key or one which is not + * of type String. All other queries + * are forwarded to the superclass. + * + * @param key the key to look for in the map. + * @return true if the key exists in the map. + * @throws NullPointerException if the specified key is null. + * @throws ClassCastException if the specified key is not a String. + */ + public boolean containsKey(Object key) + { + if (key == null) + { + throw new + NullPointerException("This map does not support null keys."); + } + if (!(key instanceof String)) + { + throw new + ClassCastException("This map only supports Strings."); + } + return super.containsKey(key); + } + + /** + * Blocks queries using a null or non-String value. + * All other queries are forwarded to the superclass. + * + * @param value the value to look for in the map. + * @return true if the value exists in the map. + * @throws NullPointerException if the specified value is null. + * @throws ClassCastException if the specified value is not a String. + */ + public boolean containsValue(Object value) + { + if (value == null) + { + throw new + NullPointerException("This map does not support null values."); + } + if (!(value instanceof String)) + { + throw new + ClassCastException("This map only supports Strings."); + } + return super.containsValue(value); + } + + /** + * Returns a set view of the map entries, with the same + * provisions as for the underlying map. + * + * @return a set containing the map entries. + */ + public Set> entrySet() + { + return new EnvironmentSet>(super.entrySet()); + } + + /** + * Blocks queries containing a null key. All other + * queries are passed on to the superclass. + * + * @param key the key to retrieve the value for. + * @return the value associated with the given key. + * @throws NullPointerException if the specified key is null. + * @throws ClassCastException if the specified key is not a String. + */ + public String get(Object key) + { + if (key == null) + { + throw new + NullPointerException("This map does not support null keys."); + } + return super.get(key); + } + + /** + * Returns a set view of the keys, with the same + * provisions as for the underlying map. + * + * @return a set containing the keys. + */ + public Set keySet() + { + return new EnvironmentSet(super.keySet()); + } + + /** + * Blocks the addition of mappings to the map. + * + * @param key the key to add. + * @param value the value to add. + * @return the previous value of the specified key, or + * null if there was no previous value. + * @throws UnsupportedOperationException as this map can't + * be extended. + */ + public String put(String key, String value) + { + throw new + UnsupportedOperationException("This map can not be extended."); + } + + /** + * Blocks the addition of mappings to the map. + * + * @param m the map from which to take the new entries. + * @throws UnsupportedOperationException as this map can't + * be extended. + */ + public void putAll(Map m) + { + throw new + UnsupportedOperationException("This map can not be extended."); + } + + /** + * Blocks the removal of entries from the map. + * + * @param key the key of the entry to remove. + * @return the removed value. + * @throws UnsupportedOperationException as entries can't + * be removed from this map. + */ + public String remove(Object key) + { + throw new + UnsupportedOperationException("Entries can not be removed " + + "from this map."); + } + + /** + * Returns a collection view of the values, with the same + * provisions as for the underlying map. + * + * @return a collection containing the values. + */ + public Collection values() + { + return new EnvironmentCollection(super.values()); + } + + }; + } + return environmentMap; } /** @@ -571,4 +781,343 @@ public final class System return VMRuntime.mapLibraryName(libname); } + + /** + * This is an specialised Collection, providing + * the necessary provisions for the collections used by the + * environment variable map. Namely, it prevents + * modifications and the use of queries with null + * or non-String values. + * + * @author Andrew John Hughes (address@hidden) + */ + private static class EnvironmentCollection + implements Collection + { + + /** + * The wrapped collection. + */ + protected Collection c; + + /** + * Constructs a new environment collection, which + * wraps the elements of the supplied collection. + * + * @param coll the collection to use as a base for + * this collection. + */ + public EnvironmentCollection(Collection coll) + { + c = (Collection) coll; + } + + /** + * Blocks the addition of elements to the collection. + * + * @param entry the new entry. + * @throws UnsupportedOperationException as the underlying + * map does not support new elements. + */ + public boolean add(E entry) + { + throw new + UnsupportedOperationException("The addition of elements is " + + "not supported."); + } + + /** + * Blocks the addition of a collection of elements to + * the collection. + * + * @param c the collection of elements to add. + * @throws UnsupportedOperationException as the underlying + * map does not support new elements. + */ + public boolean addAll(Collection c) + { + throw new + UnsupportedOperationException("The addition of elements is " + + "not supported."); + } + + /** + * Blocks the removal of all elements. + * + * @throws UnsupportedOperationException as elements + * cannot be removed from this map. + */ + public void clear() + { + throw new + UnsupportedOperationException("This map does not " + + "allow the removal of elements."); + } + + /** + * Blocks queries containing a null object or an object which + * isn't of type String. All other queries + * are forwarded to the underlying collection. + * + * @param obj the object to look for. + * @return true if the object exists in the collection. + * @throws NullPointerException if the specified object is null. + * @throws ClassCastException if the specified object is not a String. + */ + public boolean contains(Object obj) + { + if (obj == null) + { + throw new + NullPointerException("This collection does not support null values."); + } + if (!(obj instanceof String)) + { + throw new + ClassCastException("This collection only supports Strings."); + } + return c.contains(obj); + } + + /** + * Blocks queries where the collection contains a null object or + * an object which isn't of type String. All other + * queries are forwarded to the underlying collection. + * + * @param coll the collection of objects to look for. + * @return true if the collection contains all elements in the collection. + * @throws NullPointerException if the collection is null. + * @throws NullPointerException if any collection entry is null. + * @throws ClassCastException if any collection entry is not a String. + */ + public boolean containsAll(Collection coll) + { + for (Object o: coll) + { + if (o == null) + { + throw new + NullPointerException("This collection does not support null values."); + } + if (!(o instanceof String)) + { + throw new + ClassCastException("This collection only supports Strings."); + } + } + return c.containsAll(coll); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @return true if the collection is empty. + */ + public boolean isEmpty() + { + return c.isEmpty(); + } + + /** + * This returns an iterator over the map elements, with the + * same provisions as for the collection and underlying map. + * + * @return an iterator over the map elements. + */ + public Iterator iterator() + { + return new + EnvironmentIterator(c.iterator()); + } + + /** + * Blocks the removal of elements from the collection. + * + * @return true if the removal was sucessful. + * @throws UnsupportedOperationException as elements can't + * be removed from this collection. + */ + public boolean remove(Object key) + { + throw new + UnsupportedOperationException("Elements can not be removed " + + "from this collection."); + } + + /** + * Blocks the removal of all elements in the specified + * collection from the collection. + * + * @param c the collection of elements to remove. + * @return true if the elements were removed. + * @throws UnsupportedOperationException as elements can't + * be removed from this collection. + */ + public boolean removeAll(Collection c) + { + throw new + UnsupportedOperationException("Elements can not be removed " + + "from this collection."); + } + + /** + * Blocks the retention of all elements in the specified + * collection from the collection. + * + * @param c the collection of elements to retain. + * @return true if the other elements were removed. + * @throws UnsupportedOperationException as elements can't + * be removed from this collection. + */ + public boolean retainAll(Collection c) + { + throw new + UnsupportedOperationException("Elements can not be removed " + + "from this collection."); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @return the size of the underlying collection. + */ + public int size() + { + return c.size(); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @return the collection in array form. + */ + public Object[] toArray() + { + return c.toArray(); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @param a the array to use to type the result. + * @return the collection in appropriately-typed + * array form. + */ + public T[] toArray(T[] a) + { + return c.toArray(a); + } + + } // class EnvironmentCollection + + private static class EnvironmentSet + extends EnvironmentCollection + implements Set + { + + /** + * Constructs a new environment set, which + * wraps the elements of the supplied set. + * + * @param set the set to use as a base for + * this set. + */ + public EnvironmentSet(Set set) + { + super(set); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @param obj the object to compare with. + * @return true if the two objects are equal. + */ + public boolean equals(Object obj) + { + return c.equals(obj); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @return the hashcode of the collection. + */ + public int hashCode() + { + return c.hashCode(); + } + + } // class EnvironmentSet + + /* The class below is a clone of UnmodifiableIterator + * from java.util.Collections. Please keep it in sync */ + /** + * The implementation of the various iterator methods in the + * unmodifiable classes. + * + * @author Eric Blake (address@hidden) + */ + private static class EnvironmentIterator + implements Iterator + { + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * Only trusted code creates a wrapper. + * @param i the wrapped iterator + */ + EnvironmentIterator(Iterator i) + { + this.i = i; + } + + /** + * Obtains the next element in the underlying collection. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public T next() + { + return i.next(); + } + + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by next(). When this method + * returns true, an exception will not be thrown on calling + * next(). + * + * @return true if there is at least one more element in the underlying + * collection. + */ + public boolean hasNext() + { + return i.hasNext(); + } + + /** + * Blocks the removal of elements from the underlying collection by the + * iterator. + * + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the removal of elements by its iterator. + */ + public void remove() + { + throw new UnsupportedOperationException("The removal of elements is " + + "not supported."); + } + } // class EnvironmentIterator + } // class System Index: native/jni/java-lang/java_lang_VMSystem.c =================================================================== RCS file: /cvsroot/classpath/classpath/native/jni/java-lang/java_lang_VMSystem.c,v retrieving revision 1.6.2.3 diff -u -3 -p -u -r1.6.2.3 java_lang_VMSystem.c --- native/jni/java-lang/java_lang_VMSystem.c 16 Jan 2005 15:15:14 -0000 1.6.2.3 +++ native/jni/java-lang/java_lang_VMSystem.c 20 Jan 2005 14:59:05 -0000 @@ -157,3 +157,36 @@ Java_java_lang_VMSystem_getenv (JNIEnv * JCL_free_cstring(env, jname, cname); return (*env)->NewStringUTF(env, envname); } + +JNIEXPORT jobject JNICALL +Java_java_lang_VMSystem_environ (JNIEnv *env, + jclass klass __attribute__((__unused__))) +{ + extern char** environ; + char **env_pointer; + jobject variables; + jclass list_class; + jmethodID list_constructor; + jmethodID add; + + list_class = (*env)->FindClass(env, "java/util/LinkedList"); + if (list_class == NULL) + return NULL; + list_constructor = (*env)->GetMethodID(env, list_class, "", "()V"); + if (list_constructor == NULL) + return NULL; + variables = (*env)->NewObject(env, list_class, list_constructor); + if (variables == NULL) + return NULL; + add = (*env)->GetMethodID(env, list_class, "add", "(Ljava/lang/Object;)Z"); + if (add == NULL) + return NULL; + env_pointer = environ; + while (*env_pointer != NULL) + { + jstring string = (*env)->NewStringUTF(env, *env_pointer); + (*env)->CallBooleanMethod(env, variables, add, string); + ++env_pointer; + } + return variables; +} Index: vm/reference/java/lang/VMSystem.java =================================================================== RCS file: /cvsroot/classpath/classpath/vm/reference/java/lang/VMSystem.java,v retrieving revision 1.10.2.3 diff -u -3 -p -u -r1.10.2.3 VMSystem.java --- vm/reference/java/lang/VMSystem.java 16 Jan 2005 15:15:15 -0000 1.10.2.3 +++ vm/reference/java/lang/VMSystem.java 20 Jan 2005 14:59:05 -0000 @@ -37,16 +37,24 @@ exception statement from your version. * package java.lang; +import java.util.List; import java.util.Map; import java.util.Properties; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.PrintStream; /** * VMSystem is a package-private helper class for System that the * VM must implement. * * @author John Keiser + * @author Andrew John Hughes (address@hidden) */ final class VMSystem { @@ -132,9 +140,12 @@ final class VMSystem public static native long currentTimeMillis(); /** - * Return an unmodifiable Map representing the current environment. + * Returns a list of 'name=value' pairs representing the current environment + * variables. + * + * @return a list of 'name=value' pairs. */ - static native Map getenv(); + static native List environ(); /** * Gets the value of an environment variable from the current @@ -145,12 +156,7 @@ final class VMSystem * @return The string value of the variable or null when the * environment variable is not defined. */ - static String getenv(String k) - { - // Inefficient but portable default implementation. - Map m = getenv(); - return m.get(k); - } + static native String getenv(String k); /** * Helper method which creates the standard input stream.