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