• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 package org.apache.commons.compress.compressors;
20 
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Iterator;
29 import java.util.Locale;
30 import java.util.Set;
31 import java.util.SortedMap;
32 import java.util.TreeMap;
33 
34 import org.apache.commons.compress.compressors.brotli.BrotliCompressorInputStream;
35 import org.apache.commons.compress.compressors.brotli.BrotliUtils;
36 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
37 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
38 import org.apache.commons.compress.compressors.deflate.DeflateCompressorInputStream;
39 import org.apache.commons.compress.compressors.deflate.DeflateCompressorOutputStream;
40 import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream;
41 import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
42 import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
43 import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorInputStream;
44 import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorOutputStream;
45 import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream;
46 import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorOutputStream;
47 import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream;
48 import org.apache.commons.compress.compressors.lzma.LZMACompressorOutputStream;
49 import org.apache.commons.compress.compressors.lzma.LZMAUtils;
50 import org.apache.commons.compress.compressors.pack200.Pack200CompressorInputStream;
51 import org.apache.commons.compress.compressors.pack200.Pack200CompressorOutputStream;
52 import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorInputStream;
53 import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorOutputStream;
54 import org.apache.commons.compress.compressors.snappy.SnappyCompressorInputStream;
55 import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
56 import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
57 import org.apache.commons.compress.compressors.xz.XZUtils;
58 import org.apache.commons.compress.compressors.z.ZCompressorInputStream;
59 import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream;
60 import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream;
61 import org.apache.commons.compress.compressors.zstandard.ZstdUtils;
62 import org.apache.commons.compress.utils.IOUtils;
63 import org.apache.commons.compress.utils.Lists;
64 import org.apache.commons.compress.utils.ServiceLoaderIterator;
65 import org.apache.commons.compress.utils.Sets;
66 
67 /**
68  * <p>
69  * Factory to create Compressor[In|Out]putStreams from names. To add other
70  * implementations you should extend CompressorStreamFactory and override the
71  * appropriate methods (and call their implementation from super of course).
72  * </p>
73  *
74  * Example (Compressing a file):
75  *
76  * <pre>
77  * final OutputStream out = Files.newOutputStream(output.toPath());
78  * CompressorOutputStream cos = new CompressorStreamFactory()
79  *         .createCompressorOutputStream(CompressorStreamFactory.BZIP2, out);
80  * IOUtils.copy(Files.newInputStream(input.toPath()), cos);
81  * cos.close();
82  * </pre>
83  *
84  * Example (Decompressing a file):
85  *
86  * <pre>
87  * final InputStream is = Files.newInputStream(input.toPath());
88  * CompressorInputStream in = new CompressorStreamFactory().createCompressorInputStream(CompressorStreamFactory.BZIP2,
89  *         is);
90  * IOUtils.copy(in, Files.newOutputStream(output.toPath()));
91  * in.close();
92  * </pre>
93  *
94  * @Immutable provided that the deprecated method setDecompressConcatenated is
95  *            not used.
96  * @ThreadSafe even if the deprecated method setDecompressConcatenated is used
97  */
98 public class CompressorStreamFactory implements CompressorStreamProvider {
99 
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 
youNeed(String name, String url)216     private static String youNeed(String name, String url) {
217         return " In addition to Apache Commons Compress you need the " + name + " library - see " + url;
218     }
219 
220     /**
221      * Constructs a new sorted map from input stream provider names to provider
222      * objects.
223      *
224      * <p>
225      * The map returned by this method will have one entry for each provider for
226      * which support is available in the current Java virtual machine. If two or
227      * more supported provider have the same name then the resulting map will
228      * contain just one of them; which one it will contain is not specified.
229      * </p>
230      *
231      * <p>
232      * The invocation of this method, and the subsequent use of the resulting
233      * map, may cause time-consuming disk or network I/O operations to occur.
234      * This method is provided for applications that need to enumerate all of
235      * the available providers, for example to allow user provider selection.
236      * </p>
237      *
238      * <p>
239      * This method may return different results at different times if new
240      * providers are dynamically made available to the current Java virtual
241      * machine.
242      * </p>
243      *
244      * @return An immutable, map from names to provider objects
245      * @since 1.13
246      */
findAvailableCompressorInputStreamProviders()247     public static SortedMap<String, CompressorStreamProvider> findAvailableCompressorInputStreamProviders() {
248         return AccessController.doPrivileged(new PrivilegedAction<SortedMap<String, CompressorStreamProvider>>() {
249             @Override
250             public SortedMap<String, CompressorStreamProvider> run() {
251                 final TreeMap<String, CompressorStreamProvider> map = new TreeMap<>();
252                 putAll(SINGLETON.getInputStreamCompressorNames(), SINGLETON, map);
253                 for (final CompressorStreamProvider provider : findCompressorStreamProviders()) {
254                     putAll(provider.getInputStreamCompressorNames(), provider, map);
255                 }
256                 return map;
257             }
258         });
259     }
260 
261     /**
262      * Constructs a new sorted map from output stream provider names to provider
263      * objects.
264      *
265      * <p>
266      * The map returned by this method will have one entry for each provider for
267      * which support is available in the current Java virtual machine. If two or
268      * more supported provider have the same name then the resulting map will
269      * contain just one of them; which one it will contain is not specified.
270      * </p>
271      *
272      * <p>
273      * The invocation of this method, and the subsequent use of the resulting
274      * map, may cause time-consuming disk or network I/O operations to occur.
275      * This method is provided for applications that need to enumerate all of
276      * the available providers, for example to allow user provider selection.
277      * </p>
278      *
279      * <p>
280      * This method may return different results at different times if new
281      * providers are dynamically made available to the current Java virtual
282      * machine.
283      * </p>
284      *
285      * @return An immutable, map from names to provider objects
286      * @since 1.13
287      */
288     public static SortedMap<String, CompressorStreamProvider> findAvailableCompressorOutputStreamProviders() {
289         return AccessController.doPrivileged(new PrivilegedAction<SortedMap<String, CompressorStreamProvider>>() {
290             @Override
291             public SortedMap<String, CompressorStreamProvider> run() {
292                 final TreeMap<String, CompressorStreamProvider> map = new TreeMap<>();
293                 putAll(SINGLETON.getOutputStreamCompressorNames(), SINGLETON, map);
294                 for (final CompressorStreamProvider provider : findCompressorStreamProviders()) {
295                     putAll(provider.getOutputStreamCompressorNames(), provider, map);
296                 }
297                 return map;
298             }
299 
300         });
301     }
302     private static ArrayList<CompressorStreamProvider> findCompressorStreamProviders() {
303         return Lists.newArrayList(serviceLoaderIterator());
304     }
305 
306     public static String getBrotli() {
307         return BROTLI;
308     }
309 
310     public static String getBzip2() {
311         return BZIP2;
312     }
313 
314     public static String getDeflate() {
315         return DEFLATE;
316     }
317 
318     /**
319      * @since 1.16
320      * @return the constant {@link #DEFLATE64}
321      */
322     public static String getDeflate64() {
323         return DEFLATE64;
324     }
325 
326     public static String getGzip() {
327         return GZIP;
328     }
329 
330     public static String getLzma() {
331         return LZMA;
332     }
333 
334     public static String getPack200() {
335         return PACK200;
336     }
337 
338     public static CompressorStreamFactory getSingleton() {
339         return SINGLETON;
340     }
341 
342     public static String getSnappyFramed() {
343         return SNAPPY_FRAMED;
344     }
345 
346     public static String getSnappyRaw() {
347         return SNAPPY_RAW;
348     }
349 
350     public static String getXz() {
351         return XZ;
352     }
353 
354     public static String getZ() {
355         return Z;
356     }
357 
358     public static String getLZ4Framed() {
359         return LZ4_FRAMED;
360     }
361 
362     public static String getLZ4Block() {
363         return LZ4_BLOCK;
364     }
365 
366     public static String getZstandard() {
367         return ZSTANDARD;
368     }
369 
370     static void putAll(final Set<String> names, final CompressorStreamProvider provider,
371             final TreeMap<String, CompressorStreamProvider> map) {
372         for (final String name : names) {
373             map.put(toKey(name), provider);
374         }
375     }
376 
377     private static Iterator<CompressorStreamProvider> serviceLoaderIterator() {
378         return new ServiceLoaderIterator<>(CompressorStreamProvider.class);
379     }
380 
381     private static String toKey(final String name) {
382         return name.toUpperCase(Locale.ROOT);
383     }
384 
385     /**
386      * If true, decompress until the end of the input. If false, stop after the
387      * first stream and leave the input position to point to the next byte after
388      * the stream
389      */
390     private final Boolean decompressUntilEOF;
391     // This is Boolean so setDecompressConcatenated can determine whether it has
392     // been set by the ctor
393     // once the setDecompressConcatenated method has been removed, it can revert
394     // to boolean
395 
396     private SortedMap<String, CompressorStreamProvider> compressorInputStreamProviders;
397 
398     private SortedMap<String, CompressorStreamProvider> compressorOutputStreamProviders;
399 
400     /**
401      * If true, decompress until the end of the input. If false, stop after the
402      * first stream and leave the input position to point to the next byte after
403      * the stream
404      */
405     private volatile boolean decompressConcatenated = false;
406 
407     private final int memoryLimitInKb;
408     /**
409      * Create an instance with the decompress Concatenated option set to false.
410      */
411     public CompressorStreamFactory() {
412         this.decompressUntilEOF = null;
413         this.memoryLimitInKb = -1;
414     }
415 
416     /**
417      * Create an instance with the provided decompress Concatenated option.
418      *
419      * @param decompressUntilEOF
420      *            if true, decompress until the end of the input; if false, stop
421      *            after the first stream and leave the input position to point
422      *            to the next byte after the stream. This setting applies to the
423      *            gzip, bzip2 and xz formats only.
424      *
425      * @param memoryLimitInKb
426      *            Some streams require allocation of potentially significant
427      *            byte arrays/tables, and they can offer checks to prevent OOMs
428      *            on corrupt files.  Set the maximum allowed memory allocation in KBs.
429      *
430      * @since 1.14
431      */
432     public CompressorStreamFactory(final boolean decompressUntilEOF, final int memoryLimitInKb) {
433         this.decompressUntilEOF = decompressUntilEOF;
434         // Also copy to existing variable so can continue to use that as the
435         // current value
436         this.decompressConcatenated = decompressUntilEOF;
437         this.memoryLimitInKb = memoryLimitInKb;
438     }
439 
440 
441     /**
442      * Create an instance with the provided decompress Concatenated option.
443      *
444      * @param decompressUntilEOF
445      *            if true, decompress until the end of the input; if false, stop
446      *            after the first stream and leave the input position to point
447      *            to the next byte after the stream. This setting applies to the
448      *            gzip, bzip2 and xz formats only.
449      * @since 1.10
450      */
451     public CompressorStreamFactory(final boolean decompressUntilEOF) {
452         this(decompressUntilEOF, -1);
453     }
454 
455     /**
456      * Try to detect the type of compressor stream.
457      *
458      * @param in input stream
459      * @return type of compressor stream detected
460      * @throws CompressorException if no compressor stream type was detected
461      *                             or if something else went wrong
462      * @throws IllegalArgumentException if stream is null or does not support mark
463      *
464      * @since 1.14
465      */
466     public static String detect(final InputStream in) throws CompressorException {
467         if (in == null) {
468             throw new IllegalArgumentException("Stream must not be null.");
469         }
470 
471         if (!in.markSupported()) {
472             throw new IllegalArgumentException("Mark is not supported.");
473         }
474 
475         final byte[] signature = new byte[12];
476         in.mark(signature.length);
477         int signatureLength = -1;
478         try {
479             signatureLength = IOUtils.readFully(in, signature);
480             in.reset();
481         } catch (IOException e) {
482             throw new CompressorException("IOException while reading signature.", e);
483         }
484 
485         if (BZip2CompressorInputStream.matches(signature, signatureLength)) {
486             return BZIP2;
487         }
488 
489         if (GzipCompressorInputStream.matches(signature, signatureLength)) {
490             return GZIP;
491         }
492 
493         if (Pack200CompressorInputStream.matches(signature, signatureLength)) {
494             return PACK200;
495         }
496 
497         if (FramedSnappyCompressorInputStream.matches(signature, signatureLength)) {
498             return SNAPPY_FRAMED;
499         }
500 
501         if (ZCompressorInputStream.matches(signature, signatureLength)) {
502             return Z;
503         }
504 
505         if (DeflateCompressorInputStream.matches(signature, signatureLength)) {
506             return DEFLATE;
507         }
508 
509         if (XZUtils.matches(signature, signatureLength)) {
510             return XZ;
511         }
512 
513         if (LZMAUtils.matches(signature, signatureLength)) {
514             return LZMA;
515         }
516 
517         if (FramedLZ4CompressorInputStream.matches(signature, signatureLength)) {
518             return LZ4_FRAMED;
519         }
520 
521         if (ZstdUtils.matches(signature, signatureLength)) {
522             return ZSTANDARD;
523         }
524 
525         throw new CompressorException("No Compressor found for the stream signature.");
526     }
527     /**
528      * Create an compressor input stream from an input stream, autodetecting the
529      * compressor type from the first few bytes of the stream. The InputStream
530      * must support marks, like BufferedInputStream.
531      *
532      * @param in
533      *            the input stream
534      * @return the compressor input stream
535      * @throws CompressorException
536      *             if the compressor name is not known
537      * @throws IllegalArgumentException
538      *             if the stream is null or does not support mark
539      * @since 1.1
540      */
541     public CompressorInputStream createCompressorInputStream(final InputStream in) throws CompressorException {
542         return createCompressorInputStream(detect(in), in);
543     }
544 
545     /**
546      * Creates a compressor input stream from a compressor name and an input
547      * stream.
548      *
549      * @param name
550      *            of the compressor, i.e. {@value #GZIP}, {@value #BZIP2},
551      *            {@value #XZ}, {@value #LZMA}, {@value #PACK200},
552      *            {@value #SNAPPY_RAW}, {@value #SNAPPY_FRAMED}, {@value #Z},
553      *            {@value #LZ4_BLOCK}, {@value #LZ4_FRAMED}, {@value #ZSTANDARD},
554      *            {@value #DEFLATE64}
555      *            or {@value #DEFLATE}
556      * @param in
557      *            the input stream
558      * @return compressor input stream
559      * @throws CompressorException
560      *             if the compressor name is not known or not available,
561      *             or if there's an IOException or MemoryLimitException thrown
562      *             during initialization
563      * @throws IllegalArgumentException
564      *             if the name or input stream is null
565      */
566     public CompressorInputStream createCompressorInputStream(final String name, final InputStream in)
567             throws CompressorException {
568         return createCompressorInputStream(name, in, decompressConcatenated);
569     }
570 
571     @Override
572     public CompressorInputStream createCompressorInputStream(final String name, final InputStream in,
573             final boolean actualDecompressConcatenated) throws CompressorException {
574         if (name == null || in == null) {
575             throw new IllegalArgumentException("Compressor name and stream must not be null.");
576         }
577 
578         try {
579 
580             if (GZIP.equalsIgnoreCase(name)) {
581                 return new GzipCompressorInputStream(in, actualDecompressConcatenated);
582             }
583 
584             if (BZIP2.equalsIgnoreCase(name)) {
585                 return new BZip2CompressorInputStream(in, actualDecompressConcatenated);
586             }
587 
588             if (BROTLI.equalsIgnoreCase(name)) {
589                 if (!BrotliUtils.isBrotliCompressionAvailable()) {
590                     throw new CompressorException("Brotli compression is not available." + YOU_NEED_BROTLI_DEC);
591                 }
592                 return new BrotliCompressorInputStream(in);
593             }
594 
595             if (XZ.equalsIgnoreCase(name)) {
596                 if (!XZUtils.isXZCompressionAvailable()) {
597                     throw new CompressorException("XZ compression is not available." + YOU_NEED_XZ_JAVA);
598                 }
599                 return new XZCompressorInputStream(in, actualDecompressConcatenated, memoryLimitInKb);
600             }
601 
602             if (ZSTANDARD.equalsIgnoreCase(name)) {
603                 if (!ZstdUtils.isZstdCompressionAvailable()) {
604                     throw new CompressorException("Zstandard compression is not available." + YOU_NEED_ZSTD_JNI);
605                 }
606                 return new ZstdCompressorInputStream(in);
607             }
608 
609             if (LZMA.equalsIgnoreCase(name)) {
610                 if (!LZMAUtils.isLZMACompressionAvailable()) {
611                     throw new CompressorException("LZMA compression is not available" + YOU_NEED_XZ_JAVA);
612                 }
613                 return new LZMACompressorInputStream(in, memoryLimitInKb);
614             }
615 
616             if (PACK200.equalsIgnoreCase(name)) {
617                 return new Pack200CompressorInputStream(in);
618             }
619 
620             if (SNAPPY_RAW.equalsIgnoreCase(name)) {
621                 return new SnappyCompressorInputStream(in);
622             }
623 
624             if (SNAPPY_FRAMED.equalsIgnoreCase(name)) {
625                 return new FramedSnappyCompressorInputStream(in);
626             }
627 
628             if (Z.equalsIgnoreCase(name)) {
629                 return new ZCompressorInputStream(in, memoryLimitInKb);
630             }
631 
632             if (DEFLATE.equalsIgnoreCase(name)) {
633                 return new DeflateCompressorInputStream(in);
634             }
635 
636             if (DEFLATE64.equalsIgnoreCase(name)) {
637                 return new Deflate64CompressorInputStream(in);
638             }
639 
640             if (LZ4_BLOCK.equalsIgnoreCase(name)) {
641                 return new BlockLZ4CompressorInputStream(in);
642             }
643 
644             if (LZ4_FRAMED.equalsIgnoreCase(name)) {
645                 return new FramedLZ4CompressorInputStream(in, actualDecompressConcatenated);
646             }
647 
648         } catch (final IOException e) {
649             throw new CompressorException("Could not create CompressorInputStream.", e);
650         }
651         final CompressorStreamProvider compressorStreamProvider = getCompressorInputStreamProviders().get(toKey(name));
652         if (compressorStreamProvider != null) {
653             return compressorStreamProvider.createCompressorInputStream(name, in, actualDecompressConcatenated);
654         }
655 
656         throw new CompressorException("Compressor: " + name + " not found.");
657     }
658 
659     /**
660      * Creates an compressor output stream from an compressor name and an output
661      * stream.
662      *
663      * @param name
664      *            the compressor name, i.e. {@value #GZIP}, {@value #BZIP2},
665      *            {@value #XZ}, {@value #PACK200}, {@value #SNAPPY_FRAMED},
666      *            {@value #LZ4_BLOCK}, {@value #LZ4_FRAMED}, {@value #ZSTANDARD}
667      *            or {@value #DEFLATE}
668      * @param out
669      *            the output stream
670      * @return the compressor output stream
671      * @throws CompressorException
672      *             if the archiver name is not known
673      * @throws IllegalArgumentException
674      *             if the archiver name or stream is null
675      */
676     @Override
677     public CompressorOutputStream createCompressorOutputStream(final String name, final OutputStream out)
678             throws CompressorException {
679         if (name == null || out == null) {
680             throw new IllegalArgumentException("Compressor name and stream must not be null.");
681         }
682 
683         try {
684 
685             if (GZIP.equalsIgnoreCase(name)) {
686                 return new GzipCompressorOutputStream(out);
687             }
688 
689             if (BZIP2.equalsIgnoreCase(name)) {
690                 return new BZip2CompressorOutputStream(out);
691             }
692 
693             if (XZ.equalsIgnoreCase(name)) {
694                 return new XZCompressorOutputStream(out);
695             }
696 
697             if (PACK200.equalsIgnoreCase(name)) {
698                 return new Pack200CompressorOutputStream(out);
699             }
700 
701             if (LZMA.equalsIgnoreCase(name)) {
702                 return new LZMACompressorOutputStream(out);
703             }
704 
705             if (DEFLATE.equalsIgnoreCase(name)) {
706                 return new DeflateCompressorOutputStream(out);
707             }
708 
709             if (SNAPPY_FRAMED.equalsIgnoreCase(name)) {
710                 return new FramedSnappyCompressorOutputStream(out);
711             }
712 
713             if (LZ4_BLOCK.equalsIgnoreCase(name)) {
714                 return new BlockLZ4CompressorOutputStream(out);
715             }
716 
717             if (LZ4_FRAMED.equalsIgnoreCase(name)) {
718                 return new FramedLZ4CompressorOutputStream(out);
719             }
720 
721             if (ZSTANDARD.equalsIgnoreCase(name)) {
722                 return new ZstdCompressorOutputStream(out);
723             }
724         } catch (final IOException e) {
725             throw new CompressorException("Could not create CompressorOutputStream", e);
726         }
727         final CompressorStreamProvider compressorStreamProvider = getCompressorOutputStreamProviders().get(toKey(name));
728         if (compressorStreamProvider != null) {
729             return compressorStreamProvider.createCompressorOutputStream(name, out);
730         }
731         throw new CompressorException("Compressor: " + name + " not found.");
732     }
733 
734     public SortedMap<String, CompressorStreamProvider> getCompressorInputStreamProviders() {
735         if (compressorInputStreamProviders == null) {
736             compressorInputStreamProviders = Collections
737                     .unmodifiableSortedMap(findAvailableCompressorInputStreamProviders());
738         }
739         return compressorInputStreamProviders;
740     }
741 
742     public SortedMap<String, CompressorStreamProvider> getCompressorOutputStreamProviders() {
743         if (compressorOutputStreamProviders == null) {
744             compressorOutputStreamProviders = Collections
745                     .unmodifiableSortedMap(findAvailableCompressorOutputStreamProviders());
746         }
747         return compressorOutputStreamProviders;
748     }
749 
750     // For Unit tests
751     boolean getDecompressConcatenated() {
752         return decompressConcatenated;
753     }
754 
755     public Boolean getDecompressUntilEOF() {
756         return decompressUntilEOF;
757     }
758 
759     @Override
760     public Set<String> getInputStreamCompressorNames() {
761         return Sets.newHashSet(GZIP, BROTLI, BZIP2, XZ, LZMA, PACK200, DEFLATE, SNAPPY_RAW, SNAPPY_FRAMED, Z, LZ4_BLOCK,
762             LZ4_FRAMED, ZSTANDARD, DEFLATE64);
763     }
764 
765     @Override
766     public Set<String> getOutputStreamCompressorNames() {
767         return Sets.newHashSet(GZIP, BZIP2, XZ, LZMA, PACK200, DEFLATE, SNAPPY_FRAMED, LZ4_BLOCK, LZ4_FRAMED, ZSTANDARD);
768     }
769 
770     /**
771      * Whether to decompress the full input or only the first stream in formats
772      * supporting multiple concatenated input streams.
773      *
774      * <p>
775      * This setting applies to the gzip, bzip2 and xz formats only.
776      * </p>
777      *
778      * @param decompressConcatenated
779      *            if true, decompress until the end of the input; if false, stop
780      *            after the first stream and leave the input position to point
781      *            to the next byte after the stream
782      * @since 1.5
783      * @deprecated 1.10 use the {@link #CompressorStreamFactory(boolean)}
784      *             constructor instead
785      * @throws IllegalStateException
786      *             if the constructor {@link #CompressorStreamFactory(boolean)}
787      *             was used to create the factory
788      */
789     @Deprecated
790     public void setDecompressConcatenated(final boolean decompressConcatenated) {
791         if (this.decompressUntilEOF != null) {
792             throw new IllegalStateException("Cannot override the setting defined by the constructor");
793         }
794         this.decompressConcatenated = decompressConcatenated;
795     }
796 
797 }
798