2 * jFCPlib - FcpUtils.java - Copyright © 2008 David Roden
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 package net.pterodactylus.fcp;
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.Closeable;
24 import java.io.EOFException;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.net.Socket;
32 import java.util.StringTokenizer;
33 import java.util.concurrent.atomic.AtomicLong;
36 * Helper class with utility methods for the FCP protocol.
38 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
40 public class FcpUtils {
42 /** Counter for unique identifiers. */
43 private static AtomicLong counter = new AtomicLong();
46 * Returns a unique identifier.
48 * @return A unique identifier
50 public static String getUniqueIdentifier() {
51 return new StringBuilder().append(System.currentTimeMillis()).append('-').append(counter.getAndIncrement()).toString();
55 * Parses an integer field, separated by ‘;’ and returns the parsed values.
59 * @return An array with the parsed values
60 * @throws NumberFormatException
61 * if a value can not be converted to a number
63 public static int[] decodeMultiIntegerField(String field) throws NumberFormatException {
64 StringTokenizer fieldTokens = new StringTokenizer(field, ";");
65 int[] result = new int[fieldTokens.countTokens()];
67 while (fieldTokens.hasMoreTokens()) {
68 String fieldToken = fieldTokens.nextToken();
69 result[counter++] = Integer.valueOf(fieldToken);
75 * Encodes the given integer array into a string, separating the values by
79 * The values to encode
80 * @return The encoded values
82 public static String encodeMultiIntegerField(int[] values) {
83 StringBuilder encodedField = new StringBuilder();
84 for (int value : values) {
85 if (encodedField.length() > 0) {
86 encodedField.append(';');
88 encodedField.append(value);
90 return encodedField.toString();
94 * Encodes the given string array into a string, separating the values by
98 * The values to encode
99 * @return The encoded values
101 public static String encodeMultiStringField(String[] values) {
102 StringBuilder encodedField = new StringBuilder();
103 for (String value : values) {
104 if (encodedField.length() > 0) {
105 encodedField.append(';');
107 encodedField.append(value);
109 return encodedField.toString();
113 * Tries to parse the given string into an int, returning <code>-1</code>
114 * if the string can not be parsed.
117 * The string to parse
118 * @return The parsed int, or <code>-1</code>
120 public static int safeParseInt(String value) {
121 return safeParseInt(value, -1);
125 * Tries to parse the given string into an int, returning
126 * <code>defaultValue</code> if the string can not be parsed.
129 * The string to parse
130 * @param defaultValue
131 * The value to return if the string can not be parsed.
132 * @return The parsed int, or <code>defaultValue</code>
134 public static int safeParseInt(String value, int defaultValue) {
136 return Integer.valueOf(value);
137 } catch (NumberFormatException nfe1) {
143 * Tries to parse the given string into an long, returning <code>-1</code>
144 * if the string can not be parsed.
147 * The string to parse
148 * @return The parsed long, or <code>-1</code>
150 public static long safeParseLong(String value) {
151 return safeParseLong(value, -1);
155 * Tries to parse the given string into an long, returning
156 * <code>defaultValue</code> if the string can not be parsed.
159 * The string to parse
160 * @param defaultValue
161 * The value to return if the string can not be parsed.
162 * @return The parsed long, or <code>defaultValue</code>
164 public static long safeParseLong(String value, long defaultValue) {
166 return Integer.valueOf(value);
167 } catch (NumberFormatException nfe1) {
173 * Closes the given socket.
176 * The socket to close
178 public static void close(Socket socket) {
179 if (socket != null) {
182 } catch (IOException ioe1) {
189 * Closes the given Closeable.
192 * The Closeable to close
194 public static void close(Closeable closeable) {
195 if (closeable != null) {
198 } catch (IOException ioe1) {
205 * Copies as many bytes as possible (i.e. until {@link InputStream#read()}
206 * returns <code>-1</code>) from the source input stream to the destination
210 * The input stream to read from
212 * The output stream to write to
213 * @throws IOException
214 * if an I/O error occurs
216 public static void copy(InputStream source, OutputStream destination) throws IOException {
217 copy(source, destination, -1);
221 * Copies <code>length</code> bytes from the source input stream to the
222 * destination output stream. If <code>length</code> is <code>-1</code> as
223 * much bytes as possible will be copied (i.e. until
224 * {@link InputStream#read()} returns <code>-1</code> to signal the end of
228 * The input stream to read from
230 * The output stream to write to
232 * The number of bytes to copy
233 * @throws IOException
234 * if an I/O error occurs
236 public static void copy(InputStream source, OutputStream destination, long length) throws IOException {
237 copy(source, destination, length, 1 << 16);
241 * Copies <code>length</code> bytes from the source input stream to the
242 * destination output stream. If <code>length</code> is <code>-1</code> as
243 * much bytes as possible will be copied (i.e. until
244 * {@link InputStream#read()} returns <code>-1</code> to signal the end of
248 * The input stream to read from
250 * The output stream to write to
252 * The number of bytes to copy
255 * @throws IOException
256 * if an I/O error occurs
258 public static void copy(InputStream source, OutputStream destination, long length, int bufferSize) throws IOException {
259 long remaining = length;
260 byte[] buffer = new byte[bufferSize];
262 while ((remaining == -1) || (remaining > 0)) {
263 read = source.read(buffer, 0, ((remaining > bufferSize) || (remaining == -1)) ? bufferSize : (int) remaining);
268 throw new EOFException("stream reached eof");
270 destination.write(buffer, 0, read);
276 * This input stream stores the content of another input stream either in a
277 * file or in memory, depending on the length of the input stream.
279 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
281 public static class TempInputStream extends InputStream {
283 /** The default maximum lenght for in-memory storage. */
284 public static final long MAX_LENGTH_MEMORY = 65536;
286 /** The temporary file to read from. */
287 private final File tempFile;
289 /** The input stream that reads from the file. */
290 private final InputStream fileInputStream;
292 /** The input stream that reads from memory. */
293 private final InputStream memoryInputStream;
296 * Creates a new temporary input stream that stores the given input
297 * stream in a temporary file.
299 * @param originalInputStream
300 * The original input stream
301 * @throws IOException
302 * if an I/O error occurs
304 public TempInputStream(InputStream originalInputStream) throws IOException {
305 this(originalInputStream, -1);
309 * Creates a new temporary input stream that stores the given input
310 * stream in memory if it is shorter than {@link #MAX_LENGTH_MEMORY},
311 * otherwise it is stored in a file.
313 * @param originalInputStream
314 * The original input stream
316 * The length of the input stream
317 * @throws IOException
318 * if an I/O error occurs
320 public TempInputStream(InputStream originalInputStream, long length) throws IOException {
321 this(originalInputStream, length, MAX_LENGTH_MEMORY);
325 * Creates a new temporary input stream that stores the given input
326 * stream in memory if it is shorter than <code>maxMemoryLength</code>,
327 * otherwise it is stored in a file.
329 * @param originalInputStream
330 * The original input stream
332 * The length of the input stream
333 * @param maxMemoryLength
334 * The maximum length to store in memory
335 * @throws IOException
336 * if an I/O error occurs
338 public TempInputStream(InputStream originalInputStream, long length, long maxMemoryLength) throws IOException {
339 if ((length > -1) && (length <= maxMemoryLength)) {
340 ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream((int) length);
342 FcpUtils.copy(originalInputStream, memoryOutputStream, length, (int) length);
344 memoryOutputStream.close();
347 fileInputStream = null;
348 memoryInputStream = new ByteArrayInputStream(memoryOutputStream.toByteArray());
350 tempFile = File.createTempFile("temp-", ".bin");
351 tempFile.deleteOnExit();
352 FileOutputStream fileOutputStream = null;
354 fileOutputStream = new FileOutputStream(tempFile);
355 FcpUtils.copy(originalInputStream, fileOutputStream);
356 fileInputStream = new FileInputStream(tempFile);
358 FcpUtils.close(fileOutputStream);
360 memoryInputStream = null;
368 public int available() throws IOException {
369 if (memoryInputStream != null) {
370 return memoryInputStream.available();
372 return fileInputStream.available();
379 public void close() throws IOException {
380 if (memoryInputStream != null) {
381 memoryInputStream.close();
385 fileInputStream.close();
392 public synchronized void mark(int readlimit) {
393 if (memoryInputStream != null) {
394 memoryInputStream.mark(readlimit);
397 fileInputStream.mark(readlimit);
404 public boolean markSupported() {
405 if (memoryInputStream != null) {
406 return memoryInputStream.markSupported();
408 return fileInputStream.markSupported();
415 public int read() throws IOException {
416 if (memoryInputStream != null) {
417 return memoryInputStream.read();
419 return fileInputStream.read();
426 public int read(byte[] b) throws IOException {
427 if (memoryInputStream != null) {
428 return memoryInputStream.read(b);
430 return fileInputStream.read(b);
437 public int read(byte[] b, int off, int len) throws IOException {
438 if (memoryInputStream != null) {
439 return memoryInputStream.read(b, off, len);
441 return fileInputStream.read(b, off, len);
448 public synchronized void reset() throws IOException {
449 if (memoryInputStream != null) {
450 memoryInputStream.reset();
453 fileInputStream.reset();
460 public long skip(long n) throws IOException {
461 if (memoryInputStream != null) {
462 return memoryInputStream.skip(n);
464 return fileInputStream.skip(n);