001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.compressors; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.security.AccessController; 025import java.security.PrivilegedAction; 026import java.util.Collections; 027import java.util.Locale; 028import java.util.ServiceLoader; 029import java.util.Set; 030import java.util.SortedMap; 031import java.util.TreeMap; 032 033import org.apache.commons.compress.compressors.brotli.BrotliCompressorInputStream; 034import org.apache.commons.compress.compressors.brotli.BrotliUtils; 035import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; 036import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; 037import org.apache.commons.compress.compressors.deflate.DeflateCompressorInputStream; 038import org.apache.commons.compress.compressors.deflate.DeflateCompressorOutputStream; 039import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream; 040import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 041import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; 042import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorInputStream; 043import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorOutputStream; 044import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream; 045import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorOutputStream; 046import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream; 047import org.apache.commons.compress.compressors.lzma.LZMACompressorOutputStream; 048import org.apache.commons.compress.compressors.lzma.LZMAUtils; 049import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorInputStream; 050import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorOutputStream; 051import org.apache.commons.compress.compressors.snappy.SnappyCompressorInputStream; 052import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; 053import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; 054import org.apache.commons.compress.compressors.xz.XZUtils; 055import org.apache.commons.compress.compressors.z.ZCompressorInputStream; 056import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream; 057import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream; 058import org.apache.commons.compress.compressors.zstandard.ZstdUtils; 059import org.apache.commons.compress.utils.IOUtils; 060import org.apache.commons.compress.utils.Sets; 061 062/** 063 * <p> 064 * Factory to create Compressor[In|Out]putStreams from names. To add other 065 * implementations you should extend CompressorStreamFactory and override the 066 * appropriate methods (and call their implementation from super of course). 067 * </p> 068 * 069 * Example (Compressing a file): 070 * 071 * <pre> 072 * final OutputStream out = Files.newOutputStream(output.toPath()); 073 * CompressorOutputStream cos = new CompressorStreamFactory() 074 * .createCompressorOutputStream(CompressorStreamFactory.BZIP2, out); 075 * IOUtils.copy(Files.newInputStream(input.toPath()), cos); 076 * cos.close(); 077 * </pre> 078 * 079 * Example (Decompressing a file): 080 * 081 * <pre> 082 * final InputStream is = Files.newInputStream(input.toPath()); 083 * CompressorInputStream in = new CompressorStreamFactory().createCompressorInputStream(CompressorStreamFactory.BZIP2, 084 * is); 085 * IOUtils.copy(in, Files.newOutputStream(output.toPath())); 086 * in.close(); 087 * </pre> 088 * 089 * @Immutable provided that the deprecated method setDecompressConcatenated is 090 * not used. 091 * @ThreadSafe even if the deprecated method setDecompressConcatenated is used 092 */ 093public class CompressorStreamFactory implements CompressorStreamProvider { 094 095 private static final CompressorStreamFactory SINGLETON = new CompressorStreamFactory(); 096 097 098 099 /** 100 * Constant (value {@value}) used to identify the BROTLI compression 101 * algorithm. 102 * 103 * @since 1.14 104 */ 105 public static final String BROTLI = "br"; 106 107 /** 108 * Constant (value {@value}) used to identify the BZIP2 compression 109 * algorithm. 110 * 111 * @since 1.1 112 */ 113 public static final String BZIP2 = "bzip2"; 114 115 /** 116 * Constant (value {@value}) used to identify the GZIP compression 117 * algorithm. 118 * 119 * @since 1.1 120 */ 121 public static final String GZIP = "gz"; 122 123 /** 124 * Constant (value {@value}) used to identify the PACK200 compression 125 * algorithm. 126 * 127 * @since 1.3 128 */ 129 public static final String PACK200 = "pack200"; 130 131 /** 132 * Constant (value {@value}) used to identify the XZ compression method. 133 * 134 * @since 1.4 135 */ 136 public static final String XZ = "xz"; 137 138 /** 139 * Constant (value {@value}) used to identify the LZMA compression method. 140 * 141 * @since 1.6 142 */ 143 public static final String LZMA = "lzma"; 144 145 /** 146 * Constant (value {@value}) used to identify the "framed" Snappy 147 * compression method. 148 * 149 * @since 1.7 150 */ 151 public static final String SNAPPY_FRAMED = "snappy-framed"; 152 153 /** 154 * Constant (value {@value}) used to identify the "raw" Snappy compression 155 * method. Not supported as an output stream type. 156 * 157 * @since 1.7 158 */ 159 public static final String SNAPPY_RAW = "snappy-raw"; 160 161 /** 162 * Constant (value {@value}) used to identify the traditional Unix compress 163 * method. Not supported as an output stream type. 164 * 165 * @since 1.7 166 */ 167 public static final String Z = "z"; 168 169 /** 170 * Constant (value {@value}) used to identify the Deflate compress method. 171 * 172 * @since 1.9 173 */ 174 public static final String DEFLATE = "deflate"; 175 176 /** 177 * Constant (value {@value}) used to identify the Deflate64 compress method. 178 * 179 * @since 1.16 180 */ 181 public static final String DEFLATE64 = "deflate64"; 182 183 /** 184 * Constant (value {@value}) used to identify the block LZ4 185 * compression method. 186 * 187 * @since 1.14 188 */ 189 public static final String LZ4_BLOCK = "lz4-block"; 190 191 /** 192 * Constant (value {@value}) used to identify the frame LZ4 193 * compression method. 194 * 195 * @since 1.14 196 */ 197 public static final String LZ4_FRAMED = "lz4-framed"; 198 199 /** 200 * Constant (value {@value}) used to identify the Zstandard compression 201 * algorithm. Not supported as an output stream type. 202 * 203 * @since 1.16 204 */ 205 public static final String ZSTANDARD = "zstd"; 206 207 private static final String YOU_NEED_BROTLI_DEC = youNeed("Google Brotli Dec", "https://github.com/google/brotli/"); 208 private static final String YOU_NEED_XZ_JAVA = youNeed("XZ for Java", "https://tukaani.org/xz/java.html"); 209 private static final String YOU_NEED_ZSTD_JNI = youNeed("Zstd JNI", "https://github.com/luben/zstd-jni"); 210 211 private static String youNeed(final String name, final String url) { 212 return " In addition to Apache Commons Compress you need the " + name + " library - see " + url; 213 } 214 215 /** 216 * Constructs a new sorted map from input stream provider names to provider 217 * objects. 218 * 219 * <p> 220 * The map returned by this method will have one entry for each provider for 221 * which support is available in the current Java virtual machine. If two or 222 * more supported provider have the same name then the resulting map will 223 * contain just one of them; which one it will contain is not specified. 224 * </p> 225 * 226 * <p> 227 * The invocation of this method, and the subsequent use of the resulting 228 * map, may cause time-consuming disk or network I/O operations to occur. 229 * This method is provided for applications that need to enumerate all of 230 * the available providers, for example to allow user provider selection. 231 * </p> 232 * 233 * <p> 234 * This method may return different results at different times if new 235 * providers are dynamically made available to the current Java virtual 236 * machine. 237 * </p> 238 * 239 * @return An immutable, map from names to provider objects 240 * @since 1.13 241 */ 242 public static SortedMap<String, CompressorStreamProvider> findAvailableCompressorInputStreamProviders() { 243 return AccessController.doPrivileged((PrivilegedAction<SortedMap<String, CompressorStreamProvider>>) () -> { 244 final TreeMap<String, CompressorStreamProvider> map = new TreeMap<>(); 245 putAll(SINGLETON.getInputStreamCompressorNames(), SINGLETON, map); 246 archiveStreamProviderIterable().forEach(provider -> putAll(provider.getInputStreamCompressorNames(), provider, map)); 247 return map; 248 }); 249 } 250 251 /** 252 * Constructs a new sorted map from output stream provider names to provider 253 * objects. 254 * 255 * <p> 256 * The map returned by this method will have one entry for each provider for 257 * which support is available in the current Java virtual machine. If two or 258 * more supported provider have the same name then the resulting map will 259 * contain just one of them; which one it will contain is not specified. 260 * </p> 261 * 262 * <p> 263 * The invocation of this method, and the subsequent use of the resulting 264 * map, may cause time-consuming disk or network I/O operations to occur. 265 * This method is provided for applications that need to enumerate all of 266 * the available providers, for example to allow user provider selection. 267 * </p> 268 * 269 * <p> 270 * This method may return different results at different times if new 271 * providers are dynamically made available to the current Java virtual 272 * machine. 273 * </p> 274 * 275 * @return An immutable, map from names to provider objects 276 * @since 1.13 277 */ 278 public static SortedMap<String, CompressorStreamProvider> findAvailableCompressorOutputStreamProviders() { 279 return AccessController.doPrivileged((PrivilegedAction<SortedMap<String, CompressorStreamProvider>>) () -> { 280 final TreeMap<String, CompressorStreamProvider> map = new TreeMap<>(); 281 putAll(SINGLETON.getOutputStreamCompressorNames(), SINGLETON, map); 282 archiveStreamProviderIterable().forEach(provider -> putAll(provider.getOutputStreamCompressorNames(), provider, map)); 283 return map; 284 }); 285 } 286 287 public static String getBrotli() { 288 return BROTLI; 289 } 290 291 public static String getBzip2() { 292 return BZIP2; 293 } 294 295 public static String getDeflate() { 296 return DEFLATE; 297 } 298 299 /** 300 * @since 1.16 301 * @return the constant {@link #DEFLATE64} 302 */ 303 public static String getDeflate64() { 304 return DEFLATE64; 305 } 306 307 public static String getGzip() { 308 return GZIP; 309 } 310 311 public static String getLzma() { 312 return LZMA; 313 } 314 315 public static String getPack200() { 316 return PACK200; 317 } 318 319 public static CompressorStreamFactory getSingleton() { 320 return SINGLETON; 321 } 322 323 public static String getSnappyFramed() { 324 return SNAPPY_FRAMED; 325 } 326 327 public static String getSnappyRaw() { 328 return SNAPPY_RAW; 329 } 330 331 public static String getXz() { 332 return XZ; 333 } 334 335 public static String getZ() { 336 return Z; 337 } 338 339 public static String getLZ4Framed() { 340 return LZ4_FRAMED; 341 } 342 343 public static String getLZ4Block() { 344 return LZ4_BLOCK; 345 } 346 347 public static String getZstandard() { 348 return ZSTANDARD; 349 } 350 351 static void putAll(final Set<String> names, final CompressorStreamProvider provider, final TreeMap<String, CompressorStreamProvider> map) { 352 names.forEach(name -> map.put(toKey(name), provider)); 353 } 354 355 private static Iterable<CompressorStreamProvider> archiveStreamProviderIterable() { 356 return ServiceLoader.load(CompressorStreamProvider.class, ClassLoader.getSystemClassLoader()); 357 } 358 359 private static String toKey(final String name) { 360 return name.toUpperCase(Locale.ROOT); 361 } 362 363 /** 364 * If true, decompress until the end of the input. If false, stop after the 365 * first stream and leave the input position to point to the next byte after 366 * the stream 367 */ 368 private final Boolean decompressUntilEOF; 369 // This is Boolean so setDecompressConcatenated can determine whether it has 370 // been set by the ctor 371 // once the setDecompressConcatenated method has been removed, it can revert 372 // to boolean 373 374 private SortedMap<String, CompressorStreamProvider> compressorInputStreamProviders; 375 376 private SortedMap<String, CompressorStreamProvider> compressorOutputStreamProviders; 377 378 /** 379 * If true, decompress until the end of the input. If false, stop after the 380 * first stream and leave the input position to point to the next byte after 381 * the stream 382 */ 383 private volatile boolean decompressConcatenated; 384 385 private final int memoryLimitInKb; 386 387 /** 388 * Create an instance with the decompress Concatenated option set to false. 389 */ 390 public CompressorStreamFactory() { 391 this.decompressUntilEOF = null; 392 this.memoryLimitInKb = -1; 393 } 394 395 /** 396 * Create an instance with the provided decompress Concatenated option. 397 * 398 * @param decompressUntilEOF 399 * if true, decompress until the end of the input; if false, stop 400 * after the first stream and leave the input position to point 401 * to the next byte after the stream. This setting applies to the 402 * gzip, bzip2 and xz formats only. 403 * 404 * @param memoryLimitInKb 405 * Some streams require allocation of potentially significant 406 * byte arrays/tables, and they can offer checks to prevent OOMs 407 * on corrupt files. Set the maximum allowed memory allocation in KBs. 408 * 409 * @since 1.14 410 */ 411 public CompressorStreamFactory(final boolean decompressUntilEOF, final int memoryLimitInKb) { 412 this.decompressUntilEOF = decompressUntilEOF; 413 // Also copy to existing variable so can continue to use that as the 414 // current value 415 this.decompressConcatenated = decompressUntilEOF; 416 this.memoryLimitInKb = memoryLimitInKb; 417 } 418 419 /** 420 * Create an instance with the provided decompress Concatenated option. 421 * 422 * @param decompressUntilEOF 423 * if true, decompress until the end of the input; if false, stop 424 * after the first stream and leave the input position to point 425 * to the next byte after the stream. This setting applies to the 426 * gzip, bzip2 and xz formats only. 427 * @since 1.10 428 */ 429 public CompressorStreamFactory(final boolean decompressUntilEOF) { 430 this(decompressUntilEOF, -1); 431 } 432 433 /** 434 * Try to detect the type of compressor stream. 435 * 436 * @param inputStream input stream 437 * @return type of compressor stream detected 438 * @throws CompressorException if no compressor stream type was detected 439 * or if something else went wrong 440 * @throws IllegalArgumentException if stream is null or does not support mark 441 * 442 * @since 1.14 443 */ 444 public static String detect(final InputStream inputStream) throws CompressorException { 445 if (inputStream == null) { 446 throw new IllegalArgumentException("Stream must not be null."); 447 } 448 449 if (!inputStream.markSupported()) { 450 throw new IllegalArgumentException("Mark is not supported."); 451 } 452 453 final byte[] signature = new byte[12]; 454 inputStream.mark(signature.length); 455 int signatureLength = -1; 456 try { 457 signatureLength = IOUtils.readFully(inputStream, signature); 458 inputStream.reset(); 459 } catch (final IOException e) { 460 throw new CompressorException("IOException while reading signature.", e); 461 } 462 463 if (BZip2CompressorInputStream.matches(signature, signatureLength)) { 464 return BZIP2; 465 } 466 467 if (GzipCompressorInputStream.matches(signature, signatureLength)) { 468 return GZIP; 469 } 470 471 if (FramedSnappyCompressorInputStream.matches(signature, signatureLength)) { 472 return SNAPPY_FRAMED; 473 } 474 475 if (ZCompressorInputStream.matches(signature, signatureLength)) { 476 return Z; 477 } 478 479 if (DeflateCompressorInputStream.matches(signature, signatureLength)) { 480 return DEFLATE; 481 } 482 483 if (XZUtils.matches(signature, signatureLength)) { 484 return XZ; 485 } 486 487 if (LZMAUtils.matches(signature, signatureLength)) { 488 return LZMA; 489 } 490 491 if (FramedLZ4CompressorInputStream.matches(signature, signatureLength)) { 492 return LZ4_FRAMED; 493 } 494 495 if (ZstdUtils.matches(signature, signatureLength)) { 496 return ZSTANDARD; 497 } 498 499 throw new CompressorException("No Compressor found for the stream signature."); 500 } 501 /** 502 * Create an compressor input stream from an input stream, autodetecting the 503 * compressor type from the first few bytes of the stream. The InputStream 504 * must support marks, like BufferedInputStream. 505 * 506 * @param in 507 * the input stream 508 * @return the compressor input stream 509 * @throws CompressorException 510 * if the compressor name is not known 511 * @throws IllegalArgumentException 512 * if the stream is null or does not support mark 513 * @since 1.1 514 */ 515 public CompressorInputStream createCompressorInputStream(final InputStream in) throws CompressorException { 516 return createCompressorInputStream(detect(in), in); 517 } 518 519 /** 520 * Creates a compressor input stream from a compressor name and an input 521 * stream. 522 * 523 * @param name 524 * of the compressor, i.e. {@value #GZIP}, {@value #BZIP2}, 525 * {@value #XZ}, {@value #LZMA}, {@value #PACK200}, 526 * {@value #SNAPPY_RAW}, {@value #SNAPPY_FRAMED}, {@value #Z}, 527 * {@value #LZ4_BLOCK}, {@value #LZ4_FRAMED}, {@value #ZSTANDARD}, 528 * {@value #DEFLATE64} 529 * or {@value #DEFLATE} 530 * @param in 531 * the input stream 532 * @return compressor input stream 533 * @throws CompressorException 534 * if the compressor name is not known or not available, 535 * or if there's an IOException or MemoryLimitException thrown 536 * during initialization 537 * @throws IllegalArgumentException 538 * if the name or input stream is null 539 */ 540 public CompressorInputStream createCompressorInputStream(final String name, final InputStream in) 541 throws CompressorException { 542 return createCompressorInputStream(name, in, decompressConcatenated); 543 } 544 545 @Override 546 public CompressorInputStream createCompressorInputStream(final String name, final InputStream in, 547 final boolean actualDecompressConcatenated) throws CompressorException { 548 if (name == null || in == null) { 549 throw new IllegalArgumentException("Compressor name and stream must not be null."); 550 } 551 552 try { 553 554 if (GZIP.equalsIgnoreCase(name)) { 555 return new GzipCompressorInputStream(in, actualDecompressConcatenated); 556 } 557 558 if (BZIP2.equalsIgnoreCase(name)) { 559 return new BZip2CompressorInputStream(in, actualDecompressConcatenated); 560 } 561 562 if (BROTLI.equalsIgnoreCase(name)) { 563 if (!BrotliUtils.isBrotliCompressionAvailable()) { 564 throw new CompressorException("Brotli compression is not available." + YOU_NEED_BROTLI_DEC); 565 } 566 return new BrotliCompressorInputStream(in); 567 } 568 569 if (XZ.equalsIgnoreCase(name)) { 570 if (!XZUtils.isXZCompressionAvailable()) { 571 throw new CompressorException("XZ compression is not available." + YOU_NEED_XZ_JAVA); 572 } 573 return new XZCompressorInputStream(in, actualDecompressConcatenated, memoryLimitInKb); 574 } 575 576 if (ZSTANDARD.equalsIgnoreCase(name)) { 577 if (!ZstdUtils.isZstdCompressionAvailable()) { 578 throw new CompressorException("Zstandard compression is not available." + YOU_NEED_ZSTD_JNI); 579 } 580 return new ZstdCompressorInputStream(in); 581 } 582 583 if (LZMA.equalsIgnoreCase(name)) { 584 if (!LZMAUtils.isLZMACompressionAvailable()) { 585 throw new CompressorException("LZMA compression is not available" + YOU_NEED_XZ_JAVA); 586 } 587 return new LZMACompressorInputStream(in, memoryLimitInKb); 588 } 589 590 if (PACK200.equalsIgnoreCase(name)) { 591 throw new CompressorException("Pack200 compression is not available in this build."); 592 } 593 594 if (SNAPPY_RAW.equalsIgnoreCase(name)) { 595 return new SnappyCompressorInputStream(in); 596 } 597 598 if (SNAPPY_FRAMED.equalsIgnoreCase(name)) { 599 return new FramedSnappyCompressorInputStream(in); 600 } 601 602 if (Z.equalsIgnoreCase(name)) { 603 return new ZCompressorInputStream(in, memoryLimitInKb); 604 } 605 606 if (DEFLATE.equalsIgnoreCase(name)) { 607 return new DeflateCompressorInputStream(in); 608 } 609 610 if (DEFLATE64.equalsIgnoreCase(name)) { 611 return new Deflate64CompressorInputStream(in); 612 } 613 614 if (LZ4_BLOCK.equalsIgnoreCase(name)) { 615 return new BlockLZ4CompressorInputStream(in); 616 } 617 618 if (LZ4_FRAMED.equalsIgnoreCase(name)) { 619 return new FramedLZ4CompressorInputStream(in, actualDecompressConcatenated); 620 } 621 622 } catch (final IOException e) { 623 throw new CompressorException("Could not create CompressorInputStream.", e); 624 } 625 final CompressorStreamProvider compressorStreamProvider = getCompressorInputStreamProviders().get(toKey(name)); 626 if (compressorStreamProvider != null) { 627 return compressorStreamProvider.createCompressorInputStream(name, in, actualDecompressConcatenated); 628 } 629 630 throw new CompressorException("Compressor: " + name + " not found."); 631 } 632 633 /** 634 * Creates an compressor output stream from an compressor name and an output 635 * stream. 636 * 637 * @param name 638 * the compressor name, i.e. {@value #GZIP}, {@value #BZIP2}, 639 * {@value #XZ}, {@value #PACK200}, {@value #SNAPPY_FRAMED}, 640 * {@value #LZ4_BLOCK}, {@value #LZ4_FRAMED}, {@value #ZSTANDARD} 641 * or {@value #DEFLATE} 642 * @param out 643 * the output stream 644 * @return the compressor output stream 645 * @throws CompressorException 646 * if the archiver name is not known 647 * @throws IllegalArgumentException 648 * if the archiver name or stream is null 649 */ 650 @Override 651 public CompressorOutputStream createCompressorOutputStream(final String name, final OutputStream out) 652 throws CompressorException { 653 if (name == null || out == null) { 654 throw new IllegalArgumentException("Compressor name and stream must not be null."); 655 } 656 657 try { 658 659 if (GZIP.equalsIgnoreCase(name)) { 660 return new GzipCompressorOutputStream(out); 661 } 662 663 if (BZIP2.equalsIgnoreCase(name)) { 664 return new BZip2CompressorOutputStream(out); 665 } 666 667 if (XZ.equalsIgnoreCase(name)) { 668 return new XZCompressorOutputStream(out); 669 } 670 671 if (PACK200.equalsIgnoreCase(name)) { 672 throw new CompressorException("Pack200 compression is not available in this build."); 673 } 674 675 if (LZMA.equalsIgnoreCase(name)) { 676 return new LZMACompressorOutputStream(out); 677 } 678 679 if (DEFLATE.equalsIgnoreCase(name)) { 680 return new DeflateCompressorOutputStream(out); 681 } 682 683 if (SNAPPY_FRAMED.equalsIgnoreCase(name)) { 684 return new FramedSnappyCompressorOutputStream(out); 685 } 686 687 if (LZ4_BLOCK.equalsIgnoreCase(name)) { 688 return new BlockLZ4CompressorOutputStream(out); 689 } 690 691 if (LZ4_FRAMED.equalsIgnoreCase(name)) { 692 return new FramedLZ4CompressorOutputStream(out); 693 } 694 695 if (ZSTANDARD.equalsIgnoreCase(name)) { 696 return new ZstdCompressorOutputStream(out); 697 } 698 } catch (final IOException e) { 699 throw new CompressorException("Could not create CompressorOutputStream", e); 700 } 701 final CompressorStreamProvider compressorStreamProvider = getCompressorOutputStreamProviders().get(toKey(name)); 702 if (compressorStreamProvider != null) { 703 return compressorStreamProvider.createCompressorOutputStream(name, out); 704 } 705 throw new CompressorException("Compressor: " + name + " not found."); 706 } 707 708 public SortedMap<String, CompressorStreamProvider> getCompressorInputStreamProviders() { 709 if (compressorInputStreamProviders == null) { 710 compressorInputStreamProviders = Collections 711 .unmodifiableSortedMap(findAvailableCompressorInputStreamProviders()); 712 } 713 return compressorInputStreamProviders; 714 } 715 716 public SortedMap<String, CompressorStreamProvider> getCompressorOutputStreamProviders() { 717 if (compressorOutputStreamProviders == null) { 718 compressorOutputStreamProviders = Collections 719 .unmodifiableSortedMap(findAvailableCompressorOutputStreamProviders()); 720 } 721 return compressorOutputStreamProviders; 722 } 723 724 // For Unit tests 725 boolean getDecompressConcatenated() { 726 return decompressConcatenated; 727 } 728 729 public Boolean getDecompressUntilEOF() { 730 return decompressUntilEOF; 731 } 732 733 @Override 734 public Set<String> getInputStreamCompressorNames() { 735 return Sets.newHashSet(GZIP, BROTLI, BZIP2, XZ, LZMA, PACK200, DEFLATE, SNAPPY_RAW, SNAPPY_FRAMED, Z, LZ4_BLOCK, 736 LZ4_FRAMED, ZSTANDARD, DEFLATE64); 737 } 738 739 @Override 740 public Set<String> getOutputStreamCompressorNames() { 741 return Sets.newHashSet(GZIP, BZIP2, XZ, LZMA, PACK200, DEFLATE, SNAPPY_FRAMED, LZ4_BLOCK, LZ4_FRAMED, ZSTANDARD); 742 } 743 744 /** 745 * Whether to decompress the full input or only the first stream in formats 746 * supporting multiple concatenated input streams. 747 * 748 * <p> 749 * This setting applies to the gzip, bzip2 and xz formats only. 750 * </p> 751 * 752 * @param decompressConcatenated 753 * if true, decompress until the end of the input; if false, stop 754 * after the first stream and leave the input position to point 755 * to the next byte after the stream 756 * @since 1.5 757 * @deprecated 1.10 use the {@link #CompressorStreamFactory(boolean)} 758 * constructor instead 759 * @throws IllegalStateException 760 * if the constructor {@link #CompressorStreamFactory(boolean)} 761 * was used to create the factory 762 */ 763 @Deprecated 764 public void setDecompressConcatenated(final boolean decompressConcatenated) { 765 if (this.decompressUntilEOF != null) { 766 throw new IllegalStateException("Cannot override the setting defined by the constructor"); 767 } 768 this.decompressConcatenated = decompressConcatenated; 769 } 770 771}