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 String youNeed(final String name, final String url) { 214 return " In addition to Apache Commons Compress you need the " + name + " library - see " + url; 215 } 216 217 /** 218 * Constructs a new sorted map from input stream provider names to provider 219 * objects. 220 * 221 * <p> 222 * The map returned by this method will have one entry for each provider for 223 * which support is available in the current Java virtual machine. If two or 224 * more supported provider have the same name then the resulting map will 225 * contain just one of them; which one it will contain is not specified. 226 * </p> 227 * 228 * <p> 229 * The invocation of this method, and the subsequent use of the resulting 230 * map, may cause time-consuming disk or network I/O operations to occur. 231 * This method is provided for applications that need to enumerate all of 232 * the available providers, for example to allow user provider selection. 233 * </p> 234 * 235 * <p> 236 * This method may return different results at different times if new 237 * providers are dynamically made available to the current Java virtual 238 * machine. 239 * </p> 240 * 241 * @return An immutable, map from names to provider objects 242 * @since 1.13 243 */ 244 public static SortedMap<String, CompressorStreamProvider> findAvailableCompressorInputStreamProviders() { 245 return AccessController.doPrivileged((PrivilegedAction<SortedMap<String, CompressorStreamProvider>>) () -> { 246 final TreeMap<String, CompressorStreamProvider> map = new TreeMap<>(); 247 putAll(SINGLETON.getInputStreamCompressorNames(), SINGLETON, map); 248 archiveStreamProviderIterable().forEach(provider -> putAll(provider.getInputStreamCompressorNames(), provider, map)); 249 return map; 250 }); 251 } 252 253 /** 254 * Constructs a new sorted map from output stream provider names to provider 255 * objects. 256 * 257 * <p> 258 * The map returned by this method will have one entry for each provider for 259 * which support is available in the current Java virtual machine. If two or 260 * more supported provider have the same name then the resulting map will 261 * contain just one of them; which one it will contain is not specified. 262 * </p> 263 * 264 * <p> 265 * The invocation of this method, and the subsequent use of the resulting 266 * map, may cause time-consuming disk or network I/O operations to occur. 267 * This method is provided for applications that need to enumerate all of 268 * the available providers, for example to allow user provider selection. 269 * </p> 270 * 271 * <p> 272 * This method may return different results at different times if new 273 * providers are dynamically made available to the current Java virtual 274 * machine. 275 * </p> 276 * 277 * @return An immutable, map from names to provider objects 278 * @since 1.13 279 */ 280 public static SortedMap<String, CompressorStreamProvider> findAvailableCompressorOutputStreamProviders() { 281 return AccessController.doPrivileged((PrivilegedAction<SortedMap<String, CompressorStreamProvider>>) () -> { 282 final TreeMap<String, CompressorStreamProvider> map = new TreeMap<>(); 283 putAll(SINGLETON.getOutputStreamCompressorNames(), SINGLETON, map); 284 archiveStreamProviderIterable().forEach(provider -> putAll(provider.getOutputStreamCompressorNames(), provider, map)); 285 return map; 286 }); 287 } 288 289 public static String getBrotli() { 290 return BROTLI; 291 } 292 293 public static String getBzip2() { 294 return BZIP2; 295 } 296 297 public static String getDeflate() { 298 return DEFLATE; 299 } 300 301 /** 302 * @since 1.16 303 * @return the constant {@link #DEFLATE64} 304 */ 305 public static String getDeflate64() { 306 return DEFLATE64; 307 } 308 309 public static String getGzip() { 310 return GZIP; 311 } 312 313 public static String getLzma() { 314 return LZMA; 315 } 316 317 public static String getPack200() { 318 return PACK200; 319 } 320 321 public static CompressorStreamFactory getSingleton() { 322 return SINGLETON; 323 } 324 325 public static String getSnappyFramed() { 326 return SNAPPY_FRAMED; 327 } 328 329 public static String getSnappyRaw() { 330 return SNAPPY_RAW; 331 } 332 333 public static String getXz() { 334 return XZ; 335 } 336 337 public static String getZ() { 338 return Z; 339 } 340 341 public static String getLZ4Framed() { 342 return LZ4_FRAMED; 343 } 344 345 public static String getLZ4Block() { 346 return LZ4_BLOCK; 347 } 348 349 public static String getZstandard() { 350 return ZSTANDARD; 351 } 352 353 static void putAll(final Set<String> names, final CompressorStreamProvider provider, final TreeMap<String, CompressorStreamProvider> map) { 354 names.forEach(name -> map.put(toKey(name), provider)); 355 } 356 357 private static Iterable<CompressorStreamProvider> archiveStreamProviderIterable() { 358 return ServiceLoader.load(CompressorStreamProvider.class, ClassLoader.getSystemClassLoader()); 359 } 360 361 private static String toKey(final String name) { 362 return name.toUpperCase(Locale.ROOT); 363 } 364 365 /** 366 * If true, decompress until the end of the input. If false, stop after the 367 * first stream and leave the input position to point to the next byte after 368 * the stream 369 */ 370 private final Boolean decompressUntilEOF; 371 // This is Boolean so setDecompressConcatenated can determine whether it has 372 // been set by the ctor 373 // once the setDecompressConcatenated method has been removed, it can revert 374 // to boolean 375 376 private SortedMap<String, CompressorStreamProvider> compressorInputStreamProviders; 377 378 private SortedMap<String, CompressorStreamProvider> compressorOutputStreamProviders; 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 volatile boolean decompressConcatenated; 386 387 private final int memoryLimitInKb; 388 389 /** 390 * Create an instance with the decompress Concatenated option set to false. 391 */ 392 public CompressorStreamFactory() { 393 this.decompressUntilEOF = null; 394 this.memoryLimitInKb = -1; 395 } 396 397 /** 398 * Create an instance with the provided decompress Concatenated option. 399 * 400 * @param decompressUntilEOF 401 * if true, decompress until the end of the input; if false, stop 402 * after the first stream and leave the input position to point 403 * to the next byte after the stream. This setting applies to the 404 * gzip, bzip2 and xz formats only. 405 * 406 * @param memoryLimitInKb 407 * Some streams require allocation of potentially significant 408 * byte arrays/tables, and they can offer checks to prevent OOMs 409 * on corrupt files. Set the maximum allowed memory allocation in KBs. 410 * 411 * @since 1.14 412 */ 413 public CompressorStreamFactory(final boolean decompressUntilEOF, final int memoryLimitInKb) { 414 this.decompressUntilEOF = decompressUntilEOF; 415 // Also copy to existing variable so can continue to use that as the 416 // current value 417 this.decompressConcatenated = decompressUntilEOF; 418 this.memoryLimitInKb = memoryLimitInKb; 419 } 420 421 /** 422 * Create an instance with the provided decompress Concatenated option. 423 * 424 * @param decompressUntilEOF 425 * if true, decompress until the end of the input; if false, stop 426 * after the first stream and leave the input position to point 427 * to the next byte after the stream. This setting applies to the 428 * gzip, bzip2 and xz formats only. 429 * @since 1.10 430 */ 431 public CompressorStreamFactory(final boolean decompressUntilEOF) { 432 this(decompressUntilEOF, -1); 433 } 434 435 /** 436 * Try to detect the type of compressor stream. 437 * 438 * @param inputStream input stream 439 * @return type of compressor stream detected 440 * @throws CompressorException if no compressor stream type was detected 441 * or if something else went wrong 442 * @throws IllegalArgumentException if stream is null or does not support mark 443 * 444 * @since 1.14 445 */ 446 public static String detect(final InputStream inputStream) throws CompressorException { 447 if (inputStream == null) { 448 throw new IllegalArgumentException("Stream must not be null."); 449 } 450 451 if (!inputStream.markSupported()) { 452 throw new IllegalArgumentException("Mark is not supported."); 453 } 454 455 final byte[] signature = new byte[12]; 456 inputStream.mark(signature.length); 457 int signatureLength = -1; 458 try { 459 signatureLength = IOUtils.readFully(inputStream, signature); 460 inputStream.reset(); 461 } catch (final IOException e) { 462 throw new CompressorException("IOException while reading signature.", e); 463 } 464 465 if (BZip2CompressorInputStream.matches(signature, signatureLength)) { 466 return BZIP2; 467 } 468 469 if (GzipCompressorInputStream.matches(signature, signatureLength)) { 470 return GZIP; 471 } 472 473 if (Pack200CompressorInputStream.matches(signature, signatureLength)) { 474 return PACK200; 475 } 476 477 if (FramedSnappyCompressorInputStream.matches(signature, signatureLength)) { 478 return SNAPPY_FRAMED; 479 } 480 481 if (ZCompressorInputStream.matches(signature, signatureLength)) { 482 return Z; 483 } 484 485 if (DeflateCompressorInputStream.matches(signature, signatureLength)) { 486 return DEFLATE; 487 } 488 489 if (XZUtils.matches(signature, signatureLength)) { 490 return XZ; 491 } 492 493 if (LZMAUtils.matches(signature, signatureLength)) { 494 return LZMA; 495 } 496 497 if (FramedLZ4CompressorInputStream.matches(signature, signatureLength)) { 498 return LZ4_FRAMED; 499 } 500 501 if (ZstdUtils.matches(signature, signatureLength)) { 502 return ZSTANDARD; 503 } 504 505 throw new CompressorException("No Compressor found for the stream signature."); 506 } 507 /** 508 * Create an compressor input stream from an input stream, autodetecting 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 an compressor output stream from an 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}