Home | History | Annotate | Download | only in lang
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 /*
     18  * Copyright (C) 2008 The Android Open Source Project
     19  *
     20  * Licensed under the Apache License, Version 2.0 (the "License");
     21  * you may not use this file except in compliance with the License.
     22  * You may obtain a copy of the License at
     23  *
     24  *      http://www.apache.org/licenses/LICENSE-2.0
     25  *
     26  * Unless required by applicable law or agreed to in writing, software
     27  * distributed under the License is distributed on an "AS IS" BASIS,
     28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     29  * See the License for the specific language governing permissions and
     30  * limitations under the License.
     31  */
     32 
     33 package java.lang;
     34 
     35 import dalvik.system.VMRuntime;
     36 import dalvik.system.VMStack;
     37 import java.io.BufferedInputStream;
     38 import java.io.Console;
     39 import java.io.FileDescriptor;
     40 import java.io.FileInputStream;
     41 import java.io.FileOutputStream;
     42 import java.io.IOException;
     43 import java.io.InputStream;
     44 import java.io.PrintStream;
     45 import java.nio.channels.Channel;
     46 import java.nio.channels.spi.SelectorProvider;
     47 import java.util.AbstractMap;
     48 import java.util.Collections;
     49 import java.util.HashMap;
     50 import java.util.Map;
     51 import java.util.Properties;
     52 import java.util.Set;
     53 import libcore.icu.ICU;
     54 import libcore.io.ErrnoException;
     55 import libcore.io.Libcore;
     56 import libcore.io.StructPasswd;
     57 import libcore.io.StructUtsname;
     58 import libcore.util.ZoneInfoDB;
     59 
     60 /**
     61  * Provides access to system-related information and resources including
     62  * standard input and output. Enables clients to dynamically load native
     63  * libraries. All methods of this class are accessed in a static way and the
     64  * class itself can not be instantiated.
     65  *
     66  * @see Runtime
     67  */
     68 public final class System {
     69 
     70     /**
     71      * Default input stream.
     72      */
     73     public static final InputStream in;
     74 
     75     /**
     76      * Default output stream.
     77      */
     78     public static final PrintStream out;
     79 
     80     /**
     81      * Default error output stream.
     82      */
     83     public static final PrintStream err;
     84 
     85     private static final String lineSeparator;
     86     private static Properties systemProperties;
     87 
     88     static {
     89         err = new PrintStream(new FileOutputStream(FileDescriptor.err));
     90         out = new PrintStream(new FileOutputStream(FileDescriptor.out));
     91         in = new BufferedInputStream(new FileInputStream(FileDescriptor.in));
     92         lineSeparator = System.getProperty("line.separator");
     93     }
     94 
     95     /**
     96      * Sets the standard input stream to the given user defined input stream.
     97      *
     98      * @param newIn
     99      *            the user defined input stream to set as the standard input
    100      *            stream.
    101      */
    102     public static void setIn(InputStream newIn) {
    103         setFieldImpl("in", "Ljava/io/InputStream;", newIn);
    104     }
    105 
    106     /**
    107      * Sets the standard output stream to the given user defined output stream.
    108      *
    109      * @param newOut
    110      *            the user defined output stream to set as the standard output
    111      *            stream.
    112      */
    113     public static void setOut(PrintStream newOut) {
    114         setFieldImpl("out", "Ljava/io/PrintStream;", newOut);
    115     }
    116 
    117     /**
    118      * Sets the standard error output stream to the given user defined output
    119      * stream.
    120      *
    121      * @param newErr
    122      *            the user defined output stream to set as the standard error
    123      *            output stream.
    124      */
    125     public static void setErr(PrintStream newErr) {
    126         setFieldImpl("err", "Ljava/io/PrintStream;", newErr);
    127     }
    128 
    129     /**
    130      * Prevents this class from being instantiated.
    131      */
    132     private System() {
    133     }
    134 
    135     /**
    136      * Copies {@code length} elements from the array {@code src},
    137      * starting at offset {@code srcPos}, into the array {@code dst},
    138      * starting at offset {@code dstPos}.
    139      *
    140      * <p>The source and destination arrays can be the same array,
    141      * in which case copying is performed as if the source elements
    142      * are first copied into a temporary array and then into the
    143      * destination array.
    144      *
    145      * @param src
    146      *            the source array to copy the content.
    147      * @param srcPos
    148      *            the starting index of the content in {@code src}.
    149      * @param dst
    150      *            the destination array to copy the data into.
    151      * @param dstPos
    152      *            the starting index for the copied content in {@code dst}.
    153      * @param length
    154      *            the number of elements to be copied.
    155      */
    156     public static native void arraycopy(Object src, int srcPos, Object dst, int dstPos, int length);
    157 
    158     /**
    159      * Returns the current time in milliseconds since January 1, 1970 00:00:00.0 UTC.
    160      *
    161      * <p>This method always returns UTC times, regardless of the system's time zone.
    162      * This is often called "Unix time" or "epoch time".
    163      * Use a {@link java.text.DateFormat} instance to format this time for display to a human.
    164      *
    165      * <p>This method shouldn't be used for measuring timeouts or
    166      * other elapsed time measurements, as changing the system time can affect
    167      * the results. Use {@link #nanoTime} for that.
    168      */
    169     public static native long currentTimeMillis();
    170 
    171     /**
    172      * Returns the current timestamp of the most precise timer available on the
    173      * local system, in nanoseconds. Equivalent to Linux's {@code CLOCK_MONOTONIC}.
    174      *
    175      * <p>This timestamp should only be used to measure a duration by comparing it
    176      * against another timestamp on the same device.
    177      * Values returned by this method do not have a defined correspondence to
    178      * wall clock times; the zero value is typically whenever the device last booted.
    179      * Use {@link #currentTimeMillis} if you want to know what time it is.
    180      */
    181     public static native long nanoTime();
    182 
    183     /**
    184      * Causes the VM to stop running and the program to exit with the given exit status.
    185      * If {@link #runFinalizersOnExit(boolean)} has been previously invoked with a
    186      * {@code true} argument, then all objects will be properly
    187      * garbage-collected and finalized first.
    188      */
    189     public static void exit(int code) {
    190         Runtime.getRuntime().exit(code);
    191     }
    192 
    193     /**
    194      * Indicates to the VM that it would be a good time to run the
    195      * garbage collector. Note that this is a hint only. There is no guarantee
    196      * that the garbage collector will actually be run.
    197      */
    198     public static void gc() {
    199         Runtime.getRuntime().gc();
    200     }
    201 
    202     /**
    203      * Returns the value of the environment variable with the given name, or null if no such
    204      * variable exists.
    205      */
    206     public static String getenv(String name) {
    207         if (name == null) {
    208             throw new NullPointerException("name == null");
    209         }
    210         return Libcore.os.getenv(name);
    211     }
    212 
    213     /**
    214      * Returns an unmodifiable map of all environment variables to their values.
    215      */
    216     public static Map<String, String> getenv() {
    217         Map<String, String> map = new HashMap<String, String>();
    218         for (String entry : Libcore.os.environ()) {
    219             int index = entry.indexOf('=');
    220             if (index != -1) {
    221                 map.put(entry.substring(0, index), entry.substring(index + 1));
    222             }
    223         }
    224         return new SystemEnvironment(map);
    225     }
    226 
    227     /**
    228      * Returns the inherited channel from the creator of the current virtual
    229      * machine.
    230      *
    231      * @return the inherited {@link Channel} or {@code null} if none exists.
    232      * @throws IOException
    233      *             if an I/O error occurred.
    234      * @see SelectorProvider
    235      * @see SelectorProvider#inheritedChannel()
    236      */
    237     public static Channel inheritedChannel() throws IOException {
    238         return SelectorProvider.provider().inheritedChannel();
    239     }
    240 
    241     /**
    242      * Returns the system properties. Note that this is not a copy, so that
    243      * changes made to the returned Properties object will be reflected in
    244      * subsequent calls to getProperty and getProperties.
    245      *
    246      * @return the system properties.
    247      */
    248     public static Properties getProperties() {
    249         if (systemProperties == null) {
    250             initSystemProperties();
    251         }
    252         return systemProperties;
    253     }
    254 
    255     private static void initSystemProperties() {
    256         VMRuntime runtime = VMRuntime.getRuntime();
    257         Properties p = new Properties();
    258 
    259         String projectUrl = "http://www.android.com/";
    260         String projectName = "The Android Project";
    261 
    262         p.put("java.boot.class.path", runtime.bootClassPath());
    263         p.put("java.class.path", runtime.classPath());
    264 
    265         // None of these four are meaningful on Android, but these keys are guaranteed
    266         // to be present for System.getProperty. For java.class.version, we use the maximum
    267         // class file version that dx currently supports.
    268         p.put("java.class.version", "50.0");
    269         p.put("java.compiler", "");
    270         p.put("java.ext.dirs", "");
    271         p.put("java.version", "0");
    272 
    273         // TODO: does this make any sense? Should we just leave java.home unset?
    274         String javaHome = getenv("JAVA_HOME");
    275         if (javaHome == null) {
    276             javaHome = "/system";
    277         }
    278         p.put("java.home", javaHome);
    279 
    280         // On Android, each app gets its own temporary directory. This is just a fallback
    281         // default, useful only on the host.
    282         p.put("java.io.tmpdir", "/tmp");
    283 
    284         String ldLibraryPath = getenv("LD_LIBRARY_PATH");
    285         if (ldLibraryPath != null) {
    286             p.put("java.library.path", ldLibraryPath);
    287         }
    288 
    289         p.put("java.specification.name", "Dalvik Core Library");
    290         p.put("java.specification.vendor", projectName);
    291         p.put("java.specification.version", "0.9");
    292 
    293         p.put("java.vendor", projectName);
    294         p.put("java.vendor.url", projectUrl);
    295         p.put("java.vm.name", "Dalvik");
    296         p.put("java.vm.specification.name", "Dalvik Virtual Machine Specification");
    297         p.put("java.vm.specification.vendor", projectName);
    298         p.put("java.vm.specification.version", "0.9");
    299         p.put("java.vm.vendor", projectName);
    300         p.put("java.vm.version", runtime.vmVersion());
    301 
    302         p.put("file.separator", "/");
    303         p.put("line.separator", "\n");
    304         p.put("path.separator", ":");
    305 
    306         p.put("java.runtime.name", "Android Runtime");
    307         p.put("java.runtime.version", "0.9");
    308         p.put("java.vm.vendor.url", projectUrl);
    309 
    310         p.put("file.encoding", "UTF-8");
    311         p.put("user.language", "en");
    312         p.put("user.region", "US");
    313 
    314         try {
    315             StructPasswd passwd = Libcore.os.getpwuid(Libcore.os.getuid());
    316             p.put("user.home", passwd.pw_dir);
    317             p.put("user.name", passwd.pw_name);
    318         } catch (ErrnoException exception) {
    319             throw new AssertionError(exception);
    320         }
    321 
    322         StructUtsname info = Libcore.os.uname();
    323         p.put("os.arch", info.machine);
    324         p.put("os.name", info.sysname);
    325         p.put("os.version", info.release);
    326 
    327         // Undocumented Android-only properties.
    328         p.put("android.icu.library.version", ICU.getIcuVersion());
    329         p.put("android.icu.unicode.version", ICU.getUnicodeVersion());
    330         p.put("android.icu.cldr.version", ICU.getCldrVersion());
    331 
    332         parsePropertyAssignments(p, specialProperties());
    333 
    334         // Override built-in properties with settings from the command line.
    335         parsePropertyAssignments(p, runtime.properties());
    336 
    337         systemProperties = p;
    338     }
    339 
    340     /**
    341      * Returns an array of "key=value" strings containing information not otherwise
    342      * easily available, such as #defined library versions.
    343      */
    344     private static native String[] specialProperties();
    345 
    346     /**
    347      * Adds each element of 'assignments' to 'p', treating each element as an
    348      * assignment in the form "key=value".
    349      */
    350     private static void parsePropertyAssignments(Properties p, String[] assignments) {
    351         for (String assignment : assignments) {
    352             int split = assignment.indexOf('=');
    353             String key = assignment.substring(0, split);
    354             String value = assignment.substring(split + 1);
    355             p.put(key, value);
    356         }
    357     }
    358 
    359     /**
    360      * Returns the value of a particular system property or {@code null} if no
    361      * such property exists.
    362      *
    363      * <p>The following properties are always provided by the Dalvik VM:
    364      * <p><table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
    365      * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
    366      *     <td><b>Name</b></td>        <td><b>Meaning</b></td>                    <td><b>Example</b></td></tr>
    367      * <tr><td>file.separator</td>     <td>{@link java.io.File#separator}</td>    <td>{@code /}</td></tr>
    368      *
    369      * <tr><td>java.class.path</td>    <td>System class path</td>                 <td>{@code .}</td></tr>
    370      * <tr><td>java.class.version</td> <td>(Not useful on Android)</td>           <td>{@code 50.0}</td></tr>
    371      * <tr><td>java.compiler</td>      <td>(Not useful on Android)</td>           <td>Empty</td></tr>
    372      * <tr><td>java.ext.dirs</td>      <td>(Not useful on Android)</td>           <td>Empty</td></tr>
    373      * <tr><td>java.home</td>          <td>Location of the VM on the file system</td> <td>{@code /system}</td></tr>
    374      * <tr><td>java.io.tmpdir</td>     <td>See {@link java.io.File#createTempFile}</td> <td>{@code /sdcard}</td></tr>
    375      * <tr><td>java.library.path</td>  <td>Search path for JNI libraries</td>     <td>{@code /vendor/lib:/system/lib}</td></tr>
    376      * <tr><td>java.vendor</td>        <td>Human-readable VM vendor</td>          <td>{@code The Android Project}</td></tr>
    377      * <tr><td>java.vendor.url</td>    <td>URL for VM vendor's web site</td>      <td>{@code http://www.android.com/}</td></tr>
    378      * <tr><td>java.version</td>       <td>(Not useful on Android)</td>           <td>{@code 0}</td></tr>
    379      *
    380      * <tr><td>java.specification.version</td>    <td>VM libraries version</td>        <td>{@code 0.9}</td></tr>
    381      * <tr><td>java.specification.vendor</td>     <td>VM libraries vendor</td>         <td>{@code The Android Project}</td></tr>
    382      * <tr><td>java.specification.name</td>       <td>VM libraries name</td>           <td>{@code Dalvik Core Library}</td></tr>
    383      * <tr><td>java.vm.version</td>               <td>VM implementation version</td>   <td>{@code 1.2.0}</td></tr>
    384      * <tr><td>java.vm.vendor</td>                <td>VM implementation vendor</td>    <td>{@code The Android Project}</td></tr>
    385      * <tr><td>java.vm.name</td>                  <td>VM implementation name</td>      <td>{@code Dalvik}</td></tr>
    386      * <tr><td>java.vm.specification.version</td> <td>VM specification version</td>    <td>{@code 0.9}</td></tr>
    387      * <tr><td>java.vm.specification.vendor</td>  <td>VM specification vendor</td>     <td>{@code The Android Project}</td></tr>
    388      * <tr><td>java.vm.specification.name</td>    <td>VM specification name</td>       <td>{@code Dalvik Virtual Machine Specification}</td></tr>
    389      *
    390      * <tr><td>line.separator</td>     <td>The system line separator</td>         <td>{@code \n}</td></tr>
    391      *
    392      * <tr><td>os.arch</td>            <td>OS architecture</td>                   <td>{@code armv7l}</td></tr>
    393      * <tr><td>os.name</td>            <td>OS (kernel) name</td>                  <td>{@code Linux}</td></tr>
    394      * <tr><td>os.version</td>         <td>OS (kernel) version</td>               <td>{@code 2.6.32.9-g103d848}</td></tr>
    395      *
    396      * <tr><td>path.separator</td>     <td>See {@link java.io.File#pathSeparator}</td> <td>{@code :}</td></tr>
    397      *
    398      * <tr><td>user.dir</td>           <td>Base of non-absolute paths</td>        <td>{@code /}</td></tr>
    399      * <tr><td>user.home</td>          <td>(Not useful on Android)</td>           <td>Empty</td></tr>
    400      * <tr><td>user.name</td>          <td>(Not useful on Android)</td>           <td>Empty</td></tr>
    401      *
    402      * </table>
    403      *
    404      * <p>It is a mistake to try to override any of these. Doing so will have unpredictable results.
    405      *
    406      * @param propertyName
    407      *            the name of the system property to look up.
    408      * @return the value of the specified system property or {@code null} if the
    409      *         property doesn't exist.
    410      */
    411     public static String getProperty(String propertyName) {
    412         return getProperty(propertyName, null);
    413     }
    414 
    415     /**
    416      * Returns the value of a particular system property. The {@code
    417      * defaultValue} will be returned if no such property has been found.
    418      */
    419     public static String getProperty(String name, String defaultValue) {
    420         checkPropertyName(name);
    421         return getProperties().getProperty(name, defaultValue);
    422     }
    423 
    424     /**
    425      * Sets the value of a particular system property.
    426      *
    427      * @return the old value of the property or {@code null} if the property
    428      *         didn't exist.
    429      */
    430     public static String setProperty(String name, String value) {
    431         checkPropertyName(name);
    432         return (String) getProperties().setProperty(name, value);
    433     }
    434 
    435     /**
    436      * Removes a specific system property.
    437      *
    438      * @return the property value or {@code null} if the property didn't exist.
    439      * @throws NullPointerException
    440      *             if the argument is {@code null}.
    441      * @throws IllegalArgumentException
    442      *             if the argument is empty.
    443      */
    444     public static String clearProperty(String name) {
    445         checkPropertyName(name);
    446         return (String) getProperties().remove(name);
    447     }
    448 
    449     private static void checkPropertyName(String name) {
    450         if (name == null) {
    451             throw new NullPointerException("name == null");
    452         }
    453         if (name.isEmpty()) {
    454             throw new IllegalArgumentException("name is empty");
    455         }
    456     }
    457 
    458     /**
    459      * Returns the {@link java.io.Console} associated with this VM, or null.
    460      * Not all VMs will have an associated console. A console is typically only
    461      * available for programs run from the command line.
    462      * @since 1.6
    463      */
    464     public static Console console() {
    465         return Console.getConsole();
    466     }
    467 
    468     /**
    469      * Returns null. Android does not use {@code SecurityManager}. This method
    470      * is only provided for source compatibility.
    471      *
    472      * @return null
    473      */
    474     public static SecurityManager getSecurityManager() {
    475         return null;
    476     }
    477 
    478     /**
    479      * Returns an integer hash code for the parameter. The hash code returned is
    480      * the same one that would be returned by the method {@code
    481      * java.lang.Object.hashCode()}, whether or not the object's class has
    482      * overridden hashCode(). The hash code for {@code null} is {@code 0}.
    483      *
    484      * @param anObject
    485      *            the object to calculate the hash code.
    486      * @return the hash code for the given object.
    487      * @see java.lang.Object#hashCode
    488      */
    489     public static native int identityHashCode(Object anObject);
    490 
    491     /**
    492      * Returns the system's line separator. On Android, this is {@code "\n"}. The value
    493      * comes from the value of the {@code line.separator} system property when the VM
    494      * starts. Later changes to the property will not affect the value returned by this
    495      * method.
    496      * @since 1.7
    497      */
    498     public static String lineSeparator() {
    499         return lineSeparator;
    500     }
    501 
    502     /**
    503      * Loads and links the dynamic library that is identified through the
    504      * specified path. This method is similar to {@link #loadLibrary(String)},
    505      * but it accepts a full path specification whereas {@code loadLibrary} just
    506      * accepts the name of the library to load.
    507      *
    508      * @param pathName
    509      *            the path of the file to be loaded.
    510      */
    511     public static void load(String pathName) {
    512         Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());
    513     }
    514 
    515     /**
    516      * Loads and links the library with the specified name. The mapping of the
    517      * specified library name to the full path for loading the library is
    518      * implementation-dependent.
    519      *
    520      * @param libName
    521      *            the name of the library to load.
    522      * @throws UnsatisfiedLinkError
    523      *             if the library could not be loaded.
    524      */
    525     public static void loadLibrary(String libName) {
    526         Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
    527     }
    528 
    529     /**
    530      * @hide internal use only
    531      */
    532     public static void logE(String message) {
    533         log('E', message, null);
    534     }
    535 
    536     /**
    537      * @hide internal use only
    538      */
    539     public static void logE(String message, Throwable th) {
    540         log('E', message, th);
    541     }
    542 
    543     /**
    544      * @hide internal use only
    545      */
    546     public static void logI(String message) {
    547         log('I', message, null);
    548     }
    549 
    550     /**
    551      * @hide internal use only
    552      */
    553     public static void logI(String message, Throwable th) {
    554         log('I', message, th);
    555     }
    556 
    557     /**
    558      * @hide internal use only
    559      */
    560     public static void logW(String message) {
    561         log('W', message, null);
    562     }
    563 
    564     /**
    565      * @hide internal use only
    566      */
    567     public static void logW(String message, Throwable th) {
    568         log('W', message, th);
    569     }
    570 
    571     private static native void log(char type, String message, Throwable th);
    572 
    573     /**
    574      * Provides a hint to the VM that it would be useful to attempt
    575      * to perform any outstanding object finalization.
    576      */
    577     public static void runFinalization() {
    578         Runtime.getRuntime().runFinalization();
    579     }
    580 
    581     /**
    582      * Ensures that, when the VM is about to exit, all objects are
    583      * finalized. Note that all finalization which occurs when the system is
    584      * exiting is performed after all running threads have been terminated.
    585      *
    586      * @param flag
    587      *            the flag determines if finalization on exit is enabled.
    588      * @deprecated This method is unsafe.
    589      */
    590     @SuppressWarnings("deprecation")
    591     @Deprecated
    592     public static void runFinalizersOnExit(boolean flag) {
    593         Runtime.runFinalizersOnExit(flag);
    594     }
    595 
    596     /**
    597      * Sets all system properties. This does not take a copy; the passed-in object is used
    598      * directly. Passing null causes the VM to reinitialize the properties to how they were
    599      * when the VM was started.
    600      */
    601     public static void setProperties(Properties p) {
    602         systemProperties = p;
    603     }
    604 
    605     /**
    606      * Throws {@code SecurityException}.
    607      *
    608      * <p>Security managers do <i>not</i> provide a secure environment for
    609      * executing untrusted code and are unsupported on Android. Untrusted code
    610      * cannot be safely isolated within a single VM on Android, so this method
    611      * <i>always</i> throws a {@code SecurityException}.
    612      *
    613      * @param sm a security manager
    614      * @throws SecurityException always
    615      */
    616     public static void setSecurityManager(SecurityManager sm) {
    617         if (sm != null) {
    618             throw new SecurityException();
    619         }
    620     }
    621 
    622     /**
    623      * Returns the platform specific file name format for the shared library
    624      * named by the argument.
    625      *
    626      * @param userLibName
    627      *            the name of the library to look up.
    628      * @return the platform specific filename for the library.
    629      */
    630     public static native String mapLibraryName(String userLibName);
    631 
    632     /**
    633      * Sets the value of the named static field in the receiver to the passed in
    634      * argument.
    635      *
    636      * @param fieldName
    637      *            the name of the field to set, one of in, out, or err
    638      * @param stream
    639      *            the new value of the field
    640      */
    641     private static native void setFieldImpl(String fieldName, String signature, Object stream);
    642 
    643 
    644     /**
    645      * The unmodifiable environment variables map. System.getenv() specifies
    646      * that this map must throw when passed non-String keys.
    647      */
    648     static class SystemEnvironment extends AbstractMap<String, String> {
    649         private final Map<String, String> map;
    650 
    651         public SystemEnvironment(Map<String, String> map) {
    652             this.map = Collections.unmodifiableMap(map);
    653         }
    654 
    655         @Override public Set<Entry<String, String>> entrySet() {
    656             return map.entrySet();
    657         }
    658 
    659         @Override public String get(Object key) {
    660             return map.get(toNonNullString(key));
    661         }
    662 
    663         @Override public boolean containsKey(Object key) {
    664             return map.containsKey(toNonNullString(key));
    665         }
    666 
    667         @Override public boolean containsValue(Object value) {
    668             return map.containsValue(toNonNullString(value));
    669         }
    670 
    671         private String toNonNullString(Object o) {
    672             if (o == null) {
    673                 throw new NullPointerException("o == null");
    674             }
    675             return (String) o;
    676         }
    677     }
    678 }
    679