KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > lang > ProcessEnvironment


1 /*
2  * @(#)ProcessEnvironment.java 1.7 06/12/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 /* We use APIs that access a so-called Windows "Environment Block",
9  * which looks like an array of jchars like this:
10  *
11  * FOO=BAR ... GORP=QUUX
12  *
13  * This data structure has a number of peculiarities we must contend with:
14  * - The NUL jchar separators, and a double NUL jchar terminator.
15  * It appears that the Windows implementation requires double NUL
16  * termination even if the environment is empty. We should always
17  * generate environments with double NUL termination, while accepting
18  * empty environments consisting of a single NUL.
19  * - on Windows9x, this is actually an array of 8-bit chars, not jchars,
20  * encoded in the system default encoding.
21  * - The block must be sorted by Unicode value, case-insensitively.
22  * - There are magic environment variables maintained by Windows
23  * that start with a `=' (!) character. These are used for
24  * Windows drive current directory (e.g. "=C:=C:\WINNT") or the
25  * exit code of the last command (e.g. "=ExitCode=0000001").
26  *
27  * Since Java and non-9x Windows speak the same character set, and
28  * even the same encoding, we don't have to deal with unreliable
29  * conversion to byte streams. Just add a few NUL terminators.
30  *
31  * System.getenv(String) is case-insensitive, while System.getenv()
32  * returns a map that is case-sensitive, which is consistent with
33  * native Windows APIs.
34  *
35  * The non-private methods in this class are not for general use even
36  * within this package. Instead, they are the system-dependent parts
37  * of the system-independent method of the same name. Don't even
38  * think of using this class unless your method's name appears below.
39  *
40  * @author Martin Buchholz
41  * @version 1.7, 06/12/05
42  * @since 1.5
43  */

44
45 package java.lang;
46
47 import java.io.*;
48 import java.util.*;
49
50 final class ProcessEnvironment extends HashMap<String JavaDoc,String JavaDoc>
51 {
52     private static String JavaDoc validateName(String JavaDoc name) {
53     // An initial `=' indicates a magic Windows variable name -- OK
54
if (name.indexOf('=', 1) != -1 ||
55         name.indexOf('\u0000') != -1)
56         throw new IllegalArgumentException JavaDoc
57         ("Invalid environment variable name: \"" + name + "\"");
58     return name;
59     }
60
61     private static String JavaDoc validateValue(String JavaDoc value) {
62     if (value.indexOf('\u0000') != -1)
63         throw new IllegalArgumentException JavaDoc
64         ("Invalid environment variable value: \"" + value + "\"");
65     return value;
66     }
67
68     private static String JavaDoc nonNullString(Object JavaDoc o) {
69     if (o == null)
70         throw new NullPointerException JavaDoc();
71     return (String JavaDoc) o;
72     }
73
74     public String JavaDoc put(String JavaDoc key, String JavaDoc value) {
75     return super.put(validateName(key), validateValue(value));
76     }
77
78     public String JavaDoc get(Object JavaDoc key) {
79     return super.get(nonNullString(key));
80     }
81
82     public boolean containsKey(Object JavaDoc key) {
83     return super.containsKey(nonNullString(key));
84     }
85
86     public boolean containsValue(Object JavaDoc value) {
87     return super.containsValue(nonNullString(value));
88     }
89
90     public String JavaDoc remove(Object JavaDoc key) {
91     return super.remove(nonNullString(key));
92     }
93
94     private static class CheckedEntry
95     implements Map.Entry<String JavaDoc,String JavaDoc>
96     {
97     private final Map.Entry<String JavaDoc,String JavaDoc> e;
98     public CheckedEntry(Map.Entry<String JavaDoc,String JavaDoc> e) {this.e = e;}
99     public String JavaDoc getKey() { return e.getKey();}
100     public String JavaDoc getValue() { return e.getValue();}
101     public String JavaDoc setValue(String JavaDoc value) {
102         return e.setValue(validateValue(value));
103     }
104     public String JavaDoc toString() { return getKey() + "=" + getValue();}
105     public boolean equals(Object JavaDoc o) {return e.equals(o);}
106     public int hashCode() {return e.hashCode();}
107     }
108
109     private static class CheckedEntrySet
110     extends AbstractSet<Map.Entry<String JavaDoc,String JavaDoc>>
111     {
112     private final Set<Map.Entry<String JavaDoc,String JavaDoc>> s;
113     public CheckedEntrySet(Set<Map.Entry<String JavaDoc,String JavaDoc>> s) {this.s = s;}
114     public int size() {return s.size();}
115     public boolean isEmpty() {return s.isEmpty();}
116     public void clear() { s.clear();}
117     public Iterator<Map.Entry<String JavaDoc,String JavaDoc>> iterator() {
118         return new Iterator<Map.Entry<String JavaDoc,String JavaDoc>>() {
119         Iterator<Map.Entry<String JavaDoc,String JavaDoc>> i = s.iterator();
120         public boolean hasNext() { return i.hasNext();}
121         public Map.Entry<String JavaDoc,String JavaDoc> next() {
122             return new CheckedEntry(i.next());
123         }
124         public void remove() { i.remove();}
125         };
126     }
127     private static Map.Entry<String JavaDoc,String JavaDoc> checkedEntry (Object JavaDoc o) {
128         Map.Entry<String JavaDoc,String JavaDoc> e = (Map.Entry<String JavaDoc,String JavaDoc>) o;
129         nonNullString(e.getKey());
130         nonNullString(e.getValue());
131         return e;
132     }
133     public boolean contains(Object JavaDoc o) {return s.contains(checkedEntry(o));}
134     public boolean remove(Object JavaDoc o) {return s.remove(checkedEntry(o));}
135     }
136
137     private static class CheckedValues extends AbstractCollection<String JavaDoc> {
138     private final Collection<String JavaDoc> c;
139     public CheckedValues(Collection<String JavaDoc> c) {this.c = c;}
140     public int size() {return c.size();}
141     public boolean isEmpty() {return c.isEmpty();}
142     public void clear() { c.clear();}
143     public Iterator<String JavaDoc> iterator() {return c.iterator();}
144     public boolean contains(Object JavaDoc o) {return c.contains(nonNullString(o));}
145     public boolean remove(Object JavaDoc o) {return c.remove(nonNullString(o));}
146     }
147
148     private static class CheckedKeySet extends AbstractSet<String JavaDoc> {
149     private final Set<String JavaDoc> s;
150     public CheckedKeySet(Set<String JavaDoc> s) {this.s = s;}
151     public int size() {return s.size();}
152     public boolean isEmpty() {return s.isEmpty();}
153     public void clear() { s.clear();}
154     public Iterator<String JavaDoc> iterator() {return s.iterator();}
155     public boolean contains(Object JavaDoc o) {return s.contains(nonNullString(o));}
156     public boolean remove(Object JavaDoc o) {return s.remove(nonNullString(o));}
157     }
158
159     public Set<String JavaDoc> keySet() {
160     return new CheckedKeySet(super.keySet());
161     }
162
163     public Collection<String JavaDoc> values() {
164     return new CheckedValues(super.values());
165     }
166
167     public Set<Map.Entry<String JavaDoc,String JavaDoc>> entrySet() {
168     return new CheckedEntrySet(super.entrySet());
169     }
170
171
172     private static final class NameComparator
173     implements Comparator<String JavaDoc> {
174     public int compare(String JavaDoc s1, String JavaDoc s2) {
175         // We can't use String.compareToIgnoreCase since it
176
// canonicalizes to lower case, while Windows
177
// canonicalizes to upper case! For example, "_" should
178
// sort *after* "Z", not before.
179
int n1 = s1.length();
180         int n2 = s2.length();
181         int min = Math.min(n1, n2);
182             for (int i = 0; i < min; i++) {
183                 char c1 = s1.charAt(i);
184                 char c2 = s2.charAt(i);
185                 if (c1 != c2) {
186             c1 = Character.toUpperCase(c1);
187             c2 = Character.toUpperCase(c2);
188             if (c1 != c2)
189             return c1 - c2;
190                 }
191             }
192             return n1 - n2;
193     }
194     }
195
196     private static final class EntryComparator
197     implements Comparator<Map.Entry<String JavaDoc,String JavaDoc>> {
198     public int compare(Map.Entry<String JavaDoc,String JavaDoc> e1,
199                Map.Entry<String JavaDoc,String JavaDoc> e2) {
200         return nameComparator.compare(e1.getKey(), e2.getKey());
201     }
202     }
203
204     // Allow `=' as first char in name, e.g. =C:=C:\DIR
205
static final int MIN_NAME_LENGTH = 1;
206
207     private static final NameComparator nameComparator;
208     private static final EntryComparator entryComparator;
209     private static final ProcessEnvironment JavaDoc theEnvironment;
210     private static final Map<String JavaDoc,String JavaDoc> theUnmodifiableEnvironment;
211     private static final Map<String JavaDoc,String JavaDoc> theCaseInsensitiveEnvironment;
212
213     static {
214     nameComparator = new NameComparator();
215     entryComparator = new EntryComparator();
216     theEnvironment = new ProcessEnvironment JavaDoc();
217     theUnmodifiableEnvironment
218         = Collections.unmodifiableMap(theEnvironment);
219
220     String JavaDoc envblock = environmentBlock();
221     int beg, end, eql;
222     for (beg = 0;
223          ((end = envblock.indexOf('\u0000', beg )) != -1 &&
224           // An initial `=' indicates a magic Windows variable name -- OK
225
(eql = envblock.indexOf('=' , beg+1)) != -1);
226          beg = end + 1) {
227         // Ignore corrupted environment strings.
228
if (eql < end)
229         theEnvironment.put(envblock.substring(beg, eql),
230                    envblock.substring(eql+1,end));
231     }
232
233     theCaseInsensitiveEnvironment
234         = new TreeMap<String JavaDoc,String JavaDoc>(nameComparator);
235     theCaseInsensitiveEnvironment.putAll(theEnvironment);
236     }
237
238     private ProcessEnvironment() {
239     super();
240     }
241
242     private ProcessEnvironment(int capacity) {
243     super(capacity);
244     }
245
246     // Only for use by System.getenv(String)
247
static String JavaDoc getenv(String JavaDoc name) {
248     // The original implementation used a native call to _wgetenv,
249
// but it turns out that _wgetenv is only consistent with
250
// GetEnvironmentStringsW (for non-ASCII) if `wmain' is used
251
// instead of `main', even in a process created using
252
// CREATE_UNICODE_ENVIRONMENT. Instead we perform the
253
// case-insensitive comparison ourselves. At least this
254
// guarantees that System.getenv().get(String) will be
255
// consistent with System.getenv(String).
256
return theCaseInsensitiveEnvironment.get(name);
257     }
258
259     // Only for use by System.getenv()
260
static Map<String JavaDoc,String JavaDoc> getenv() {
261     return theUnmodifiableEnvironment;
262     }
263
264     // Only for use by ProcessBuilder.environment()
265
static Map<String JavaDoc,String JavaDoc> environment() {
266     return (Map<String JavaDoc,String JavaDoc>) theEnvironment.clone();
267     }
268
269     // Only for use by Runtime.exec(...String[]envp...)
270
static Map<String JavaDoc,String JavaDoc> emptyEnvironment(int capacity) {
271     return new ProcessEnvironment JavaDoc(capacity);
272     }
273
274     private static native String JavaDoc environmentBlock();
275
276     // Only for use by ProcessImpl.start()
277
String JavaDoc toEnvironmentBlock() {
278     // Sort Unicode-case-insensitively by name
279
List<Map.Entry<String JavaDoc,String JavaDoc>> list
280         = new ArrayList<Map.Entry<String JavaDoc,String JavaDoc>>(entrySet());
281     Collections.sort(list, entryComparator);
282
283     StringBuilder JavaDoc sb = new StringBuilder JavaDoc(size()*30);
284     for (Map.Entry<String JavaDoc,String JavaDoc> e : list)
285         sb.append(e.getKey())
286           .append('=')
287           .append(e.getValue())
288           .append('\u0000');
289     // Ensure double NUL termination,
290
// even if environment is empty.
291
if (sb.length() == 0)
292         sb.append('\u0000');
293     sb.append('\u0000');
294     return sb.toString();
295     }
296
297     static String JavaDoc toEnvironmentBlock(Map<String JavaDoc,String JavaDoc> map) {
298     return map == null ? null :
299         ((ProcessEnvironment JavaDoc)map).toEnvironmentBlock();
300     }
301 }
302
Popular Tags