Save This Page
Home » openjdk-7 » java » lang » [javadoc | source]
    1   /*
    2    * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   /* We use APIs that access a so-called Windows "Environment Block",
   27    * which looks like an array of jchars like this:
   28    *
   29    * FOO=BAR\u0000 ... GORP=QUUX\u0000\u0000
   30    *
   31    * This data structure has a number of peculiarities we must contend with:
   32    * (see: http://windowssdk.msdn.microsoft.com/en-us/library/ms682009.aspx)
   33    * - The NUL jchar separators, and a double NUL jchar terminator.
   34    *   It appears that the Windows implementation requires double NUL
   35    *   termination even if the environment is empty.  We should always
   36    *   generate environments with double NUL termination, while accepting
   37    *   empty environments consisting of a single NUL.
   38    * - on Windows9x, this is actually an array of 8-bit chars, not jchars,
   39    *   encoded in the system default encoding.
   40    * - The block must be sorted by Unicode value, case-insensitively,
   41    *   as if folded to upper case.
   42    * - There are magic environment variables maintained by Windows
   43    *   that start with a `=' (!) character.  These are used for
   44    *   Windows drive current directory (e.g. "=C:=C:\WINNT") or the
   45    *   exit code of the last command (e.g. "=ExitCode=0000001").
   46    *
   47    * Since Java and non-9x Windows speak the same character set, and
   48    * even the same encoding, we don't have to deal with unreliable
   49    * conversion to byte streams.  Just add a few NUL terminators.
   50    *
   51    * System.getenv(String) is case-insensitive, while System.getenv()
   52    * returns a map that is case-sensitive, which is consistent with
   53    * native Windows APIs.
   54    *
   55    * The non-private methods in this class are not for general use even
   56    * within this package.  Instead, they are the system-dependent parts
   57    * of the system-independent method of the same name.  Don't even
   58    * think of using this class unless your method's name appears below.
   59    *
   60    * @author Martin Buchholz
   61    * @since 1.5
   62    */
   63   
   64   package java.lang;
   65   
   66   import java.io;
   67   import java.util;
   68   
   69   final class ProcessEnvironment extends HashMap<String,String>
   70   {
   71       private static String validateName(String name) {
   72           // An initial `=' indicates a magic Windows variable name -- OK
   73           if (name.indexOf('=', 1)   != -1 ||
   74               name.indexOf('\u0000') != -1)
   75               throw new IllegalArgumentException
   76                   ("Invalid environment variable name: \"" + name + "\"");
   77           return name;
   78       }
   79   
   80       private static String validateValue(String value) {
   81           if (value.indexOf('\u0000') != -1)
   82               throw new IllegalArgumentException
   83                   ("Invalid environment variable value: \"" + value + "\"");
   84           return value;
   85       }
   86   
   87       private static String nonNullString(Object o) {
   88           if (o == null)
   89               throw new NullPointerException();
   90           return (String) o;
   91       }
   92   
   93       public String put(String key, String value) {
   94           return super.put(validateName(key), validateValue(value));
   95       }
   96   
   97       public String get(Object key) {
   98           return super.get(nonNullString(key));
   99       }
  100   
  101       public boolean containsKey(Object key) {
  102           return super.containsKey(nonNullString(key));
  103       }
  104   
  105       public boolean containsValue(Object value) {
  106           return super.containsValue(nonNullString(value));
  107       }
  108   
  109       public String remove(Object key) {
  110           return super.remove(nonNullString(key));
  111       }
  112   
  113       private static class CheckedEntry
  114           implements Map.Entry<String,String>
  115       {
  116           private final Map.Entry<String,String> e;
  117           public CheckedEntry(Map.Entry<String,String> e) {this.e = e;}
  118           public String getKey()   { return e.getKey();}
  119           public String getValue() { return e.getValue();}
  120           public String setValue(String value) {
  121               return e.setValue(validateValue(value));
  122           }
  123           public String toString() { return getKey() + "=" + getValue();}
  124           public boolean equals(Object o) {return e.equals(o);}
  125           public int hashCode()    {return e.hashCode();}
  126       }
  127   
  128       private static class CheckedEntrySet
  129           extends AbstractSet<Map.Entry<String,String>>
  130       {
  131           private final Set<Map.Entry<String,String>> s;
  132           public CheckedEntrySet(Set<Map.Entry<String,String>> s) {this.s = s;}
  133           public int size()        {return s.size();}
  134           public boolean isEmpty() {return s.isEmpty();}
  135           public void clear()      {       s.clear();}
  136           public Iterator<Map.Entry<String,String>> iterator() {
  137               return new Iterator<Map.Entry<String,String>>() {
  138                   Iterator<Map.Entry<String,String>> i = s.iterator();
  139                   public boolean hasNext() { return i.hasNext();}
  140                   public Map.Entry<String,String> next() {
  141                       return new CheckedEntry(i.next());
  142                   }
  143                   public void remove() { i.remove();}
  144               };
  145           }
  146           private static Map.Entry<String,String> checkedEntry (Object o) {
  147               Map.Entry<String,String> e = (Map.Entry<String,String>) o;
  148               nonNullString(e.getKey());
  149               nonNullString(e.getValue());
  150               return e;
  151           }
  152           public boolean contains(Object o) {return s.contains(checkedEntry(o));}
  153           public boolean remove(Object o)   {return s.remove(checkedEntry(o));}
  154       }
  155   
  156       private static class CheckedValues extends AbstractCollection<String> {
  157           private final Collection<String> c;
  158           public CheckedValues(Collection<String> c) {this.c = c;}
  159           public int size()                  {return c.size();}
  160           public boolean isEmpty()           {return c.isEmpty();}
  161           public void clear()                {       c.clear();}
  162           public Iterator<String> iterator() {return c.iterator();}
  163           public boolean contains(Object o)  {return c.contains(nonNullString(o));}
  164           public boolean remove(Object o)    {return c.remove(nonNullString(o));}
  165       }
  166   
  167       private static class CheckedKeySet extends AbstractSet<String> {
  168           private final Set<String> s;
  169           public CheckedKeySet(Set<String> s) {this.s = s;}
  170           public int size()                  {return s.size();}
  171           public boolean isEmpty()           {return s.isEmpty();}
  172           public void clear()                {       s.clear();}
  173           public Iterator<String> iterator() {return s.iterator();}
  174           public boolean contains(Object o)  {return s.contains(nonNullString(o));}
  175           public boolean remove(Object o)    {return s.remove(nonNullString(o));}
  176       }
  177   
  178       public Set<String> keySet() {
  179           return new CheckedKeySet(super.keySet());
  180       }
  181   
  182       public Collection<String> values() {
  183           return new CheckedValues(super.values());
  184       }
  185   
  186       public Set<Map.Entry<String,String>> entrySet() {
  187           return new CheckedEntrySet(super.entrySet());
  188       }
  189   
  190   
  191       private static final class NameComparator
  192           implements Comparator<String> {
  193           public int compare(String s1, String s2) {
  194               // We can't use String.compareToIgnoreCase since it
  195               // canonicalizes to lower case, while Windows
  196               // canonicalizes to upper case!  For example, "_" should
  197               // sort *after* "Z", not before.
  198               int n1 = s1.length();
  199               int n2 = s2.length();
  200               int min = Math.min(n1, n2);
  201               for (int i = 0; i < min; i++) {
  202                   char c1 = s1.charAt(i);
  203                   char c2 = s2.charAt(i);
  204                   if (c1 != c2) {
  205                       c1 = Character.toUpperCase(c1);
  206                       c2 = Character.toUpperCase(c2);
  207                       if (c1 != c2)
  208                           // No overflow because of numeric promotion
  209                           return c1 - c2;
  210                   }
  211               }
  212               return n1 - n2;
  213           }
  214       }
  215   
  216       private static final class EntryComparator
  217           implements Comparator<Map.Entry<String,String>> {
  218           public int compare(Map.Entry<String,String> e1,
  219                              Map.Entry<String,String> e2) {
  220               return nameComparator.compare(e1.getKey(), e2.getKey());
  221           }
  222       }
  223   
  224       // Allow `=' as first char in name, e.g. =C:=C:\DIR
  225       static final int MIN_NAME_LENGTH = 1;
  226   
  227       private static final NameComparator nameComparator;
  228       private static final EntryComparator entryComparator;
  229       private static final ProcessEnvironment theEnvironment;
  230       private static final Map<String,String> theUnmodifiableEnvironment;
  231       private static final Map<String,String> theCaseInsensitiveEnvironment;
  232   
  233       static {
  234           nameComparator  = new NameComparator();
  235           entryComparator = new EntryComparator();
  236           theEnvironment  = new ProcessEnvironment();
  237           theUnmodifiableEnvironment
  238               = Collections.unmodifiableMap(theEnvironment);
  239   
  240           String envblock = environmentBlock();
  241           int beg, end, eql;
  242           for (beg = 0;
  243                ((end = envblock.indexOf('\u0000', beg  )) != -1 &&
  244                 // An initial `=' indicates a magic Windows variable name -- OK
  245                 (eql = envblock.indexOf('='     , beg+1)) != -1);
  246                beg = end + 1) {
  247               // Ignore corrupted environment strings.
  248               if (eql < end)
  249                   theEnvironment.put(envblock.substring(beg, eql),
  250                                      envblock.substring(eql+1,end));
  251           }
  252   
  253           theCaseInsensitiveEnvironment
  254               = new TreeMap<String,String>(nameComparator);
  255           theCaseInsensitiveEnvironment.putAll(theEnvironment);
  256       }
  257   
  258       private ProcessEnvironment() {
  259           super();
  260       }
  261   
  262       private ProcessEnvironment(int capacity) {
  263           super(capacity);
  264       }
  265   
  266       // Only for use by System.getenv(String)
  267       static String getenv(String name) {
  268           // The original implementation used a native call to _wgetenv,
  269           // but it turns out that _wgetenv is only consistent with
  270           // GetEnvironmentStringsW (for non-ASCII) if `wmain' is used
  271           // instead of `main', even in a process created using
  272           // CREATE_UNICODE_ENVIRONMENT.  Instead we perform the
  273           // case-insensitive comparison ourselves.  At least this
  274           // guarantees that System.getenv().get(String) will be
  275           // consistent with System.getenv(String).
  276           return theCaseInsensitiveEnvironment.get(name);
  277       }
  278   
  279       // Only for use by System.getenv()
  280       static Map<String,String> getenv() {
  281           return theUnmodifiableEnvironment;
  282       }
  283   
  284       // Only for use by ProcessBuilder.environment()
  285       static Map<String,String> environment() {
  286           return (Map<String,String>) theEnvironment.clone();
  287       }
  288   
  289       // Only for use by Runtime.exec(...String[]envp...)
  290       static Map<String,String> emptyEnvironment(int capacity) {
  291           return new ProcessEnvironment(capacity);
  292       }
  293   
  294       private static native String environmentBlock();
  295   
  296       // Only for use by ProcessImpl.start()
  297       String toEnvironmentBlock() {
  298           // Sort Unicode-case-insensitively by name
  299           List<Map.Entry<String,String>> list
  300               = new ArrayList<Map.Entry<String,String>>(entrySet());
  301           Collections.sort(list, entryComparator);
  302   
  303           StringBuilder sb = new StringBuilder(size()*30);
  304           for (Map.Entry<String,String> e : list)
  305               sb.append(e.getKey())
  306                 .append('=')
  307                 .append(e.getValue())
  308                 .append('\u0000');
  309           // Ensure double NUL termination,
  310           // even if environment is empty.
  311           if (sb.length() == 0)
  312               sb.append('\u0000');
  313           sb.append('\u0000');
  314           return sb.toString();
  315       }
  316   
  317       static String toEnvironmentBlock(Map<String,String> map) {
  318           return map == null ? null :
  319               ((ProcessEnvironment)map).toEnvironmentBlock();
  320       }
  321   }

Save This Page
Home » openjdk-7 » java » lang » [javadoc | source]