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.pack200.Pack200CompressorInputStream; 049import org.apache.commons.compress.compressors.pack200.Pack200CompressorOutputStream; 050import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorInputStream; 051import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorOutputStream; 052import org.apache.commons.compress.compressors.snappy.SnappyCompressorInputStream; 053import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; 054import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; 055import org.apache.commons.compress.compressors.xz.XZUtils; 056import org.apache.commons.compress.compressors.z.ZCompressorInputStream; 057import org.apache.commons.compress.utils.IOUtils; 058import org.apache.commons.compress.utils.Lists; 059import org.apache.commons.compress.utils.ServiceLoaderIterator; 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 for (final CompressorStreamProvider provider : findCompressorStreamProviders()) { 247 putAll(provider.getInputStreamCompressorNames(), provider, map); 248 } 249 return map; 250 }); 251 } 252 253 /** 254 * Constructs a new sorted map from output stream provider names to provider 255 * objects. 256 * 257 * <p> 258 * The map returned by this method will have one entry for each provider for 259 * which support is available in the current Java virtual machine. If two or 260 * more supported provider have the same name then the resulting map will 261 * contain just one of them; which one it will contain is not specified. 262 * </p> 263 * 264 * <p> 265 * The invocation of this method, and the subsequent use of the resulting 266 * map, may cause time-consuming disk or network I/O operations to occur. 267 * This method is provided for applications that need to enumerate all of 268 * the available providers, for example to allow user provider selection. 269 * </p> 270 * 271 * <p> 272 * This method may return different results at different times if new 273 * providers are dynamically made available to the current Java virtual 274 * machine. 275 * </p> 276 * 277 * @return An immutable, map from names to provider objects 278 * @since 1.13 279 */ 280 public static SortedMap<String, CompressorStreamProvider> findAvailableCompressorOutputStreamProviders() { 281 return AccessController.doPrivileged((PrivilegedAction<SortedMap<String, CompressorStreamProvider>>) () -> { 282 final TreeMap<String, CompressorStreamProvider> map = new TreeMap<>(); 283 putAll(SINGLETON.getOutputStreamCompressorNames(), SINGLETON, map); 284 for (final CompressorStreamProvider provider : findCompressorStreamProviders()) { 285 putAll(provider.getOutputStreamCompressorNames(), provider, map); 286 } 287 return map; 288 }); 289 } 290 private static ArrayList<CompressorStreamProvider> findCompressorStreamProviders() { 291 return Lists.newArrayList(serviceLoaderIterator()); 292 } 293 294 public static String getBrotli() { 295 return BROTLI; 296 } 297 298 public static String getBzip2() { 299 return BZIP2; 300 } 301 302 public static String getDeflate() { 303 return DEFLATE; 304 } 305 306 /** 307 * @since 1.16 308 * @return the constant {@link #DEFLATE64} 309 */ 310 public static String getDeflate64() { 311 return DEFLATE64; 312 } 313 314 public static String getGzip() { 315 return GZIP; 316 } 317 318 public static String getLzma() { 319 return LZMA; 320 } 321 322 public static String getPack200() { 323 return PACK200; 324 } 325 326 public static CompressorStreamFactory getSingleton() { 327 return SINGLETON; 328 } 329 330 public static String getSnappyFramed() { 331 return SNAPPY_FRAMED; 332 } 333 334 public static String getSnappyRaw() { 335 return SNAPPY_RAW; 336 } 337 338 public static String getXz() { 339 return XZ; 340 } 341 342 public static String getZ() { 343 return Z; 344 } 345 346 public static String getLZ4Framed() { 347 return LZ4_FRAMED; 348 } 349 350 public static String getLZ4Block() { 351 return LZ4_BLOCK; 352 } 353 354 public static String getZstandard() { 355 return ZSTANDARD; 356 } 357 358 static void putAll(final Set<String> names, final CompressorStreamProvider provider, 359 final TreeMap<String, CompressorStreamProvider> map) { 360 for (final String name : names) { 361 map.put(toKey(name), provider); 362 } 363 } 364 365 private static Iterator<CompressorStreamProvider> serviceLoaderIterator() { 366 return new ServiceLoaderIterator<>(CompressorStreamProvider.class); 367 } 368 369 private static String toKey(final String name) { 370 return name.toUpperCase(Locale.ROOT); 371 } 372 373 /** 374 * If true, decompress until the end of the input. If false, stop after the 375 * first stream and leave the input position to point to the next byte after 376 * the stream 377 */ 378 private final Boolean decompressUntilEOF; 379 // This is Boolean so setDecompressConcatenated can determine whether it has 380 // been set by the ctor 381 // once the setDecompressConcatenated method has been removed, it can revert 382 // to boolean 383 384 private SortedMap<String, CompressorStreamProvider> compressorInputStreamProviders; 385 386 private SortedMap<String, CompressorStreamProvider> compressorOutputStreamProviders; 387 388 /** 389 * If true, decompress until the end of the input. If false, stop after the 390 * first stream and leave the input position to point to the next byte after 391 * the stream 392 */ 393 private volatile boolean decompressConcatenated; 394 395 private final int memoryLimitInKb; 396 397 /** 398 * Create an instance with the decompress Concatenated option set to false. 399 */ 400 public CompressorStreamFactory() { 401 this.decompressUntilEOF = null; 402 this.memoryLimitInKb = -1; 403 } 404 405 /** 406 * Create an instance with the provided decompress Concatenated option. 407 * 408 * @param decompressUntilEOF 409 * if true, decompress until the end of the input; if false, stop 410 * after the first stream and leave the input position to point 411 * to the next byte after the stream. This setting applies to the 412 * gzip, bzip2 and xz formats only. 413 * 414 * @param memoryLimitInKb 415 * Some streams require allocation of potentially significant 416 * byte arrays/tables, and they can offer checks to prevent OOMs 417 * on corrupt files. Set the maximum allowed memory allocation in KBs. 418 * 419 * @since 1.14 420 */ 421 public CompressorStreamFactory(final boolean decompressUntilEOF, final int memoryLimitInKb) { 422 this.decompressUntilEOF = decompressUntilEOF; 423 // Also copy to existing variable so can continue to use that as the 424 // current value 425 this.decompressConcatenated = decompressUntilEOF; 426 this.memoryLimitInKb = memoryLimitInKb; 427 } 428 429 /** 430 * Create an instance with the provided decompress Concatenated option. 431 * 432 * @param decompressUntilEOF 433 * if true, decompress until the end of the input; if false, stop 434 * after the first stream and leave the input position to point 435 * to the next byte after the stream. This setting applies to the 436 * gzip, bzip2 and xz formats only. 437 * @since 1.10 438 */ 439 public CompressorStreamFactory(final boolean decompressUntilEOF) { 440 this(decompressUntilEOF, -1); 441 } 442 443 /** 444 * Try to detect the type of compressor stream. 445 * 446 * @param inputStream input stream 447 * @return type of compressor stream detected 448 * @throws CompressorException if no compressor stream type was detected 449 * or if something else went wrong 450 * @throws IllegalArgumentException if stream is null or does not support mark 451 * 452 * @since 1.14 453 */ 454 public static String detect(final InputStream inputStream) throws CompressorException { 455 if (inputStream == null) { 456 throw new IllegalArgumentException("Stream must not be null."); 457 } 458 459 if (!inputStream.markSupported()) { 460 throw new IllegalArgumentException("Mark is not supported."); 461 } 462 463 final byte[] signature = new byte[12]; 464 inputStream.mark(signature.length); 465 int signatureLength = -1; 466 try { 467 signatureLength = IOUtils.readFully(inputStream, signature); 468 inputStream.reset(); 469 } catch (final IOException e) { 470 throw new CompressorException("IOException while reading signature.", e); 471 } 472 473 if (BZip2CompressorInputStream.matches(signature, signatureLength)) { 474 return BZIP2; 475 } 476 477 if (GzipCompressorInputStream.matches(signature, signatureLength)) { 478 return GZIP; 479 } 480 481 if (Pack200CompressorInputStream.matches(signature, signatureLength)) { 482 return PACK200; 483 } 484 485 if (FramedSnappyCompressorInputStream.matches(signature, signatureLength)) { 486 return SNAPPY_FRAMED; 487 } 488 489 if (ZCompressorInputStream.matches(signature, signatureLength)) { 490 return Z; 491 } 492 493 if (DeflateCompressorInputStream.matches(signature, signatureLength)) { 494 return DEFLATE; 495 } 496 497 if (XZUtils.matches(signature, signatureLength)) { 498 return XZ; 499 } 500 501 if (LZMAUtils.matches(signature, signatureLength)) { 502 return LZMA; 503 } 504 505 if (FramedLZ4CompressorInputStream.matches(signature, signatureLength)) { 506 return LZ4_FRAMED; 507 } 508 509 throw new CompressorException("No Compressor found for the stream signature."); 510 } 511 /** 512 * Create an compressor input stream from an input stream, autodetecting the 513 * compressor type from the first few bytes of the stream. The InputStream 514 * must support marks, like BufferedInputStream. 515 * 516 * @param in 517 * the input stream 518 * @return the compressor input stream 519 * @throws CompressorException 520 * if the compressor name is not known 521 * @throws IllegalArgumentException 522 * if the stream is null or does not support mark 523 * @since 1.1 524 */ 525 public CompressorInputStream createCompressorInputStream(final InputStream in) throws CompressorException { 526 return createCompressorInputStream(detect(in), in); 527 } 528 529 /** 530 * Creates a compressor input stream from a compressor name and an input 531 * stream. 532 * 533 * @param name 534 * of the compressor, i.e. {@value #GZIP}, {@value #BZIP2}, 535 * {@value #XZ}, {@value #LZMA}, {@value #PACK200}, 536 * {@value #SNAPPY_RAW}, {@value #SNAPPY_FRAMED}, {@value #Z}, 537 * {@value #LZ4_BLOCK}, {@value #LZ4_FRAMED}, {@value #ZSTANDARD}, 538 * {@value #DEFLATE64} 539 * or {@value #DEFLATE} 540 * @param in 541 * the input stream 542 * @return compressor input stream 543 * @throws CompressorException 544 * if the compressor name is not known or not available, 545 * or if there's an IOException or MemoryLimitException thrown 546 * during initialization 547 * @throws IllegalArgumentException 548 * if the name or input stream is null 549 */ 550 public CompressorInputStream createCompressorInputStream(final String name, final InputStream in) 551 throws CompressorException { 552 return createCompressorInputStream(name, in, decompressConcatenated); 553 } 554 555 @Override 556 public CompressorInputStream createCompressorInputStream(final String name, final InputStream in, 557 final boolean actualDecompressConcatenated) throws CompressorException { 558 if (name == null || in == null) { 559 throw new IllegalArgumentException("Compressor name and stream must not be null."); 560 } 561 562 try { 563 564 if (GZIP.equalsIgnoreCase(name)) { 565 return new GzipCompressorInputStream(in, actualDecompressConcatenated); 566 } 567 568 if (BZIP2.equalsIgnoreCase(name)) { 569 return new BZip2CompressorInputStream(in, actualDecompressConcatenated); 570 } 571 572 if (BROTLI.equalsIgnoreCase(name)) { 573 throw new CompressorException("Brotli compression is not available in this build."); 574 } 575 576 if (XZ.equalsIgnoreCase(name)) { 577 if (!XZUtils.isXZCompressionAvailable()) { 578 throw new CompressorException("XZ compression is not available." + YOU_NEED_XZ_JAVA); 579 } 580 return new XZCompressorInputStream(in, actualDecompressConcatenated, memoryLimitInKb); 581 } 582 583 if (ZSTANDARD.equalsIgnoreCase(name)) { 584 throw new CompressorException("Zstandard compression is not available in this build."); 585 } 586 587 if (LZMA.equalsIgnoreCase(name)) { 588 if (!LZMAUtils.isLZMACompressionAvailable()) { 589 throw new CompressorException("LZMA compression is not available" + YOU_NEED_XZ_JAVA); 590 } 591 return new LZMACompressorInputStream(in, memoryLimitInKb); 592 } 593 594 if (PACK200.equalsIgnoreCase(name)) { 595 return new Pack200CompressorInputStream(in); 596 } 597 598 if (SNAPPY_RAW.equalsIgnoreCase(name)) { 599 return new SnappyCompressorInputStream(in); 600 } 601 602 if (SNAPPY_FRAMED.equalsIgnoreCase(name)) { 603 return new FramedSnappyCompressorInputStream(in); 604 } 605 606 if (Z.equalsIgnoreCase(name)) { 607 return new ZCompressorInputStream(in, memoryLimitInKb); 608 } 609 610 if (DEFLATE.equalsIgnoreCase(name)) { 611 return new DeflateCompressorInputStream(in); 612 } 613 614 if (DEFLATE64.equalsIgnoreCase(name)) { 615 return new Deflate64CompressorInputStream(in); 616 } 617 618 if (LZ4_BLOCK.equalsIgnoreCase(name)) { 619 return new BlockLZ4CompressorInputStream(in); 620 } 621 622 if (LZ4_FRAMED.equalsIgnoreCase(name)) { 623 return new FramedLZ4CompressorInputStream(in, actualDecompressConcatenated); 624 } 625 626 } catch (final IOException e) { 627 throw new CompressorException("Could not create CompressorInputStream.", e); 628 } 629 final CompressorStreamProvider compressorStreamProvider = getCompressorInputStreamProviders().get(toKey(name)); 630 if (compressorStreamProvider != null) { 631 return compressorStreamProvider.createCompressorInputStream(name, in, actualDecompressConcatenated); 632 } 633 634 throw new CompressorException("Compressor: " + name + " not found."); 635 } 636 637 /** 638 * Creates an compressor output stream from an compressor name and an output 639 * stream. 640 * 641 * @param name 642 * the compressor name, i.e. {@value #GZIP}, {@value #BZIP2}, 643 * {@value #XZ}, {@value #PACK200}, {@value #SNAPPY_FRAMED}, 644 * {@value #LZ4_BLOCK}, {@value #LZ4_FRAMED}, {@value #ZSTANDARD} 645 * or {@value #DEFLATE} 646 * @param out 647 * the output stream 648 * @return the compressor output stream 649 * @throws CompressorException 650 * if the archiver name is not known 651 * @throws IllegalArgumentException 652 * if the archiver name or stream is null 653 */ 654 @Override 655 public CompressorOutputStream createCompressorOutputStream(final String name, final OutputStream out) 656 throws CompressorException { 657 if (name == null || out == null) { 658 throw new IllegalArgumentException("Compressor name and stream must not be null."); 659 } 660 661 try { 662 663 if (GZIP.equalsIgnoreCase(name)) { 664 return new GzipCompressorOutputStream(out); 665 } 666 667 if (BZIP2.equalsIgnoreCase(name)) { 668 return new BZip2CompressorOutputStream(out); 669 } 670 671 if (XZ.equalsIgnoreCase(name)) { 672 return new XZCompressorOutputStream(out); 673 } 674 675 if (PACK200.equalsIgnoreCase(name)) { 676 return new Pack200CompressorOutputStream(out); 677 } 678 679 if (LZMA.equalsIgnoreCase(name)) { 680 return new LZMACompressorOutputStream(out); 681 } 682 683 if (DEFLATE.equalsIgnoreCase(name)) { 684 return new DeflateCompressorOutputStream(out); 685 } 686 687 if (SNAPPY_FRAMED.equalsIgnoreCase(name)) { 688 return new FramedSnappyCompressorOutputStream(out); 689 } 690 691 if (LZ4_BLOCK.equalsIgnoreCase(name)) { 692 return new BlockLZ4CompressorOutputStream(out); 693 } 694 695 if (LZ4_FRAMED.equalsIgnoreCase(name)) { 696 return new FramedLZ4CompressorOutputStream(out); 697 } 698 699 if (ZSTANDARD.equalsIgnoreCase(name)) { 700 throw new CompressorException("Zstandard compression is not available in this build."); 701 } 702 } catch (final IOException e) { 703 throw new CompressorException("Could not create CompressorOutputStream", e); 704 } 705 final CompressorStreamProvider compressorStreamProvider = getCompressorOutputStreamProviders().get(toKey(name)); 706 if (compressorStreamProvider != null) { 707 return compressorStreamProvider.createCompressorOutputStream(name, out); 708 } 709 throw new CompressorException("Compressor: " + name + " not found."); 710 } 711 712 public SortedMap<String, CompressorStreamProvider> getCompressorInputStreamProviders() { 713 if (compressorInputStreamProviders == null) { 714 compressorInputStreamProviders = Collections 715 .unmodifiableSortedMap(findAvailableCompressorInputStreamProviders()); 716 } 717 return compressorInputStreamProviders; 718 } 719 720 public SortedMap<String, CompressorStreamProvider> getCompressorOutputStreamProviders() { 721 if (compressorOutputStreamProviders == null) { 722 compressorOutputStreamProviders = Collections 723 .unmodifiableSortedMap(findAvailableCompressorOutputStreamProviders()); 724 } 725 return compressorOutputStreamProviders; 726 } 727 728 // For Unit tests 729 boolean getDecompressConcatenated() { 730 return decompressConcatenated; 731 } 732 733 public Boolean getDecompressUntilEOF() { 734 return decompressUntilEOF; 735 } 736 737 @Override 738 public Set<String> getInputStreamCompressorNames() { 739 return Sets.newHashSet(GZIP, BROTLI, BZIP2, XZ, LZMA, PACK200, DEFLATE, SNAPPY_RAW, SNAPPY_FRAMED, Z, LZ4_BLOCK, 740 LZ4_FRAMED, ZSTANDARD, DEFLATE64); 741 } 742 743 @Override 744 public Set<String> getOutputStreamCompressorNames() { 745 return Sets.newHashSet(GZIP, BZIP2, XZ, LZMA, PACK200, DEFLATE, SNAPPY_FRAMED, LZ4_BLOCK, LZ4_FRAMED, ZSTANDARD); 746 } 747 748 /** 749 * Whether to decompress the full input or only the first stream in formats 750 * supporting multiple concatenated input streams. 751 * 752 * <p> 753 * This setting applies to the gzip, bzip2 and xz formats only. 754 * </p> 755 * 756 * @param decompressConcatenated 757 * if true, decompress until the end of the input; if false, stop 758 * after the first stream and leave the input position to point 759 * to the next byte after the stream 760 * @since 1.5 761 * @deprecated 1.10 use the {@link #CompressorStreamFactory(boolean)} 762 * constructor instead 763 * @throws IllegalStateException 764 * if the constructor {@link #CompressorStreamFactory(boolean)} 765 * was used to create the factory 766 */ 767 @Deprecated 768 public void setDecompressConcatenated(final boolean decompressConcatenated) { 769 if (this.decompressUntilEOF != null) { 770 throw new IllegalStateException("Cannot override the setting defined by the constructor"); 771 } 772 this.decompressConcatenated = decompressConcatenated; 773 } 774 775}