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