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