/*
- * jSite2 - FcpUtils.java -
- * Copyright © 2008 David Roden
+ * jFCPlib - FcpUtils.java - Copyright © 2008 David Roden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
package net.pterodactylus.fcp;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Helper class with utility methods for the FCP protocol.
- *
- * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
*/
public class FcpUtils {
/**
* Returns a unique identifier.
- *
+ *
* @return A unique identifier
*/
public static String getUniqueIdentifier() {
/**
* Parses an integer field, separated by ‘;’ and returns the parsed values.
- *
+ *
* @param field
* The field to parse
* @return An array with the parsed values
/**
* Encodes the given integer array into a string, separating the values by
* ‘;’.
- *
+ *
* @param values
* The values to encode
* @return The encoded values
*/
public static String encodeMultiIntegerField(int[] values) {
StringBuilder encodedField = new StringBuilder();
- for (int value: values) {
+ for (int value : values) {
if (encodedField.length() > 0) {
encodedField.append(';');
}
/**
* Encodes the given string array into a string, separating the values by
* ‘;’.
- *
+ *
* @param values
* The values to encode
* @return The encoded values
*/
public static String encodeMultiStringField(String[] values) {
StringBuilder encodedField = new StringBuilder();
- for (String value: values) {
+ for (String value : values) {
if (encodedField.length() > 0) {
encodedField.append(';');
}
}
/**
- * Tries to parse the given string into an int, returning <code>-1</code>
- * if the string can not be parsed.
- *
+ * Tries to parse the given string into an int, returning <code>-1</code> if
+ * the string can not be parsed.
+ *
* @param value
* The string to parse
* @return The parsed int, or <code>-1</code>
/**
* Tries to parse the given string into an int, returning
* <code>defaultValue</code> if the string can not be parsed.
- *
+ *
* @param value
* The string to parse
* @param defaultValue
/**
* Tries to parse the given string into an long, returning <code>-1</code>
* if the string can not be parsed.
- *
+ *
* @param value
* The string to parse
* @return The parsed long, or <code>-1</code>
/**
* Tries to parse the given string into an long, returning
* <code>defaultValue</code> if the string can not be parsed.
- *
+ *
* @param value
* The string to parse
* @param defaultValue
/**
* Closes the given socket.
- *
+ *
* @param socket
* The socket to close
*/
/**
* Closes the given Closeable.
- *
+ *
* @param closeable
* The Closeable to close
*/
/**
* Copies as many bytes as possible (i.e. until {@link InputStream#read()}
- * returns <code>-1</code>) from the source input stream to the
- * destination output stream.
- *
+ * returns <code>-1</code>) from the source input stream to the destination
+ * output stream.
+ *
* @param source
* The input stream to read from
* @param destination
/**
* Copies <code>length</code> bytes from the source input stream to the
- * destination output stream. If <code>length</code> is <code>-1</code>
- * as much bytes as possible will be copied (i.e. until
+ * destination output stream. If <code>length</code> is <code>-1</code> as
+ * much bytes as possible will be copied (i.e. until
* {@link InputStream#read()} returns <code>-1</code> to signal the end of
* the stream).
- *
+ *
* @param source
* The input stream to read from
* @param destination
/**
* Copies <code>length</code> bytes from the source input stream to the
- * destination output stream. If <code>length</code> is <code>-1</code>
- * as much bytes as possible will be copied (i.e. until
+ * destination output stream. If <code>length</code> is <code>-1</code> as
+ * much bytes as possible will be copied (i.e. until
* {@link InputStream#read()} returns <code>-1</code> to signal the end of
* the stream).
- *
+ *
* @param source
* The input stream to read from
* @param destination
}
}
+ /**
+ * This input stream stores the content of another input stream either in a
+ * file or in memory, depending on the length of the input stream.
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ */
+ public static class TempInputStream extends InputStream {
+
+ /** The default maximum lenght for in-memory storage. */
+ public static final long MAX_LENGTH_MEMORY = 65536;
+
+ /** The temporary file to read from. */
+ private final File tempFile;
+
+ /** The input stream that reads from the file. */
+ private final InputStream fileInputStream;
+
+ /** The input stream that reads from memory. */
+ private final InputStream memoryInputStream;
+
+ /**
+ * Creates a new temporary input stream that stores the given input
+ * stream in a temporary file.
+ *
+ * @param originalInputStream
+ * The original input stream
+ * @throws IOException
+ * if an I/O error occurs
+ */
+ public TempInputStream(InputStream originalInputStream) throws IOException {
+ this(originalInputStream, -1);
+ }
+
+ /**
+ * Creates a new temporary input stream that stores the given input
+ * stream in memory if it is shorter than {@link #MAX_LENGTH_MEMORY},
+ * otherwise it is stored in a file.
+ *
+ * @param originalInputStream
+ * The original input stream
+ * @param length
+ * The length of the input stream
+ * @throws IOException
+ * if an I/O error occurs
+ */
+ public TempInputStream(InputStream originalInputStream, long length) throws IOException {
+ this(originalInputStream, length, MAX_LENGTH_MEMORY);
+ }
+
+ /**
+ * Creates a new temporary input stream that stores the given input
+ * stream in memory if it is shorter than <code>maxMemoryLength</code>,
+ * otherwise it is stored in a file.
+ *
+ * @param originalInputStream
+ * The original input stream
+ * @param length
+ * The length of the input stream
+ * @param maxMemoryLength
+ * The maximum length to store in memory
+ * @throws IOException
+ * if an I/O error occurs
+ */
+ public TempInputStream(InputStream originalInputStream, long length, long maxMemoryLength) throws IOException {
+ if ((length > -1) && (length <= maxMemoryLength)) {
+ ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream((int) length);
+ try {
+ FcpUtils.copy(originalInputStream, memoryOutputStream, length, (int) length);
+ } finally {
+ memoryOutputStream.close();
+ }
+ tempFile = null;
+ fileInputStream = null;
+ memoryInputStream = new ByteArrayInputStream(memoryOutputStream.toByteArray());
+ } else {
+ tempFile = File.createTempFile("temp-", ".bin");
+ tempFile.deleteOnExit();
+ FileOutputStream fileOutputStream = null;
+ try {
+ fileOutputStream = new FileOutputStream(tempFile);
+ FcpUtils.copy(originalInputStream, fileOutputStream);
+ fileInputStream = new FileInputStream(tempFile);
+ } finally {
+ FcpUtils.close(fileOutputStream);
+ }
+ memoryInputStream = null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int available() throws IOException {
+ if (memoryInputStream != null) {
+ return memoryInputStream.available();
+ }
+ return fileInputStream.available();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void close() throws IOException {
+ if (memoryInputStream != null) {
+ memoryInputStream.close();
+ return;
+ }
+ tempFile.delete();
+ fileInputStream.close();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void mark(int readlimit) {
+ if (memoryInputStream != null) {
+ memoryInputStream.mark(readlimit);
+ return;
+ }
+ fileInputStream.mark(readlimit);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean markSupported() {
+ if (memoryInputStream != null) {
+ return memoryInputStream.markSupported();
+ }
+ return fileInputStream.markSupported();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int read() throws IOException {
+ if (memoryInputStream != null) {
+ return memoryInputStream.read();
+ }
+ return fileInputStream.read();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int read(byte[] b) throws IOException {
+ if (memoryInputStream != null) {
+ return memoryInputStream.read(b);
+ }
+ return fileInputStream.read(b);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (memoryInputStream != null) {
+ return memoryInputStream.read(b, off, len);
+ }
+ return fileInputStream.read(b, off, len);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void reset() throws IOException {
+ if (memoryInputStream != null) {
+ memoryInputStream.reset();
+ return;
+ }
+ fileInputStream.reset();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long skip(long n) throws IOException {
+ if (memoryInputStream != null) {
+ return memoryInputStream.skip(n);
+ }
+ return fileInputStream.skip(n);
+ }
+
+ }
+
}