1 |
/* java.util.zip.Inflater |
/* Inflater.java - Decompress a data stream |
2 |
Copyright (C) 2001 Free Software Foundation, Inc. |
Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
7 |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
8 |
the Free Software Foundation; either version 2, or (at your option) |
the Free Software Foundation; either version 2, or (at your option) |
9 |
any later version. |
any later version. |
10 |
|
|
11 |
GNU Classpath is distributed in the hope that it will be useful, but |
GNU Classpath is distributed in the hope that it will be useful, but |
12 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
37 |
|
|
38 |
package java.util.zip; |
package java.util.zip; |
39 |
|
|
40 |
|
/* Written using on-line Java Platform 1.2 API Specification |
41 |
|
* and JCL book. |
42 |
|
* Believed complete and correct. |
43 |
|
*/ |
44 |
|
|
45 |
/** |
/** |
46 |
* Inflater is used to decompress data that has been compressed according |
* Inflater is used to decompress data that has been compressed according |
47 |
* to the "deflate" standard described in rfc1950. |
* to the "deflate" standard described in rfc1950. |
62 |
* needed at a later stage. |
* needed at a later stage. |
63 |
* |
* |
64 |
* @author John Leuner, Jochen Hoenicke |
* @author John Leuner, Jochen Hoenicke |
65 |
|
* @author Tom Tromey |
66 |
|
* @date May 17, 1999 |
67 |
* @since JDK 1.1 |
* @since JDK 1.1 |
68 |
*/ |
*/ |
69 |
public class Inflater |
public class Inflater |
163 |
/** |
/** |
164 |
* Creates a new inflater. |
* Creates a new inflater. |
165 |
*/ |
*/ |
166 |
public Inflater() |
public Inflater () |
167 |
{ |
{ |
168 |
this(false); |
this (false); |
169 |
} |
} |
170 |
|
|
171 |
/** |
/** |
172 |
* Creates a new inflater. |
* Creates a new inflater. |
173 |
* @param nowrap true if no header and checksum field appears in the |
* @param nowrap true if no header and checksum field appears in the |
175 |
* Sun JDK you should provide one byte of input more than needed in |
* Sun JDK you should provide one byte of input more than needed in |
176 |
* this case. |
* this case. |
177 |
*/ |
*/ |
178 |
public Inflater(boolean nowrap) |
public Inflater (boolean nowrap) |
179 |
{ |
{ |
180 |
this.nowrap = nowrap; |
this.nowrap = nowrap; |
181 |
this.adler = new Adler32(); |
this.adler = new Adler32(); |
185 |
} |
} |
186 |
|
|
187 |
/** |
/** |
188 |
|
* Finalizes this object. |
189 |
|
*/ |
190 |
|
protected void finalize () |
191 |
|
{ |
192 |
|
/* Exists only for compatibility */ |
193 |
|
} |
194 |
|
|
195 |
|
/** |
196 |
|
* Frees all objects allocated by the inflater. There's no reason |
197 |
|
* to call this, since you can just rely on garbage collection (even |
198 |
|
* for the Sun implementation). Exists only for compatibility |
199 |
|
* with Sun's JDK, where the compressor allocates native memory. |
200 |
|
* If you call any method (even reset) afterwards the behaviour is |
201 |
|
* <i>undefined</i>. |
202 |
|
* @deprecated Just clear all references to inflater instead. |
203 |
|
*/ |
204 |
|
public void end () |
205 |
|
{ |
206 |
|
outputWindow = null; |
207 |
|
input = null; |
208 |
|
dynHeader = null; |
209 |
|
litlenTree = null; |
210 |
|
distTree = null; |
211 |
|
adler = null; |
212 |
|
} |
213 |
|
|
214 |
|
/** |
215 |
|
* Returns true, if the inflater has finished. This means, that no |
216 |
|
* input is needed and no output can be produced. |
217 |
|
*/ |
218 |
|
public boolean finished() |
219 |
|
{ |
220 |
|
return mode == FINISHED && outputWindow.getAvailable() == 0; |
221 |
|
} |
222 |
|
|
223 |
|
/** |
224 |
|
* Gets the adler checksum. This is either the checksum of all |
225 |
|
* uncompressed bytes returned by inflate(), or if needsDictionary() |
226 |
|
* returns true (and thus no output was yet produced) this is the |
227 |
|
* adler checksum of the expected dictionary. |
228 |
|
* @returns the adler checksum. |
229 |
|
*/ |
230 |
|
public int getAdler() |
231 |
|
{ |
232 |
|
return needsDictionary() ? readAdler : (int) adler.getValue(); |
233 |
|
} |
234 |
|
|
235 |
|
/** |
236 |
|
* Gets the number of unprocessed input. Useful, if the end of the |
237 |
|
* stream is reached and you want to further process the bytes after |
238 |
|
* the deflate stream. |
239 |
|
* @return the number of bytes of the input which were not processed. |
240 |
|
*/ |
241 |
|
public int getRemaining() |
242 |
|
{ |
243 |
|
return input.getAvailableBytes(); |
244 |
|
} |
245 |
|
|
246 |
|
/** |
247 |
|
* Gets the total number of processed compressed input bytes. |
248 |
|
* @return the total number of bytes of processed input bytes. |
249 |
|
*/ |
250 |
|
public int getTotalIn() |
251 |
|
{ |
252 |
|
return totalIn - getRemaining(); |
253 |
|
} |
254 |
|
|
255 |
|
/** |
256 |
|
* Gets the total number of output bytes returned by inflate(). |
257 |
|
* @return the total number of output bytes. |
258 |
|
*/ |
259 |
|
public int getTotalOut() |
260 |
|
{ |
261 |
|
return totalOut; |
262 |
|
} |
263 |
|
|
264 |
|
/** |
265 |
|
* Inflates the compressed stream to the output buffer. If this |
266 |
|
* returns 0, you should check, whether needsDictionary(), |
267 |
|
* needsInput() or finished() returns true, to determine why no |
268 |
|
* further output is produced. |
269 |
|
* @param buffer the output buffer. |
270 |
|
* @return the number of bytes written to the buffer, 0 if no further |
271 |
|
* output can be produced. |
272 |
|
* @exception DataFormatException if deflated stream is invalid. |
273 |
|
* @exception IllegalArgumentException if buf has length 0. |
274 |
|
*/ |
275 |
|
public int inflate (byte[] buf) throws DataFormatException |
276 |
|
{ |
277 |
|
return inflate (buf, 0, buf.length); |
278 |
|
} |
279 |
|
|
280 |
|
/** |
281 |
|
* Inflates the compressed stream to the output buffer. If this |
282 |
|
* returns 0, you should check, whether needsDictionary(), |
283 |
|
* needsInput() or finished() returns true, to determine why no |
284 |
|
* further output is produced. |
285 |
|
* @param buffer the output buffer. |
286 |
|
* @param off the offset into buffer where the output should start. |
287 |
|
* @param len the maximum length of the output. |
288 |
|
* @return the number of bytes written to the buffer, 0 if no further |
289 |
|
* output can be produced. |
290 |
|
* @exception DataFormatException if deflated stream is invalid. |
291 |
|
* @exception IndexOutOfBoundsException if the off and/or len are wrong. |
292 |
|
*/ |
293 |
|
public int inflate (byte[] buf, int off, int len) throws DataFormatException |
294 |
|
{ |
295 |
|
/* Special case: len may be zero */ |
296 |
|
if (len == 0) |
297 |
|
return 0; |
298 |
|
/* Check for correct buff, off, len triple */ |
299 |
|
if (0 > off || off > off + len || off + len > buf.length) |
300 |
|
throw new ArrayIndexOutOfBoundsException(); |
301 |
|
int count = 0; |
302 |
|
int more; |
303 |
|
do |
304 |
|
{ |
305 |
|
if (mode != DECODE_CHKSUM) |
306 |
|
{ |
307 |
|
/* Don't give away any output, if we are waiting for the |
308 |
|
* checksum in the input stream. |
309 |
|
* |
310 |
|
* With this trick we have always: |
311 |
|
* needsInput() and not finished() |
312 |
|
* implies more output can be produced. |
313 |
|
*/ |
314 |
|
more = outputWindow.copyOutput(buf, off, len); |
315 |
|
adler.update(buf, off, more); |
316 |
|
off += more; |
317 |
|
count += more; |
318 |
|
totalOut += more; |
319 |
|
len -= more; |
320 |
|
if (len == 0) |
321 |
|
return count; |
322 |
|
} |
323 |
|
} |
324 |
|
while (decode() || (outputWindow.getAvailable() > 0 |
325 |
|
&& mode != DECODE_CHKSUM)); |
326 |
|
return count; |
327 |
|
} |
328 |
|
|
329 |
|
/** |
330 |
|
* Returns true, if a preset dictionary is needed to inflate the input. |
331 |
|
*/ |
332 |
|
public boolean needsDictionary () |
333 |
|
{ |
334 |
|
return mode == DECODE_DICT && neededBits == 0; |
335 |
|
} |
336 |
|
|
337 |
|
/** |
338 |
|
* Returns true, if the input buffer is empty. |
339 |
|
* You should then call setInput(). <br> |
340 |
|
* |
341 |
|
* <em>NOTE</em>: This method also returns true when the stream is finished. |
342 |
|
*/ |
343 |
|
public boolean needsInput () |
344 |
|
{ |
345 |
|
return input.needsInput (); |
346 |
|
} |
347 |
|
|
348 |
|
/** |
349 |
* Resets the inflater so that a new stream can be decompressed. All |
* Resets the inflater so that a new stream can be decompressed. All |
350 |
* pending input and output will be discarded. |
* pending input and output will be discarded. |
351 |
*/ |
*/ |
352 |
public void reset() |
public void reset () |
353 |
{ |
{ |
354 |
mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; |
mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; |
355 |
totalIn = totalOut = 0; |
totalIn = totalOut = 0; |
363 |
} |
} |
364 |
|
|
365 |
/** |
/** |
366 |
|
* Sets the preset dictionary. This should only be called, if |
367 |
|
* needsDictionary() returns true and it should set the same |
368 |
|
* dictionary, that was used for deflating. The getAdler() |
369 |
|
* function returns the checksum of the dictionary needed. |
370 |
|
* @param buffer the dictionary. |
371 |
|
* @exception IllegalStateException if no dictionary is needed. |
372 |
|
* @exception IllegalArgumentException if the dictionary checksum is |
373 |
|
* wrong. |
374 |
|
*/ |
375 |
|
public void setDictionary (byte[] buffer) |
376 |
|
{ |
377 |
|
setDictionary(buffer, 0, buffer.length); |
378 |
|
} |
379 |
|
|
380 |
|
/** |
381 |
|
* Sets the preset dictionary. This should only be called, if |
382 |
|
* needsDictionary() returns true and it should set the same |
383 |
|
* dictionary, that was used for deflating. The getAdler() |
384 |
|
* function returns the checksum of the dictionary needed. |
385 |
|
* @param buffer the dictionary. |
386 |
|
* @param off the offset into buffer where the dictionary starts. |
387 |
|
* @param len the length of the dictionary. |
388 |
|
* @exception IllegalStateException if no dictionary is needed. |
389 |
|
* @exception IllegalArgumentException if the dictionary checksum is |
390 |
|
* wrong. |
391 |
|
* @exception IndexOutOfBoundsException if the off and/or len are wrong. |
392 |
|
*/ |
393 |
|
public void setDictionary (byte[] buffer, int off, int len) |
394 |
|
{ |
395 |
|
if (!needsDictionary()) |
396 |
|
throw new IllegalStateException(); |
397 |
|
|
398 |
|
adler.update(buffer, off, len); |
399 |
|
if ((int) adler.getValue() != readAdler) |
400 |
|
throw new IllegalArgumentException("Wrong adler checksum"); |
401 |
|
adler.reset(); |
402 |
|
outputWindow.copyDict(buffer, off, len); |
403 |
|
mode = DECODE_BLOCKS; |
404 |
|
} |
405 |
|
|
406 |
|
/** |
407 |
|
* Sets the input. This should only be called, if needsInput() |
408 |
|
* returns true. |
409 |
|
* @param buffer the input. |
410 |
|
* @exception IllegalStateException if no input is needed. |
411 |
|
*/ |
412 |
|
public void setInput (byte[] buf) |
413 |
|
{ |
414 |
|
setInput (buf, 0, buf.length); |
415 |
|
} |
416 |
|
|
417 |
|
/** |
418 |
|
* Sets the input. This should only be called, if needsInput() |
419 |
|
* returns true. |
420 |
|
* @param buffer the input. |
421 |
|
* @param off the offset into buffer where the input starts. |
422 |
|
* @param len the length of the input. |
423 |
|
* @exception IllegalStateException if no input is needed. |
424 |
|
* @exception IndexOutOfBoundsException if the off and/or len are wrong. |
425 |
|
*/ |
426 |
|
public void setInput (byte[] buf, int off, int len) |
427 |
|
{ |
428 |
|
input.setInput (buf, off, len); |
429 |
|
totalIn += len; |
430 |
|
} |
431 |
|
|
432 |
|
/** |
433 |
* Decodes the deflate header. |
* Decodes the deflate header. |
434 |
* @return false if more input is needed. |
* @return false if more input is needed. |
435 |
* @exception DataFormatException if header is invalid. |
* @exception DataFormatException if header is invalid. |
436 |
*/ |
*/ |
437 |
private boolean decodeHeader() throws DataFormatException |
private boolean decodeHeader () throws DataFormatException |
438 |
{ |
{ |
439 |
int header = input.peekBits(16); |
int header = input.peekBits(16); |
440 |
if (header < 0) |
if (header < 0) |
472 |
* Decodes the dictionary checksum after the deflate header. |
* Decodes the dictionary checksum after the deflate header. |
473 |
* @return false if more input is needed. |
* @return false if more input is needed. |
474 |
*/ |
*/ |
475 |
private boolean decodeDict() |
private boolean decodeDict () |
476 |
{ |
{ |
477 |
while (neededBits > 0) |
while (neededBits > 0) |
478 |
{ |
{ |
492 |
* full or the current block ends. |
* full or the current block ends. |
493 |
* @exception DataFormatException if deflated stream is invalid. |
* @exception DataFormatException if deflated stream is invalid. |
494 |
*/ |
*/ |
495 |
private boolean decodeHuffman() throws DataFormatException |
private boolean decodeHuffman () throws DataFormatException |
496 |
{ |
{ |
497 |
int free = outputWindow.getFreeSpace(); |
int free = outputWindow.getFreeSpace(); |
498 |
while (free >= 258) |
while (free >= 258) |
584 |
* @return false if more input is needed. |
* @return false if more input is needed. |
585 |
* @exception DataFormatException if checksum doesn't match. |
* @exception DataFormatException if checksum doesn't match. |
586 |
*/ |
*/ |
587 |
private boolean decodeChksum() throws DataFormatException |
private boolean decodeChksum () throws DataFormatException |
588 |
{ |
{ |
589 |
while (neededBits > 0) |
while (neededBits > 0) |
590 |
{ |
{ |
608 |
* @return false if more input is needed, or if finished. |
* @return false if more input is needed, or if finished. |
609 |
* @exception DataFormatException if deflated stream is invalid. |
* @exception DataFormatException if deflated stream is invalid. |
610 |
*/ |
*/ |
611 |
private boolean decode() throws DataFormatException |
private boolean decode () throws DataFormatException |
612 |
{ |
{ |
613 |
switch (mode) |
switch (mode) |
614 |
{ |
{ |
712 |
throw new IllegalStateException(); |
throw new IllegalStateException(); |
713 |
} |
} |
714 |
} |
} |
|
|
|
|
/** |
|
|
* Sets the preset dictionary. This should only be called, if |
|
|
* needsDictionary() returns true and it should set the same |
|
|
* dictionary, that was used for deflating. The getAdler() |
|
|
* function returns the checksum of the dictionary needed. |
|
|
* @param buffer the dictionary. |
|
|
* @exception IllegalStateException if no dictionary is needed. |
|
|
* @exception IllegalArgumentException if the dictionary checksum is |
|
|
* wrong. |
|
|
*/ |
|
|
public void setDictionary(byte[] buffer) |
|
|
{ |
|
|
setDictionary(buffer, 0, buffer.length); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Sets the preset dictionary. This should only be called, if |
|
|
* needsDictionary() returns true and it should set the same |
|
|
* dictionary, that was used for deflating. The getAdler() |
|
|
* function returns the checksum of the dictionary needed. |
|
|
* @param buffer the dictionary. |
|
|
* @param off the offset into buffer where the dictionary starts. |
|
|
* @param len the length of the dictionary. |
|
|
* @exception IllegalStateException if no dictionary is needed. |
|
|
* @exception IllegalArgumentException if the dictionary checksum is |
|
|
* wrong. |
|
|
* @exception IndexOutOfBoundsException if the off and/or len are wrong. |
|
|
*/ |
|
|
public void setDictionary(byte[] buffer, int off, int len) |
|
|
{ |
|
|
if (!needsDictionary()) |
|
|
throw new IllegalStateException(); |
|
|
|
|
|
adler.update(buffer, off, len); |
|
|
if ((int) adler.getValue() != readAdler) |
|
|
throw new IllegalArgumentException("Wrong adler checksum"); |
|
|
adler.reset(); |
|
|
outputWindow.copyDict(buffer, off, len); |
|
|
mode = DECODE_BLOCKS; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Sets the input. This should only be called, if needsInput() |
|
|
* returns true. |
|
|
* @param buffer the input. |
|
|
* @exception IllegalStateException if no input is needed. |
|
|
*/ |
|
|
public void setInput(byte[] buf) |
|
|
{ |
|
|
setInput(buf, 0, buf.length); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Sets the input. This should only be called, if needsInput() |
|
|
* returns true. |
|
|
* @param buffer the input. |
|
|
* @param off the offset into buffer where the input starts. |
|
|
* @param len the length of the input. |
|
|
* @exception IllegalStateException if no input is needed. |
|
|
* @exception IndexOutOfBoundsException if the off and/or len are wrong. |
|
|
*/ |
|
|
public void setInput(byte[] buf, int off, int len) |
|
|
{ |
|
|
input.setInput(buf, off, len); |
|
|
totalIn += len; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Inflates the compressed stream to the output buffer. If this |
|
|
* returns 0, you should check, whether needsDictionary(), |
|
|
* needsInput() or finished() returns true, to determine why no |
|
|
* further output is produced. |
|
|
* @param buffer the output buffer. |
|
|
* @return the number of bytes written to the buffer, 0 if no further |
|
|
* output can be produced. |
|
|
* @exception DataFormatException if deflated stream is invalid. |
|
|
* @exception IllegalArgumentException if buf has length 0. |
|
|
*/ |
|
|
public int inflate(byte[] buf) throws DataFormatException |
|
|
{ |
|
|
return inflate(buf, 0, buf.length); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Inflates the compressed stream to the output buffer. If this |
|
|
* returns 0, you should check, whether needsDictionary(), |
|
|
* needsInput() or finished() returns true, to determine why no |
|
|
* further output is produced. |
|
|
* @param buffer the output buffer. |
|
|
* @param off the offset into buffer where the output should start. |
|
|
* @param len the maximum length of the output. |
|
|
* @return the number of bytes written to the buffer, 0 if no further |
|
|
* output can be produced. |
|
|
* @exception DataFormatException if deflated stream is invalid. |
|
|
* @exception IndexOutOfBoundsException if the off and/or len are wrong. |
|
|
*/ |
|
|
public int inflate(byte[] buf, int off, int len) throws DataFormatException |
|
|
{ |
|
|
/* Special case: len may be zero */ |
|
|
if (len == 0) |
|
|
return 0; |
|
|
/* Check for correct buff, off, len triple */ |
|
|
if (0 > off || off > off + len || off + len > buf.length) |
|
|
throw new ArrayIndexOutOfBoundsException(); |
|
|
int count = 0; |
|
|
int more; |
|
|
do |
|
|
{ |
|
|
if (mode != DECODE_CHKSUM) |
|
|
{ |
|
|
/* Don't give away any output, if we are waiting for the |
|
|
* checksum in the input stream. |
|
|
* |
|
|
* With this trick we have always: |
|
|
* needsInput() and not finished() |
|
|
* implies more output can be produced. |
|
|
*/ |
|
|
more = outputWindow.copyOutput(buf, off, len); |
|
|
adler.update(buf, off, more); |
|
|
off += more; |
|
|
count += more; |
|
|
totalOut += more; |
|
|
len -= more; |
|
|
if (len == 0) |
|
|
return count; |
|
|
} |
|
|
} |
|
|
while (decode() || (outputWindow.getAvailable() > 0 |
|
|
&& mode != DECODE_CHKSUM)); |
|
|
return count; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Returns true, if the input buffer is empty. |
|
|
* You should then call setInput(). <br> |
|
|
* |
|
|
* <em>NOTE</em>: This method also returns true when the stream is finished. |
|
|
*/ |
|
|
public boolean needsInput() |
|
|
{ |
|
|
return input.needsInput(); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Returns true, if a preset dictionary is needed to inflate the input. |
|
|
*/ |
|
|
public boolean needsDictionary() |
|
|
{ |
|
|
return mode == DECODE_DICT && neededBits == 0; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Returns true, if the inflater has finished. This means, that no |
|
|
* input is needed and no output can be produced. |
|
|
*/ |
|
|
public boolean finished() |
|
|
{ |
|
|
return mode == FINISHED && outputWindow.getAvailable() == 0; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Gets the adler checksum. This is either the checksum of all |
|
|
* uncompressed bytes returned by inflate(), or if needsDictionary() |
|
|
* returns true (and thus no output was yet produced) this is the |
|
|
* adler checksum of the expected dictionary. |
|
|
* @returns the adler checksum. |
|
|
*/ |
|
|
public int getAdler() |
|
|
{ |
|
|
return needsDictionary() ? readAdler : (int) adler.getValue(); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Gets the total number of output bytes returned by inflate(). |
|
|
* @return the total number of output bytes. |
|
|
*/ |
|
|
public int getTotalOut() |
|
|
{ |
|
|
return totalOut; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Gets the total number of processed compressed input bytes. |
|
|
* @return the total number of bytes of processed input bytes. |
|
|
*/ |
|
|
public int getTotalIn() |
|
|
{ |
|
|
return totalIn - getRemaining(); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Gets the number of unprocessed input. Useful, if the end of the |
|
|
* stream is reached and you want to further process the bytes after |
|
|
* the deflate stream. |
|
|
* @return the number of bytes of the input which were not processed. |
|
|
*/ |
|
|
public int getRemaining() |
|
|
{ |
|
|
return input.getAvailableBytes(); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Frees all objects allocated by the inflater. There's no reason |
|
|
* to call this, since you can just rely on garbage collection (even |
|
|
* for the Sun implementation). Exists only for compatibility |
|
|
* with Sun's JDK, where the compressor allocates native memory. |
|
|
* If you call any method (even reset) afterwards the behaviour is |
|
|
* <i>undefined</i>. |
|
|
* @deprecated Just clear all references to inflater instead. |
|
|
*/ |
|
|
public void end() |
|
|
{ |
|
|
outputWindow = null; |
|
|
input = null; |
|
|
dynHeader = null; |
|
|
litlenTree = null; |
|
|
distTree = null; |
|
|
adler = null; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Finalizes this object. |
|
|
*/ |
|
|
protected void finalize() |
|
|
{ |
|
|
/* Exists only for compatibility */ |
|
|
} |
|
715 |
} |
} |