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