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);
278 * This input stream stores the content of another input stream either in a
279 * file or in memory, depending on the length of the input stream.
281 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
283 public static class TempInputStream extends InputStream {
285 /** The default maximum lenght for in-memory storage. */
286 public static final long MAX_LENGTH_MEMORY = 65536;
288 /** The temporary file to read from. */
289 private final File tempFile;
291 /** The input stream that reads from the file. */
292 private final InputStream fileInputStream;
294 /** The input stream that reads from memory. */
295 private final InputStream memoryInputStream;
298 * Creates a new temporary input stream that stores the given input
299 * stream in a temporary file.
301 * @param originalInputStream
302 * The original input stream
303 * @throws IOException
304 * if an I/O error occurs
306 public TempInputStream(InputStream originalInputStream) throws IOException {
307 this(originalInputStream, -1);
311 * Creates a new temporary input stream that stores the given input
312 * stream in memory if it is shorter than {@link #MAX_LENGTH_MEMORY},
313 * otherwise it is stored in a file.
315 * @param originalInputStream
316 * The original input stream
318 * The length of the input stream
319 * @throws IOException
320 * if an I/O error occurs
322 public TempInputStream(InputStream originalInputStream, long length) throws IOException {
323 this(originalInputStream, length, MAX_LENGTH_MEMORY);
327 * Creates a new temporary input stream that stores the given input
328 * stream in memory if it is shorter than <code>maxMemoryLength</code>,
329 * otherwise it is stored in a file.
331 * @param originalInputStream
332 * The original input stream
334 * The length of the input stream
335 * @param maxMemoryLength
336 * The maximum length to store in memory
337 * @throws IOException
338 * if an I/O error occurs
340 public TempInputStream(InputStream originalInputStream, long length, long maxMemoryLength) throws IOException {
341 if ((length > -1) && (length <= maxMemoryLength)) {
342 ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream((int) length);
344 FcpUtils.copy(originalInputStream, memoryOutputStream, length, (int) length);
346 memoryOutputStream.close();
349 fileInputStream = null;
350 memoryInputStream = new ByteArrayInputStream(memoryOutputStream.toByteArray());
352 tempFile = File.createTempFile("temp-", ".bin");
353 tempFile.deleteOnExit();
354 FileOutputStream fileOutputStream = null;
356 fileOutputStream = new FileOutputStream(tempFile);
357 FcpUtils.copy(originalInputStream, fileOutputStream);
358 fileInputStream = new FileInputStream(tempFile);
360 FcpUtils.close(fileOutputStream);
362 memoryInputStream = null;
370 public int available() throws IOException {
371 if (memoryInputStream != null) {
372 return memoryInputStream.available();
374 return fileInputStream.available();
381 public void close() throws IOException {
382 if (memoryInputStream != null) {
383 memoryInputStream.close();
387 fileInputStream.close();
394 public synchronized void mark(int readlimit) {
395 if (memoryInputStream != null) {
396 memoryInputStream.mark(readlimit);
399 fileInputStream.mark(readlimit);
406 public boolean markSupported() {
407 if (memoryInputStream != null) {
408 return memoryInputStream.markSupported();
410 return fileInputStream.markSupported();
417 public int read() throws IOException {
418 if (memoryInputStream != null) {
419 return memoryInputStream.read();
421 return fileInputStream.read();
428 public int read(byte[] b) throws IOException {
429 if (memoryInputStream != null) {
430 return memoryInputStream.read(b);
432 return fileInputStream.read(b);
439 public int read(byte[] b, int off, int len) throws IOException {
440 if (memoryInputStream != null) {
441 return memoryInputStream.read(b, off, len);
443 return fileInputStream.read(b, off, len);
450 public synchronized void reset() throws IOException {
451 if (memoryInputStream != null) {
452 memoryInputStream.reset();
455 fileInputStream.reset();
462 public long skip(long n) throws IOException {
463 if (memoryInputStream != null) {
464 return memoryInputStream.skip(n);
466 return fileInputStream.skip(n);