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; 020 021import java.io.File; 022import java.io.FilterOutputStream; 023import java.io.IOException; 024import java.io.OutputStream; 025import java.nio.charset.Charset; 026import java.nio.charset.StandardCharsets; 027import java.nio.file.Files; 028import java.nio.file.Path; 029import java.util.concurrent.atomic.AtomicBoolean; 030 031/** 032 * Abstracts classes that compress or archive an output stream. 033 * 034 * @param <T> The underlying {@link OutputStream} type. 035 * @since 1.28.0 036 */ 037public abstract class CompressFilterOutputStream<T extends OutputStream> extends FilterOutputStream { 038 039 /** 040 * Writes and filters the bytes from the specified String to this output stream using the given Charset. 041 * 042 * @param os the target output stream. 043 * @param data the data. 044 * @param charset The {@link Charset} to be used to encode the {@code String} 045 * @return the ASCII bytes. 046 * @exception IOException if an I/O error occurs. 047 * @see OutputStream#write(byte[]) 048 */ 049 private static byte[] write(final OutputStream os, final String data, final Charset charset) throws IOException { 050 final byte[] bytes = data.getBytes(charset); 051 os.write(bytes); 052 return bytes; 053 } 054 055 /** 056 * Whether this instance was successfully closed. 057 */ 058 private final AtomicBoolean closed = new AtomicBoolean(); 059 /** 060 * Whether this instance was successfully finished. 061 * <p> 062 * The state transition usually is open, to finished, to closed. 063 * </p> 064 */ 065 private boolean finished; 066 067 /** 068 * Constructs a new instance without a backing {@link OutputStream}. 069 * <p> 070 * You must initialize {@code this.out} after construction. 071 * </p> 072 */ 073 public CompressFilterOutputStream() { 074 super(null); 075 } 076 077 /** 078 * Creates an output stream filter built on top of the specified underlying {@link OutputStream}. 079 * 080 * @param out the underlying output stream to be assigned to the field {@code this.out} for later use, or {@code null} if this instance is to be created 081 * without an underlying stream. 082 */ 083 public CompressFilterOutputStream(final T out) { 084 super(out); 085 } 086 087 /** 088 * Check to make sure that this stream has not been closed. 089 * 090 * @throws IOException if the stream is already closed. 091 */ 092 protected void checkOpen() throws IOException { 093 if (isClosed()) { 094 throw new IOException("Stream closed"); 095 } 096 } 097 098 @Override 099 public void close() throws IOException { 100 if (closed.compareAndSet(false, true)) { 101 // don't propagate more than once. 102 super.close(); 103 } 104 } 105 106 /** 107 * Finishes the addition of entries to this stream, without closing it. Additional data can be written, if the format supports it. 108 * 109 * @throws IOException Maybe thrown by subclasses if the user forgets to close the entry. 110 */ 111 public void finish() throws IOException { 112 finished = true; 113 } 114 115 /** 116 * Tests whether this instance was successfully closed. 117 * 118 * @return whether this instance was successfully closed. 119 * @since 1.27.0 120 */ 121 public boolean isClosed() { 122 return closed.get(); 123 } 124 125 /** 126 * Tests whether this instance was successfully finished. 127 * 128 * @return whether this instance was successfully finished. 129 * @since 1.27.0 130 */ 131 protected boolean isFinished() { 132 return finished; 133 } 134 135 /** 136 * Gets the underlying output stream. 137 * 138 * @return the underlying output stream. 139 */ 140 @SuppressWarnings("unchecked") 141 protected T out() { 142 return (T) out; 143 } 144 145 /** 146 * Writes all bytes from a file this output stream. 147 * 148 * @param file the path to the source file. 149 * @return the number of bytes read or written. 150 * @throws IOException if an I/O error occurs when reading or writing. 151 */ 152 public long write(final File file) throws IOException { 153 return write(file.toPath()); 154 } 155 156 /** 157 * Writes all bytes from a file to this output stream. 158 * 159 * @param path the path to the source file. 160 * @return the number of bytes read or written. 161 * @throws IOException if an I/O error occurs when reading or writing. 162 */ 163 public long write(final Path path) throws IOException { 164 return Files.copy(path, this); 165 } 166 167 /** 168 * Writes and filters the ASCII bytes from the specified String to this output stream. 169 * 170 * @param data the data. 171 * @return the ASCII bytes. 172 * @throws IOException if an I/O error occurs. 173 * @see OutputStream#write(byte[]) 174 */ 175 public byte[] writeUsAscii(final String data) throws IOException { 176 return write(this, data, StandardCharsets.US_ASCII); 177 } 178 179 /** 180 * Writes the raw ASCII bytes from the specified String to this output stream. 181 * 182 * @param data the data. 183 * @return the ASCII bytes. 184 * @throws IOException if an I/O error occurs. 185 * @see OutputStream#write(byte[]) 186 */ 187 public byte[] writeUsAsciiRaw(final String data) throws IOException { 188 return write(out, data, StandardCharsets.US_ASCII); 189 } 190 191 /** 192 * Writes and filters the UTF-8 bytes from the specified String to this output stream. 193 * 194 * @param data the data. 195 * @return the ASCII bytes. 196 * @throws IOException if an I/O error occurs. 197 * @see OutputStream#write(byte[]) 198 */ 199 public byte[] writeUtf8(final String data) throws IOException { 200 return write(this, data, StandardCharsets.UTF_8); 201 } 202}