2 * jSite2 - FcpUtils.java -
3 * Copyright © 2008 David Roden
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 package net.pterodactylus.fcp;
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.Closeable;
25 import java.io.EOFException;
27 import java.io.FileInputStream;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.net.Socket;
33 import java.util.StringTokenizer;
34 import java.util.concurrent.atomic.AtomicLong;
37 * Helper class with utility methods for the FCP protocol.
39 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
41 public class FcpUtils {
43 /** Counter for unique identifiers. */
44 private static AtomicLong counter = new AtomicLong();
47 * Returns a unique identifier.
49 * @return A unique identifier
51 public static String getUniqueIdentifier() {
52 return new StringBuilder().append(System.currentTimeMillis()).append('-').append(counter.getAndIncrement()).toString();
56 * Parses an integer field, separated by ‘;’ and returns the parsed values.
60 * @return An array with the parsed values
61 * @throws NumberFormatException
62 * if a value can not be converted to a number
64 public static int[] decodeMultiIntegerField(String field) throws NumberFormatException {
65 StringTokenizer fieldTokens = new StringTokenizer(field, ";");
66 int[] result = new int[fieldTokens.countTokens()];
68 while (fieldTokens.hasMoreTokens()) {
69 String fieldToken = fieldTokens.nextToken();
70 result[counter++] = Integer.valueOf(fieldToken);
76 * Encodes the given integer array into a string, separating the values by
80 * The values to encode
81 * @return The encoded values
83 public static String encodeMultiIntegerField(int[] values) {
84 StringBuilder encodedField = new StringBuilder();
85 for (int value : values) {
86 if (encodedField.length() > 0) {
87 encodedField.append(';');
89 encodedField.append(value);
91 return encodedField.toString();
95 * Encodes the given string array into a string, separating the values by
99 * The values to encode
100 * @return The encoded values
102 public static String encodeMultiStringField(String[] values) {
103 StringBuilder encodedField = new StringBuilder();
104 for (String value : values) {
105 if (encodedField.length() > 0) {
106 encodedField.append(';');
108 encodedField.append(value);
110 return encodedField.toString();
114 * Tries to parse the given string into an int, returning <code>-1</code>
115 * if the string can not be parsed.
118 * The string to parse
119 * @return The parsed int, or <code>-1</code>
121 public static int safeParseInt(String value) {
122 return safeParseInt(value, -1);
126 * Tries to parse the given string into an int, returning
127 * <code>defaultValue</code> if the string can not be parsed.
130 * The string to parse
131 * @param defaultValue
132 * The value to return if the string can not be parsed.
133 * @return The parsed int, or <code>defaultValue</code>
135 public static int safeParseInt(String value, int defaultValue) {
137 return Integer.valueOf(value);
138 } catch (NumberFormatException nfe1) {
144 * Tries to parse the given string into an long, returning <code>-1</code>
145 * if the string can not be parsed.
148 * The string to parse
149 * @return The parsed long, or <code>-1</code>
151 public static long safeParseLong(String value) {
152 return safeParseLong(value, -1);
156 * Tries to parse the given string into an long, returning
157 * <code>defaultValue</code> if the string can not be parsed.
160 * The string to parse
161 * @param defaultValue
162 * The value to return if the string can not be parsed.
163 * @return The parsed long, or <code>defaultValue</code>
165 public static long safeParseLong(String value, long defaultValue) {
167 return Integer.valueOf(value);
168 } catch (NumberFormatException nfe1) {
174 * Closes the given socket.
177 * The socket to close
179 public static void close(Socket socket) {
180 if (socket != null) {
183 } catch (IOException ioe1) {
190 * Closes the given Closeable.
193 * The Closeable to close
195 public static void close(Closeable closeable) {
196 if (closeable != null) {
199 } catch (IOException ioe1) {
206 * Copies as many bytes as possible (i.e. until {@link InputStream#read()}
207 * returns <code>-1</code>) from the source input stream to the
208 * destination output stream.
211 * The input stream to read from
213 * The output stream to write to
214 * @throws IOException
215 * if an I/O error occurs
217 public static void copy(InputStream source, OutputStream destination) throws IOException {
218 copy(source, destination, -1);
222 * Copies <code>length</code> bytes from the source input stream to the
223 * destination output stream. If <code>length</code> is <code>-1</code>
224 * as much bytes as possible will be copied (i.e. until
225 * {@link InputStream#read()} returns <code>-1</code> to signal the end of
229 * The input stream to read from
231 * The output stream to write to
233 * The number of bytes to copy
234 * @throws IOException
235 * if an I/O error occurs
237 public static void copy(InputStream source, OutputStream destination, long length) throws IOException {
238 copy(source, destination, length, 1 << 16);
242 * Copies <code>length</code> bytes from the source input stream to the
243 * destination output stream. If <code>length</code> is <code>-1</code>
244 * as much bytes as possible will be copied (i.e. until
245 * {@link InputStream#read()} returns <code>-1</code> to signal the end of
249 * The input stream to read from
251 * The output stream to write to
253 * The number of bytes to copy
256 * @throws IOException
257 * if an I/O error occurs
259 public static void copy(InputStream source, OutputStream destination, long length, int bufferSize) throws IOException {
260 long remaining = length;
261 byte[] buffer = new byte[bufferSize];
263 while ((remaining == -1) || (remaining > 0)) {
264 read = source.read(buffer, 0, ((remaining > bufferSize) || (remaining == -1)) ? bufferSize : (int) remaining);
269 throw new EOFException("stream reached eof");
271 destination.write(buffer, 0, read);
277 * This input stream stores the content of another input stream either in a
278 * file or in memory, depending on the length of the input stream.
280 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
282 public static class TempInputStream extends InputStream {
284 /** The default maximum lenght for in-memory storage. */
285 public static final long MAX_LENGTH_MEMORY = 65536;
287 /** The temporary file to read from. */
288 private final File tempFile;
290 /** The input stream that reads from the file. */
291 private final InputStream fileInputStream;
293 /** The input stream that reads from memory. */
294 private final InputStream memoryInputStream;
297 * Creates a new temporary input stream that stores the given input
298 * stream in a temporary file.
300 * @param originalInputStream
301 * The original input stream
302 * @throws IOException
303 * if an I/O error occurs
305 public TempInputStream(InputStream originalInputStream) throws IOException {
306 this(originalInputStream, -1);
310 * Creates a new temporary input stream that stores the given input
311 * stream in memory if it is shorter than {@link #MAX_LENGTH_MEMORY},
312 * otherwise it is stored in a file.
314 * @param originalInputStream
315 * The original input stream
317 * The length of the input stream
318 * @throws IOException
319 * if an I/O error occurs
321 public TempInputStream(InputStream originalInputStream, long length) throws IOException {
322 this(originalInputStream, length, MAX_LENGTH_MEMORY);
326 * Creates a new temporary input stream that stores the given input
327 * stream in memory if it is shorter than <code>maxMemoryLength</code>,
328 * otherwise it is stored in a file.
330 * @param originalInputStream
331 * The original input stream
333 * The length of the input stream
334 * @param maxMemoryLength
335 * The maximum length to store in memory
336 * @throws IOException
337 * if an I/O error occurs
339 public TempInputStream(InputStream originalInputStream, long length, long maxMemoryLength) throws IOException {
340 if ((length > -1) && (length <= maxMemoryLength)) {
341 ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream((int) length);
343 FcpUtils.copy(originalInputStream, memoryOutputStream, length, (int) length);
345 memoryOutputStream.close();
348 fileInputStream = null;
349 memoryInputStream = new ByteArrayInputStream(memoryOutputStream.toByteArray());
351 tempFile = File.createTempFile("temp-", ".bin");
352 tempFile.deleteOnExit();
353 FileOutputStream fileOutputStream = null;
355 fileOutputStream = new FileOutputStream(tempFile);
356 FcpUtils.copy(originalInputStream, fileOutputStream);
357 fileInputStream = new FileInputStream(tempFile);
359 FcpUtils.close(fileOutputStream);
361 memoryInputStream = null;
369 public int available() throws IOException {
370 if (memoryInputStream != null) {
371 return memoryInputStream.available();
373 return fileInputStream.available();
380 public void close() throws IOException {
381 if (memoryInputStream != null) {
382 memoryInputStream.close();
386 fileInputStream.close();
393 public synchronized void mark(int readlimit) {
394 if (memoryInputStream != null) {
395 memoryInputStream.mark(readlimit);
398 fileInputStream.mark(readlimit);
405 public boolean markSupported() {
406 if (memoryInputStream != null) {
407 return memoryInputStream.markSupported();
409 return fileInputStream.markSupported();
416 public int read() throws IOException {
417 if (memoryInputStream != null) {
418 return memoryInputStream.read();
420 return fileInputStream.read();
427 public int read(byte[] b) throws IOException {
428 if (memoryInputStream != null) {
429 return memoryInputStream.read(b);
431 return fileInputStream.read(b);
438 public int read(byte[] b, int off, int len) throws IOException {
439 if (memoryInputStream != null) {
440 return memoryInputStream.read(b, off, len);
442 return fileInputStream.read(b, off, len);
449 public synchronized void reset() throws IOException {
450 if (memoryInputStream != null) {
451 memoryInputStream.reset();
454 fileInputStream.reset();
461 public long skip(long n) throws IOException {
462 if (memoryInputStream != null) {
463 return memoryInputStream.skip(n);
465 return fileInputStream.skip(n);