    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 *   https://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.xz;
    020
    021import java.io.IOException;
    022import java.io.OutputStream;
    023
    024import org.apache.commons.compress.compressors.CompressorOutputStream;
    025import org.apache.commons.io.build.AbstractStreamBuilder;
    026import org.tukaani.xz.LZMA2Options;
    027import org.tukaani.xz.XZOutputStream;
    028
    029// @formatter:off
    030/**
    031 * Compresses an output stream using the XZ and LZMA2 compression options.
    032 * <p>
    033 * For example:
    034 * </p>
    035 * <pre>{@code
    036 * XZCompressorOutputStream s = XZCompressorOutputStream.builder()
    037 *   .setPath(path)
    038 *   .setLzma2Options(new LZMA2Options(...))
    039 *   .get();
    040 * }
    041 * </pre>
    042 *
    043 * <h2>Calling flush</h2>
    044 * <p>
    045 * Calling {@link #flush()} flushes the encoder and calls {@code outputStream.flush()}. All buffered pending data will then be decompressible from the output
    046 * stream. Calling this function very often may increase the compressed file size a lot.
    047 * </p>
    048 *
    049 * @since 1.4
    050 */
    051// @formatter:on
    052public class XZCompressorOutputStream extends CompressorOutputStream<XZOutputStream> {
    053
    054    // @formatter:off
    055    /**
    056     * Builds a new {@link XZCompressorOutputStream}.
    057     *
    058     * <p>
    059     * For example:
    060     * </p>
    061     * <pre>{@code
    062     * XZCompressorOutputStream s = XZCompressorOutputStream.builder()
    063     *   .setPath(path)
    064     *   .setLzma2Options(new LZMA2Options(...))
    065     *   .get();
    066     * }
    067     * </pre>
    068     *
    069     * @see #get()
    070     * @since 1.28.0
    071     */
    072    // @formatter:on
    073    public static class Builder extends AbstractStreamBuilder<XZCompressorOutputStream, Builder> {
    074
    075        private LZMA2Options lzma2Options = new LZMA2Options();
    076
    077        /**
    078         * Constructs a new builder of {@link XZCompressorOutputStream}.
    079         */
    080        public Builder() {
    081            // empty
    082        }
    083
    084        @Override
    085        public XZCompressorOutputStream get() throws IOException {
    086            return new XZCompressorOutputStream(this);
    087        }
    088
    089        /**
    090         * Sets LZMA options.
    091         * <p>
    092         * Passing {@code null} resets to the default value {@link LZMA2Options#LZMA2Options()}.
    093         * </p>
    094         *
    095         * @param lzma2Options LZMA options.
    096         * @return this instance.
    097         */
    098        public Builder setLzma2Options(final LZMA2Options lzma2Options) {
    099            this.lzma2Options = lzma2Options != null ? lzma2Options : new LZMA2Options();
    100            return this;
    101        }
    102
    103    }
    104
    105    /**
    106     * Constructs a new builder of {@link XZCompressorOutputStream}.
    107     *
    108     * @return a new builder of {@link XZCompressorOutputStream}.
    109     * @since 1.28.0
    110     */
    111    public static Builder builder() {
    112        return new Builder();
    113    }
    114
    115    @SuppressWarnings("resource") // Caller closes
    116    private XZCompressorOutputStream(final Builder builder) throws IOException {
    117        super(new XZOutputStream(builder.getOutputStream(), builder.lzma2Options));
    118        }
    119
    120    /**
    121     * Creates a new XZ compressor using the default LZMA2 options. This is equivalent to {@code XZCompressorOutputStream(outputStream, 6)}.
    122     *
    123     * @param outputStream the stream to wrap
    124     * @throws IOException on error
    125     */
    126    public XZCompressorOutputStream(final OutputStream outputStream) throws IOException {
    127        this(builder().setOutputStream(outputStream));
    128    }
    129
    130    /**
    131     * Creates a new XZ compressor using the specified LZMA2 preset level.
    132     * <p>
    133     * The presets 0-3 are fast presets with medium compression. The presets 4-6 are fairly slow presets with high compression. The default preset is 6.
    134     * </p>
    135     * <p>
    136     * The presets 7-9 are like the preset 6 but use bigger dictionaries and have higher compressor and decompressor memory requirements. Unless the
    137     * uncompressed size of the file exceeds 8&nbsp;MiB, 16&nbsp;MiB, or 32&nbsp;MiB, it is waste of memory to use the presets 7, 8, or 9, respectively.
    138     * </p>
    139     *
    140     * @param outputStream the stream to wrap
    141     * @param preset       the preset
    142     * @throws IOException on error
    143     * @deprecated Use {@link #builder()}.
    144     */
    145    @Deprecated
    146    @SuppressWarnings("resource") // Caller closes
    147    public XZCompressorOutputStream(final OutputStream outputStream, final int preset) throws IOException {
    148        super(new XZOutputStream(outputStream, new LZMA2Options(preset)));
    149    }
    150
    151    /**
    152     * Finishes compression without closing the underlying stream. No more data can be written to this stream after finishing.
    153     *
    154     * @throws IOException on error
    155     */
    156    @Override
    157    @SuppressWarnings("resource") // instance variable access
    158    public void finish() throws IOException {
    159        out().finish();
    160    }
    161
    162    @Override
    163    public void write(final byte[] buf, final int off, final int len) throws IOException {
    164        out.write(buf, off, len);
    165    }
    166
    167}