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