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