| /******************************************************************************* |
| * Copyright (c) 2007, 2018 compeople AG and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * compeople AG (Stefan Liebig) - initial API and implementation |
| * IBM Corporation - bug fixes and enhancements |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.sar; |
| |
| import java.io.*; |
| |
| /** |
| * The SarOutputStream writes a stream archive as an OutputStream. Methods are |
| * provided to put entries, and then write their contents by writing to this |
| * stream using write(). |
| */ |
| public class SarOutputStream extends OutputStream { |
| |
| private boolean finished; |
| private final DataOutputStream dataOutputStream; |
| private final DirectByteArrayOutputStream entryContent; |
| |
| /** |
| * @param outputStream |
| * @throws IOException |
| */ |
| public SarOutputStream(OutputStream outputStream) throws IOException { |
| dataOutputStream = new DataOutputStream(outputStream); |
| entryContent = new DirectByteArrayOutputStream(16 * 1024); |
| writeString(SarConstants.SARFILE_MARKER); |
| dataOutputStream.writeInt(SarConstants.SARFILE_VERSION); |
| finished = false; |
| } |
| |
| /** |
| * Ends the SAR archive and closes the underlying OutputStream. |
| * |
| * @see java.io.OutputStream#close() |
| */ |
| @Override |
| public void close() throws IOException { |
| finish(); |
| super.close(); |
| } |
| |
| /** |
| * Finish this SAR archive but does not close the underlying output stream. |
| * |
| * @throws IOException |
| */ |
| public void finish() throws IOException { |
| if (finished) |
| return; |
| |
| writeEOFRecord(); |
| finished = true; |
| } |
| |
| /** |
| * Put an entry on the output stream. This writes the entry's header record and |
| * positions the output stream for writing the contents of the entry. Once this |
| * method is called, the stream is ready for calls to write() to write the |
| * entry's contents. Once the contents are written, closeEntry() <B>MUST </B> be |
| * called to ensure that all buffered data is completely written to the output |
| * stream. |
| * |
| * @param entry the SarEntry to be written to the archive. |
| * @throws IOException |
| */ |
| public void putNextEntry(SarEntry entry) throws IOException { |
| entry.writeTo(this); |
| } |
| |
| /** |
| * Close an entry. This method MUST be called for all file entries that contain |
| * data. The reason is that we must buffer data written to the stream in order |
| * to satisfy the buffer's record based writes. Thus, there may be data |
| * fragments still being assembled that must be written to the output stream |
| * before this entry is closed and the next entry written. |
| * |
| * @throws IOException |
| */ |
| public void closeEntry() throws IOException { |
| writeBytes(entryContent.getBuffer(), entryContent.getBufferLength()); |
| entryContent.reset(); |
| } |
| |
| /** |
| * @param s |
| * @throws IOException |
| */ |
| void writeString(String s) throws IOException { |
| byte[] bytes = null; |
| if (s != null) |
| bytes = s.getBytes(SarConstants.DEFAULT_ENCODING); |
| |
| writeBytes(bytes); |
| } |
| |
| /** |
| * @param bytes |
| * @throws IOException |
| */ |
| void writeBytes(byte[] bytes) throws IOException { |
| writeBytes(bytes, bytes != null ? bytes.length : -1); |
| } |
| |
| /** |
| * @param bytes |
| * @throws IOException |
| */ |
| void writeBytes(byte[] bytes, int length) throws IOException { |
| if (bytes != null) { |
| dataOutputStream.writeInt(length); |
| dataOutputStream.write(bytes, 0, length); |
| } else { |
| dataOutputStream.writeInt(-1); |
| } |
| } |
| |
| /** |
| * @param v |
| * @throws IOException |
| */ |
| void writeInt(int v) throws IOException { |
| dataOutputStream.writeInt(v); |
| } |
| |
| /** |
| * @param bool |
| * @throws IOException |
| */ |
| public void writeBool(boolean bool) throws IOException { |
| dataOutputStream.writeBoolean(bool); |
| } |
| |
| /** |
| * @param v |
| * @throws IOException |
| */ |
| void writeLong(long v) throws IOException { |
| dataOutputStream.writeLong(v); |
| } |
| |
| /** |
| * Writes a byte to the current org.eclipse.equinox.p2.sar archive entry. |
| * |
| * @param b the byte written. |
| * @throws IOException |
| * |
| * @see java.io.OutputStream#write(int) |
| */ |
| @Override |
| public void write(int b) throws IOException { |
| byte[] bytes = new byte[1]; |
| bytes[0] = (byte) b; |
| entryContent.write(bytes); |
| } |
| |
| /** |
| * Writes bytes to the current org.eclipse.equinox.p2.sar archive entry. |
| * |
| * @param bytes The buffer to write to the archive. |
| * @throws IOException |
| * |
| * @see java.io.OutputStream#write(byte[]) |
| */ |
| @Override |
| public void write(byte[] bytes) throws IOException { |
| entryContent.write(bytes, 0, bytes.length); |
| } |
| |
| /** |
| * Writes bytes to the current org.eclipse.equinox.p2.sar archive entry. |
| * |
| * @param bytes The buffer to write to the archive. |
| * @param offset The offset in the buffer from which to get bytes. |
| * @param numToWrite The number of bytes to write. |
| * |
| * @throws IOException |
| * |
| * @see java.io.OutputStream#write(byte[], int, int) |
| */ |
| @Override |
| public void write(byte[] bytes, int offset, int numToWrite) throws IOException { |
| entryContent.write(bytes, offset, numToWrite); |
| } |
| |
| /** |
| * Write an EOF (end of archive) entry to the org.eclipse.equinox.p2.sar |
| * archive. |
| * |
| * @throws IOException |
| */ |
| private void writeEOFRecord() throws IOException { |
| SarEntry eofEntry = new SarEntry(); |
| eofEntry.writeTo(this); |
| } |
| |
| } |