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