001: /*
002: * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.java.util.jar.pack;
027:
028: import java.util.*;
029: import java.util.jar.*;
030: import java.util.zip.*;
031: import java.io.*;
032: import java.beans.PropertyChangeListener;
033: import java.beans.PropertyChangeEvent;
034:
035: /*
036: * Implementation of the Pack provider.
037: * </pre></blockquote>
038: * @author John Rose
039: * @author Kumar Srinivasan
040: * @version 1.10, 05/05/07
041: */
042:
043: public class UnpackerImpl implements Pack200.Unpacker {
044:
045: /**
046: * Register a listener for changes to options.
047: * @param listener An object to be invoked when a property is changed.
048: */
049: public void addPropertyChangeListener(
050: PropertyChangeListener listener) {
051: _props.addListener(listener);
052: }
053:
054: /**
055: * Remove a listener for the PropertyChange event.
056: * @param listener The PropertyChange listener to be removed.
057: */
058: public void removePropertyChangeListener(
059: PropertyChangeListener listener) {
060: _props.removeListener(listener);
061: }
062:
063: public UnpackerImpl() {
064: _props = new PropMap();
065: //_props.getProperty() consults defaultProps invisibly.
066: //_props.putAll(defaultProps);
067: }
068:
069: // Private stuff.
070: final PropMap _props;
071:
072: /**
073: * Get the set of options for the pack and unpack engines.
074: * @return A sorted association of option key strings to option values.
075: */
076: public SortedMap properties() {
077: return _props;
078: }
079:
080: // Back-pointer to NativeUnpacker, when active.
081: Object _nunp;
082:
083: public String toString() {
084: return Utils.getVersionString();
085: }
086:
087: //Driver routines
088:
089: // The unpack worker...
090: /**
091: * Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally
092: * the entire buffer must be read, it may be more efficient to read the packed-stream
093: * to a file and pass the File object, in the alternate method described below.
094: * <p>
095: * Closes its input but not its output. (The output can accumulate more elements.)
096: * @param in an InputStream.
097: * @param out a JarOutputStream.
098: * @exception IOException if an error is encountered.
099: */
100: public void unpack(InputStream in0, JarOutputStream out)
101: throws IOException {
102: assert (Utils.currentInstance.get() == null);
103: TimeZone tz = (_props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null
104: : TimeZone.getDefault();
105:
106: try {
107: Utils.currentInstance.set(this );
108: if (tz != null)
109: TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
110: final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE);
111: BufferedInputStream in = new BufferedInputStream(in0);
112: if (Utils.isJarMagic(Utils.readMagic(in))) {
113: if (verbose > 0)
114: Utils.log.info("Copying unpacked JAR file...");
115: Utils.copyJarFile(new JarInputStream(in), out);
116: } else if (_props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) {
117: (new DoUnpack()).run(in, out);
118: in.close();
119: Utils.markJarFile(out);
120: } else {
121: (new NativeUnpack(this )).run(in, out);
122: in.close();
123: Utils.markJarFile(out);
124: }
125: } finally {
126: _nunp = null;
127: Utils.currentInstance.set(null);
128: if (tz != null)
129: TimeZone.setDefault(tz);
130: }
131: }
132:
133: /**
134: * Takes an input File containing the pack file, and generates a JarOutputStream.
135: * <p>
136: * Does not close its output. (The output can accumulate more elements.)
137: * @param in a File.
138: * @param out a JarOutputStream.
139: * @exception IOException if an error is encountered.
140: */
141: public void unpack(File in, JarOutputStream out) throws IOException {
142: // Use the stream-based implementation.
143: // %%% Reconsider if native unpacker learns to memory-map the file.
144: FileInputStream instr = new FileInputStream(in);
145: unpack(instr, out);
146: if (_props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) {
147: in.delete();
148: }
149: }
150:
151: private class DoUnpack {
152: final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE);
153:
154: {
155: _props.setInteger(Pack200.Unpacker.PROGRESS, 0);
156: }
157:
158: // Here's where the bits are read from disk:
159: final Package pkg = new Package();
160:
161: final boolean keepModtime = Pack200.Packer.KEEP.equals(_props
162: .getProperty(Utils.UNPACK_MODIFICATION_TIME,
163: Pack200.Packer.KEEP));
164: final boolean keepDeflateHint = Pack200.Packer.KEEP
165: .equals(_props.getProperty(
166: Pack200.Unpacker.DEFLATE_HINT,
167: Pack200.Packer.KEEP));
168: final int modtime;
169: final boolean deflateHint;
170: {
171: if (!keepModtime) {
172: modtime = _props
173: .getTime(Utils.UNPACK_MODIFICATION_TIME);
174: } else {
175: modtime = pkg.default_modtime;
176: }
177:
178: deflateHint = (keepDeflateHint) ? false
179: : _props
180: .getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT);
181: }
182:
183: // Checksum apparatus.
184: final CRC32 crc = new CRC32();
185: final ByteArrayOutputStream bufOut = new ByteArrayOutputStream();
186: final OutputStream crcOut = new CheckedOutputStream(bufOut, crc);
187:
188: public void run(BufferedInputStream in, JarOutputStream out)
189: throws IOException {
190: if (verbose > 0) {
191: _props.list(System.out);
192: }
193: for (int seg = 1;; seg++) {
194: unpackSegment(in, out);
195:
196: // Try to get another segment.
197: if (!Utils.isPackMagic(Utils.readMagic(in)))
198: break;
199: if (verbose > 0)
200: Utils.log.info("Finished segment #" + seg);
201: }
202: }
203:
204: private void unpackSegment(InputStream in, JarOutputStream out)
205: throws IOException {
206: _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,
207: "0");
208: // Process the output directory or jar output.
209: new PackageReader(pkg, in).read();
210:
211: if (_props.getBoolean("unpack.strip.debug"))
212: pkg.stripAttributeKind("Debug");
213: if (_props.getBoolean("unpack.strip.compile"))
214: pkg.stripAttributeKind("Compile");
215: _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,
216: "50");
217: pkg.ensureAllClassFiles();
218: // Now write out the files.
219: HashSet classesToWrite = new HashSet(pkg.getClasses());
220: for (Iterator i = pkg.getFiles().iterator(); i.hasNext();) {
221: Package.File file = (Package.File) i.next();
222: String name = file.nameString;
223: JarEntry je = new JarEntry(Utils.getJarEntryName(name));
224: boolean deflate;
225:
226: deflate = (keepDeflateHint) ? (((file.options & Constants.FO_DEFLATE_HINT) != 0) || ((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0))
227: : deflateHint;
228:
229: boolean needCRC = !deflate; // STORE mode requires CRC
230:
231: if (needCRC)
232: crc.reset();
233: bufOut.reset();
234: if (file.isClassStub()) {
235: Package.Class cls = file.getStubClass();
236: assert (cls != null);
237: new ClassWriter(cls, needCRC ? crcOut : bufOut)
238: .write();
239: classesToWrite.remove(cls); // for an error check
240: } else {
241: // collect data & maybe CRC
242: file.writeTo(needCRC ? crcOut : bufOut);
243: }
244: je.setMethod(deflate ? JarEntry.DEFLATED
245: : JarEntry.STORED);
246: if (needCRC) {
247: if (verbose > 0)
248: Utils.log.info("stored size=" + bufOut.size()
249: + " and crc=" + crc.getValue());
250:
251: je.setMethod(JarEntry.STORED);
252: je.setSize(bufOut.size());
253: je.setCrc(crc.getValue());
254: }
255: if (keepModtime) {
256: je.setTime(file.modtime);
257: // Convert back to milliseconds
258: je.setTime((long) file.modtime * 1000);
259: } else {
260: je.setTime((long) modtime * 1000);
261: }
262: out.putNextEntry(je);
263: bufOut.writeTo(out);
264: out.closeEntry();
265: if (verbose > 0)
266: Utils.log.info("Writing "
267: + Utils.zeString((ZipEntry) je));
268: }
269: assert (classesToWrite.isEmpty());
270: _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,
271: "100");
272: pkg.reset(); // reset for the next segment, if any
273: }
274: }
275: }
|