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