1 jls.loader.provide('jls.util.zip.ZipFile');
  2 
  3 jls.loader.require('jls.util.zip.ZipEntry');
  4 jls.loader.require('jls.util.zip.Inflater');
  5 jls.loader.require('jls.io.File');
  6 jls.loader.require('jls.io.FileChannel');
  7 jls.loader.require('jls.lang.Struct');
  8 
  9 jls.util.zip.ZipFile = jls.lang.Class.create( /** @lends jls.util.zip.ZipFile.prototype */
 10 {
 11     /**
 12      * Creates a zip file.
 13      *
 14      * @param {jls.io.File} file The file .
 15      * @constructs
 16 	 * @class This class represents a zip file.
 17      */
 18     initialize : function(file) {
 19         this._buffer = jls.lang.ByteBuffer.allocate(1024);
 20         var buffer = jls.lang.ByteBuffer.allocate(64);
 21         buffer.setByteOrder(jls.lang.Buffer.LITTLE_ENDIAN);
 22 
 23         this._endOfCentralDirectoryRecord = new jls.lang.Struct(jls.util.zip.ZipEntry.getEndOfCentralDirectoryRecordStructDefinition(), buffer); // 22
 24         this._dataDescriptor = new jls.lang.Struct(jls.util.zip.ZipEntry.getDataDescriptorStructDefinition(), buffer); // 12
 25         this._localFileHeader = new jls.lang.Struct(jls.util.zip.ZipEntry.getLocalFileHeaderStructDefinition(), buffer); // 30
 26         this._fileHeader = new jls.lang.Struct(jls.util.zip.ZipEntry.getFileHeaderStructDefinition(), buffer); // 46
 27 
 28         this._file = file;
 29         this._channel = new jls.io.FileChannel(this._file);
 30         this._entryCount = 0;
 31         this._entries = {};
 32         
 33         try {
 34             //this._channel.seek(this._file.size() - this._endOfCentralDirectoryRecord.size());
 35             this._channel.seek(-this._endOfCentralDirectoryRecord.size(), jls.io.FileDescriptor.SEEK_END);
 36 
 37             this._readStruct(this._endOfCentralDirectoryRecord, jls.util.zip.ZipEntry.END_CENTRAL_DIR_SIGNATURE, 'End Of Central Directory Record');
 38             this._entryCount = this._endOfCentralDirectoryRecord.get('entryCount');
 39             jls.logger.debug('entry count: ' + this._entryCount);
 40 
 41             this._channel.seek(this._endOfCentralDirectoryRecord.get('offset'));
 42             for (var i = 0; i < this._entryCount; i++) {
 43                 this._readStruct(this._fileHeader, jls.util.zip.ZipEntry.FILE_HEADER_SIGNATURE, 'File Header');
 44                 var filename = this._readString(this._fileHeader.get('filenameLength'));
 45                 this._channel.seek(this._fileHeader.get('extraFieldLength'), jls.io.FileDescriptor.SEEK_CUR);
 46                 var comment = this._readString(this._fileHeader.get('fileCommentLength'));
 47                 jls.logger.debug('entry: "' + filename + '"');
 48                 this._entries[filename] = new jls.util.zip.ZipEntry(filename, comment, null, this._fileHeader);
 49             }
 50         }
 51         catch(e) {
 52             this.close();
 53             throw e;
 54         }
 55     },
 56     _readString : function(length) {
 57         this._buffer.clear();
 58         this._buffer.setLimit(length);
 59         this._channel.read(this._buffer);
 60         if (this._buffer.remaining() != 0) {
 61             throw new jls.lang.Exception('Unable to read string');
 62         }
 63         this._buffer.flip();
 64         return this._buffer.getString('UTF-8');
 65     },
 66     _readStruct : function(str, signature, name) {
 67         name = name || 'structure';
 68         str.clear();
 69         this._channel.read(str.buffer());
 70         if (str.buffer().remaining() != 0) {
 71             throw new jls.lang.Exception('Unable to read ' + name);
 72         }
 73         var sig = str.get('signature');
 74         jls.logger.debug('signature: 0x' + sig.toString(16));
 75         if (sig != signature) {
 76             throw new jls.lang.Exception('Invalid ' + name + ' signature (0x' + sig.toString(16) + ' != 0x' + signature.toString(16) + ')');
 77         }
 78     },
 79     /**
 80      * Closes the zip file.
 81      *
 82      */
 83     close : function() {
 84         this._channel.close();
 85         this._entryCount = 0;
 86         this._entries = {};
 87     },
 88     /**
 89      * Returns the zip entry count.
 90      *
 91      * @returns {Number} The zip entry count.
 92      */
 93     size : function() {
 94         return this._entryCount;
 95     },
 96     /**
 97      * Returns an entry depending on the specified name.
 98      *
 99      * @param {String} name The entry name.
100      * @returns {jls.util.zip.ZipEntry} The zip entry or null.
101      */
102     getEntry : function(name) {
103         if (name in this._entries) {
104             return this._entries[name];
105         }
106         return null;
107     },
108     /**
109      * Tells if this zip file contains a specified entry name.
110      *
111      * @param {String} name The entry name.
112      * @returns {Boolean} true if this zip file contains a specified entry name.
113      */
114     hasEntry : function(name) {
115         return name in this._entries;
116     },
117     /**
118      * Returns a byte buffer containing the uncompressed entry content.
119      *
120      * @param {String} name The entry name.
121      * @returns {jls.lang.ByteBuffer} The uncompressed entry content.
122      */
123     getEntryContent : function(name) {
124         var entry = this.getEntry(name);
125         if (entry == null) {
126             throw new jls.lang.Exception('Entry not found');
127         }
128         if (entry.getMethod() != jls.util.zip.ZipEntry.COMPRESSION_METHOD_DEFLATED) {
129             throw new jls.lang.Exception('Invalid method (' + entry.getMethod() + ')');
130         }
131         this._channel.seek(entry.getOffset(), jls.io.FileDescriptor.SEEK_SET);
132         this._readStruct(this._localFileHeader, jls.util.zip.ZipEntry.LOCAL_FILE_HEADER_SIGNATURE, 'Local File Header');
133         this._channel.seek(this._localFileHeader.get('filenameLength') + this._localFileHeader.get('extraFieldLength'), jls.io.FileDescriptor.SEEK_CUR);
134         var compressedSize = this._localFileHeader.get('compressedSize');
135         var uncompressedSize = this._localFileHeader.get('uncompressedSize');
136         jls.logger.debug('compressedSize: ' + compressedSize);
137         jls.logger.debug('uncompressedSize: ' + uncompressedSize);
138         var inputBuffer = jls.lang.ByteBuffer.allocate(compressedSize);
139         var outputBuffer = jls.lang.ByteBuffer.allocate(uncompressedSize);
140         
141         var inflater = new jls.util.zip.Inflater();
142         this._channel.read(inputBuffer);
143         inputBuffer.flip();
144         inflater.setInput(inputBuffer);
145         inflater.inflate(outputBuffer);
146         inflater.end();
147         
148         outputBuffer.flip();
149         inputBuffer.free();
150         return outputBuffer;
151     },
152     getEntryContentAsString : function(name, csn) {
153         var content = this.getEntryContent(name);
154         return content.getString((typeof csn != 'undefined') ? csn : 'UTF-8');
155     },
156     getInputStream : function(entry) {
157         return null;
158     },
159     /**
160      * Returns the zip entries.
161      *
162      * @returns {Array} The zip entries.
163      */
164     entries : function() {
165         var list = [];
166         for (var name in this._entries) {
167             list.push(this._entries[name])
168         }
169         return list;
170     },
171     /**
172      * Returns the file name.
173      *
174      * @returns {String} The file name.
175      */
176     getName : function() {
177         return this._file.getName();
178     }
179 });
180 
181