1/*
2 * Copyright (c) 1996, 2015, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*****************************************************************************/
27/*                    Copyright (c) IBM Corporation 1998                     */
28/*                                                                           */
29/* (C) Copyright IBM Corp. 1998                                              */
30/*                                                                           */
31/*****************************************************************************/
32
33package sun.rmi.rmic;
34
35import java.io.File;
36import java.io.IOException;
37import java.io.OutputStream;
38import java.util.Collection;
39import java.util.Enumeration;
40import java.util.Iterator;
41import java.util.LinkedHashSet;
42import java.util.StringTokenizer;
43import java.util.Vector;
44import java.util.jar.JarFile;
45import java.util.jar.Manifest;
46import java.util.jar.Attributes;
47import sun.tools.java.ClassPath;
48
49/**
50 * BatchEnvironment for rmic extends javac's version in four ways:
51 * 1. It overrides errorString() to handle looking for rmic-specific
52 * error messages in rmic's resource bundle
53 * 2. It provides a mechanism for recording intermediate generated
54 * files so that they can be deleted later.
55 * 3. It holds a reference to the Main instance so that generators
56 * can refer to it.
57 * 4. It provides access to the ClassPath passed to the constructor.
58 *
59 * WARNING: The contents of this source file are not part of any
60 * supported API.  Code that depends on them does so at its own risk:
61 * they are subject to change or removal without notice.
62 */
63
64@SuppressWarnings("deprecation")
65public class BatchEnvironment extends sun.tools.javac.BatchEnvironment {
66
67    /** instance of Main which created this environment */
68    private Main main;
69
70    /**
71     * Create a ClassPath object for rmic from a class path string.
72     */
73    public static ClassPath createClassPath(String classPathString) {
74        ClassPath[] paths = classPaths(null, classPathString, null);
75        return paths[1];
76    }
77
78    /**
79     * Create a ClassPath object for rmic from the relevant command line
80     * options for class path and boot class path.
81     */
82    public static ClassPath createClassPath(String classPathString,
83                                            String sysClassPathString)
84    {
85        /**
86         * Previously, this method delegated to the
87         * sun.tools.javac.BatchEnvironment.classPaths method in order
88         * to supply default values for paths not specified on the
89         * command line, expand extensions directories into specific
90         * JAR files, and construct the ClassPath object-- but as part
91         * of the fix for 6473331, which adds support for Class-Path
92         * manifest entries in JAR files, those steps are now handled
93         * here directly, with the help of a Path utility class copied
94         * from the new javac implementation (see below).
95         */
96        Path path = new Path();
97
98        if (sysClassPathString == null) {
99            sysClassPathString = System.getProperty("sun.boot.class.path");
100        }
101        if (sysClassPathString != null) {
102            path.addFiles(sysClassPathString);
103        }
104
105        /*
106         * Class-Path manifest entries are supported for JAR files
107         * everywhere except in the boot class path.
108         */
109        path.expandJarClassPaths(true);
110
111        /*
112         * In the application class path, an empty element means
113         * the current working directory.
114         */
115        path.emptyPathDefault(".");
116
117        if (classPathString == null) {
118            // The env.class.path property is the user's CLASSPATH
119            // environment variable, and it set by the wrapper (ie,
120            // javac.exe).
121            classPathString = System.getProperty("env.class.path");
122            if (classPathString == null) {
123                classPathString = ".";
124            }
125        }
126        path.addFiles(classPathString);
127
128        return new ClassPath(path.toArray(new String[path.size()]));
129    }
130
131    /**
132     * Create a BatchEnvironment for rmic with the given class path,
133     * stream for messages and Main.
134     */
135    public BatchEnvironment(OutputStream out, ClassPath path, Main main) {
136        super(out, new ClassPath(""), path);
137                                // use empty "sourcePath" (see 4666958)
138        this.main = main;
139    }
140
141    /**
142     * Get the instance of Main which created this environment.
143     */
144    public Main getMain() {
145        return main;
146    }
147
148    /**
149     * Get the ClassPath.
150     */
151    public ClassPath getClassPath() {
152        return binaryPath;
153    }
154
155    /** list of generated source files created in this environment */
156    private Vector<File> generatedFiles = new Vector<>();
157
158    /**
159     * Remember a generated source file generated so that it
160     * can be removed later, if appropriate.
161     */
162    public void addGeneratedFile(File file) {
163        generatedFiles.addElement(file);
164    }
165
166    /**
167     * Delete all the generated source files made during the execution
168     * of this environment (those that have been registered with the
169     * "addGeneratedFile" method).
170     */
171    public void deleteGeneratedFiles() {
172        synchronized(generatedFiles) {
173            Enumeration<File> enumeration = generatedFiles.elements();
174            while (enumeration.hasMoreElements()) {
175                File file = enumeration.nextElement();
176                file.delete();
177            }
178            generatedFiles.removeAllElements();
179        }
180    }
181
182    /**
183     * Release resources, if any.
184     */
185    public void shutdown() {
186        main = null;
187        generatedFiles = null;
188        super.shutdown();
189    }
190
191    /**
192     * Return the formatted, localized string for a named error message
193     * and supplied arguments.  For rmic error messages, with names that
194     * being with "rmic.", look up the error message in rmic's resource
195     * bundle; otherwise, defer to java's superclass method.
196     */
197    public String errorString(String err,
198                              Object arg0, Object arg1, Object arg2)
199    {
200        if (err.startsWith("rmic.") || err.startsWith("warn.rmic.")) {
201            String result =  Main.getText(err,
202                                          (arg0 != null ? arg0.toString() : null),
203                                          (arg1 != null ? arg1.toString() : null),
204                                          (arg2 != null ? arg2.toString() : null));
205
206            if (err.startsWith("warn.")) {
207                result = "warning: " + result;
208            }
209            return result;
210        } else {
211            return super.errorString(err, arg0, arg1, arg2);
212        }
213    }
214    public void reset() {
215    }
216
217    /**
218     * Utility for building paths of directories and JAR files.  This
219     * class was copied from com.sun.tools.javac.util.Paths as part of
220     * the fix for 6473331, which adds support for Class-Path manifest
221     * entries in JAR files.  Diagnostic code is simply commented out
222     * because rmic silently ignored these conditions historically.
223     */
224    private static class Path extends LinkedHashSet<String> {
225        private static final long serialVersionUID = 0;
226        private static final boolean warn = false;
227
228        private static class PathIterator implements Collection<String> {
229            private int pos = 0;
230            private final String path;
231            private final String emptyPathDefault;
232
233            public PathIterator(String path, String emptyPathDefault) {
234                this.path = path;
235                this.emptyPathDefault = emptyPathDefault;
236            }
237            public PathIterator(String path) { this(path, null); }
238            public Iterator<String> iterator() {
239                return new Iterator<String>() {
240                    public boolean hasNext() {
241                        return pos <= path.length();
242                    }
243                    public String next() {
244                        int beg = pos;
245                        int end = path.indexOf(File.pathSeparator, beg);
246                        if (end == -1)
247                            end = path.length();
248                        pos = end + 1;
249
250                        if (beg == end && emptyPathDefault != null)
251                            return emptyPathDefault;
252                        else
253                            return path.substring(beg, end);
254                    }
255                    public void remove() {
256                        throw new UnsupportedOperationException();
257                    }
258                };
259            }
260
261            // required for Collection.
262            public int size() {
263                throw new UnsupportedOperationException();
264            }
265            public boolean isEmpty() {
266                throw new UnsupportedOperationException();
267            }
268            public boolean contains(Object o) {
269                throw new UnsupportedOperationException();
270            }
271            public Object[] toArray() {
272                throw new UnsupportedOperationException();
273            }
274            public <T> T[] toArray(T[] a) {
275                throw new UnsupportedOperationException();
276            }
277            public boolean add(String o) {
278                throw new UnsupportedOperationException();
279            }
280            public boolean remove(Object o) {
281                throw new UnsupportedOperationException();
282            }
283            public boolean containsAll(Collection<?> c) {
284                throw new UnsupportedOperationException();
285            }
286            public boolean addAll(Collection<? extends String> c) {
287                throw new UnsupportedOperationException();
288            }
289            public boolean removeAll(Collection<?> c) {
290                throw new UnsupportedOperationException();
291            }
292            public boolean retainAll(Collection<?> c) {
293                throw new UnsupportedOperationException();
294            }
295            public void clear() {
296                throw new UnsupportedOperationException();
297            }
298            public boolean equals(Object o) {
299                throw new UnsupportedOperationException();
300            }
301            public int hashCode() {
302                throw new UnsupportedOperationException();
303            }
304        }
305
306        /** Is this the name of a zip file? */
307        private static boolean isZip(String name) {
308            return new File(name).isFile();
309        }
310
311        private boolean expandJarClassPaths = false;
312
313        public Path expandJarClassPaths(boolean x) {
314            expandJarClassPaths = x;
315            return this;
316        }
317
318        /** What to use when path element is the empty string */
319        private String emptyPathDefault = null;
320
321        public Path emptyPathDefault(String x) {
322            emptyPathDefault = x;
323            return this;
324        }
325
326        public Path() { super(); }
327
328        public Path addDirectories(String dirs, boolean warn) {
329            if (dirs != null)
330                for (String dir : new PathIterator(dirs))
331                    addDirectory(dir, warn);
332            return this;
333        }
334
335        public Path addDirectories(String dirs) {
336            return addDirectories(dirs, warn);
337        }
338
339        private void addDirectory(String dir, boolean warn) {
340            if (! new File(dir).isDirectory()) {
341//              if (warn)
342//                  log.warning(Position.NOPOS,
343//                              "dir.path.element.not.found", dir);
344                return;
345            }
346
347            for (String direntry : new File(dir).list()) {
348                String canonicalized = direntry.toLowerCase();
349                if (canonicalized.endsWith(".jar") ||
350                    canonicalized.endsWith(".zip"))
351                    addFile(dir + File.separator + direntry, warn);
352            }
353        }
354
355        public Path addFiles(String files, boolean warn) {
356            if (files != null)
357                for (String file : new PathIterator(files, emptyPathDefault))
358                    addFile(file, warn);
359            return this;
360        }
361
362        public Path addFiles(String files) {
363            return addFiles(files, warn);
364        }
365
366        private void addFile(String file, boolean warn) {
367            if (contains(file)) {
368                /* Discard duplicates and avoid infinite recursion */
369                return;
370            }
371
372            File ele = new File(file);
373            if (! ele.exists()) {
374                /* No such file or directory exist */
375                if (warn)
376//                      log.warning(Position.NOPOS,
377//                          "path.element.not.found", file);
378                    return;
379            }
380
381            if (ele.isFile()) {
382                /* File is an ordinay file  */
383                String arcname = file.toLowerCase();
384                if (! (arcname.endsWith(".zip") ||
385                       arcname.endsWith(".jar"))) {
386                    /* File name don't have right extension */
387//                      if (warn)
388//                          log.warning(Position.NOPOS,
389//                              "invalid.archive.file", file);
390                    return;
391                }
392            }
393
394            /* Now what we have left is either a directory or a file name
395               confirming to archive naming convention */
396
397            super.add(file);
398            if (expandJarClassPaths && isZip(file))
399                addJarClassPath(file, warn);
400        }
401
402        // Adds referenced classpath elements from a jar's Class-Path
403        // Manifest entry.  In some future release, we may want to
404        // update this code to recognize URLs rather than simple
405        // filenames, but if we do, we should redo all path-related code.
406        private void addJarClassPath(String jarFileName, boolean warn) {
407            try {
408                String jarParent = new File(jarFileName).getParent();
409                JarFile jar = new JarFile(jarFileName);
410
411                try {
412                    Manifest man = jar.getManifest();
413                    if (man == null) return;
414
415                    Attributes attr = man.getMainAttributes();
416                    if (attr == null) return;
417
418                    String path = attr.getValue(Attributes.Name.CLASS_PATH);
419                    if (path == null) return;
420
421                    for (StringTokenizer st = new StringTokenizer(path);
422                        st.hasMoreTokens();) {
423                        String elt = st.nextToken();
424                        if (jarParent != null)
425                            elt = new File(jarParent, elt).getCanonicalPath();
426                        addFile(elt, warn);
427                    }
428                } finally {
429                    jar.close();
430                }
431            } catch (IOException e) {
432//              log.error(Position.NOPOS,
433//                        "error.reading.file", jarFileName,
434//                        e.getLocalizedMessage());
435            }
436        }
437    }
438}
439