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     */
019    package org.apache.commons.compress.compressors;
020    
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.OutputStream;
024    
025    import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
026    import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
027    import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
028    import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
029    import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
030    import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
031    import org.apache.commons.compress.compressors.pack200.Pack200CompressorInputStream;
032    import org.apache.commons.compress.compressors.pack200.Pack200CompressorOutputStream;
033    
034    /**
035     * <p>Factory to create Compressor[In|Out]putStreams from names. To add other
036     * implementations you should extend CompressorStreamFactory and override the
037     * appropriate methods (and call their implementation from super of course).</p>
038     * 
039     * Example (Compressing a file):
040     * 
041     * <pre>
042     * final OutputStream out = new FileOutputStream(output); 
043     * CompressorOutputStream cos = 
044     *      new CompressorStreamFactory().createCompressorOutputStream(CompressorStreamFactory.BZIP2, out);
045     * IOUtils.copy(new FileInputStream(input), cos);
046     * cos.close();
047     * </pre>
048     * 
049     * Example (Compressing a file):
050     * <pre>
051     * final InputStream is = new FileInputStream(input); 
052     * CompressorInputStream in = 
053     *      new CompressorStreamFactory().createCompressorInputStream(CompressorStreamFactory.BZIP2, is);
054     * IOUtils.copy(in, new FileOutputStream(output));
055     * in.close();
056     * </pre>
057     * 
058     * @Immutable
059     */
060    public class CompressorStreamFactory {
061    
062        /**
063         * Constant used to identify the BZIP2 compression algorithm.
064         * @since Commons Compress 1.1
065         */
066        public static final String BZIP2 = "bzip2";
067    
068        /**
069         * Constant used to identify the GZIP compression algorithm.
070         * @since Commons Compress 1.1
071         */
072        public static final String GZIP = "gz";
073        /**
074         * Constant used to identify the PACK200 compression algorithm.
075         * @since Commons Compress 1.3
076         */
077        public static final String PACK200 = "pack200";
078    
079        /**
080         * Constant used to identify the XZ compression method.
081         * @since Commons Compress 1.4
082         */
083        public static final String XZ = "xz";
084    
085        /**
086         * Create an compressor input stream from an input stream, autodetecting
087         * the compressor type from the first few bytes of the stream. The InputStream
088         * must support marks, like BufferedInputStream.
089         * 
090         * @param in the input stream
091         * @return the compressor input stream
092         * @throws CompressorException if the compressor name is not known
093         * @throws IllegalArgumentException if the stream is null or does not support mark
094         * @since Commons Compress 1.1
095         */
096        public CompressorInputStream createCompressorInputStream(final InputStream in)
097                throws CompressorException {
098            if (in == null) {
099                throw new IllegalArgumentException("Stream must not be null.");
100            }
101    
102            if (!in.markSupported()) {
103                throw new IllegalArgumentException("Mark is not supported.");
104            }
105    
106            final byte[] signature = new byte[12];
107            in.mark(signature.length);
108            try {
109                int signatureLength = in.read(signature);
110                in.reset();
111    
112                if (BZip2CompressorInputStream.matches(signature, signatureLength)) {
113                    return new BZip2CompressorInputStream(in);
114                }
115    
116                if (GzipCompressorInputStream.matches(signature, signatureLength)) {
117                    return new GzipCompressorInputStream(in);
118                }
119    
120                if (XZCompressorInputStream.matches(signature, signatureLength)) {
121                    return new XZCompressorInputStream(in);
122                }
123    
124                if (Pack200CompressorInputStream.matches(signature, signatureLength)) {
125                    return new Pack200CompressorInputStream(in);
126                }
127    
128            } catch (IOException e) {
129                throw new CompressorException("Failed to detect Compressor from InputStream.", e);
130            }
131    
132            throw new CompressorException("No Compressor found for the stream signature.");
133        }
134    
135        /**
136         * Create a compressor input stream from a compressor name and an input stream.
137         * 
138         * @param name of the compressor, i.e. "gz", "bzip2", "xz", or "pack200"
139         * @param in the input stream
140         * @return compressor input stream
141         * @throws CompressorException if the compressor name is not known
142         * @throws IllegalArgumentException if the name or input stream is null
143         */
144        public CompressorInputStream createCompressorInputStream(final String name,
145                final InputStream in) throws CompressorException {
146            if (name == null || in == null) {
147                throw new IllegalArgumentException(
148                        "Compressor name and stream must not be null.");
149            }
150    
151            try {
152    
153                if (GZIP.equalsIgnoreCase(name)) {
154                    return new GzipCompressorInputStream(in);
155                }
156    
157                if (BZIP2.equalsIgnoreCase(name)) {
158                    return new BZip2CompressorInputStream(in);
159                }
160    
161                if (XZ.equalsIgnoreCase(name)) {
162                    return new XZCompressorInputStream(in);
163                }
164    
165                if (PACK200.equalsIgnoreCase(name)) {
166                    return new Pack200CompressorInputStream(in);
167                }
168    
169            } catch (IOException e) {
170                throw new CompressorException(
171                        "Could not create CompressorInputStream.", e);
172            }
173            throw new CompressorException("Compressor: " + name + " not found.");
174        }
175    
176        /**
177         * Create an compressor output stream from an compressor name and an input stream.
178         * 
179         * @param name the compressor name, i.e. "gz", "bzip2", "xz", or "pack200"
180         * @param out the output stream
181         * @return the compressor output stream
182         * @throws CompressorException if the archiver name is not known
183         * @throws IllegalArgumentException if the archiver name or stream is null
184         */
185        public CompressorOutputStream createCompressorOutputStream(
186                final String name, final OutputStream out)
187                throws CompressorException {
188            if (name == null || out == null) {
189                throw new IllegalArgumentException(
190                        "Compressor name and stream must not be null.");
191            }
192    
193            try {
194    
195                if (GZIP.equalsIgnoreCase(name)) {
196                    return new GzipCompressorOutputStream(out);
197                }
198    
199                if (BZIP2.equalsIgnoreCase(name)) {
200                    return new BZip2CompressorOutputStream(out);
201                }
202    
203                if (XZ.equalsIgnoreCase(name)) {
204                    return new XZCompressorOutputStream(out);
205                }
206    
207                if (PACK200.equalsIgnoreCase(name)) {
208                    return new Pack200CompressorOutputStream(out);
209                }
210    
211            } catch (IOException e) {
212                throw new CompressorException(
213                        "Could not create CompressorOutputStream", e);
214            }
215            throw new CompressorException("Compressor: " + name + " not found.");
216        }
217    }