2 * jFCPlib - FcpMessage.java - Copyright © 2008–2016 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 3 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, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.fcp;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Iterator;
27 import java.util.Map.Entry;
30 * An FCP message. FCP messages consist of a name, an arbitrary amount of
31 * “fields” (i.e. key-value pairs), a message end marker, and optional payload
32 * data that follows the marker.
34 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
36 public class FcpMessage implements Iterable<String> {
38 /** Constant for the linefeed. */
39 private static final String LINEFEED = "\r\n";
41 /** The name of the message. */
42 private final String name;
44 /** The fields of the message. */
45 private final Map<String, String> fields = new HashMap<String, String>();
47 /** The optional payload input stream. */
48 private InputStream payloadInputStream;
51 * Creates a new FCP message with the given name.
54 * The name of the FCP message
56 public FcpMessage(String name) {
61 * Creates a new FCP message with the given name and the given payload
62 * input stream. The payload input stream is not read until the message is
63 * sent to the node using {@link FcpConnection#sendMessage(FcpMessage)}.
66 * The name of the message
67 * @param payloadInputStream
68 * The payload of the message
70 public FcpMessage(String name, InputStream payloadInputStream) {
72 this.payloadInputStream = payloadInputStream;
76 * Returns the name of the message.
78 * @return The name of the message
80 public String getName() {
85 * Checks whether this message has a field with the given name.
88 * The name of the field to check for
89 * @return <code>true</code> if the message has a field with the given
90 * name, <code>false</code> otherwise
92 public boolean hasField(String field) {
93 return fields.containsKey(field);
97 * Sets the field with the given name to the given value. If the field
98 * already exists in this message it is overwritten.
101 * The name of the field
103 * The value of the field
105 public void setField(String field, String value) {
106 if ((field == null) || (value == null)) {
107 throw new NullPointerException(((field == null) ? "field " : "value ") + "must not be null");
109 fields.put(field, value);
112 public FcpMessage put(String field, String value) {
113 setField(field, value);
118 * Returns the value of the given field.
121 * The name of the field
122 * @return The value of the field, or <code>null</code> if there is no such
125 public String getField(String field) {
126 return fields.get(field);
130 * Returns all fields of this message.
132 * @return All fields of this message
134 public Map<String, String> getFields() {
135 return Collections.unmodifiableMap(fields);
142 public Iterator<String> iterator() {
143 return fields.keySet().iterator();
147 * Sets the payload input stream of the message.
149 * @param payloadInputStream
150 * The payload input stream
152 public void setPayloadInputStream(InputStream payloadInputStream) {
153 this.payloadInputStream = payloadInputStream;
157 * Writes this message to the given output stream. If the message has a
158 * payload (i.e. {@link #payloadInputStream} is not <code>null</code>) the
159 * payload is written to the given output stream after the message as well.
160 * That means that this method can only be called once because on the
161 * second invocation the payload input stream could not be read (again).
163 * @param outputStream
164 * The output stream to write the message to
165 * @throws IOException
166 * if an I/O error occurs
168 public void write(OutputStream outputStream) throws IOException {
169 writeLine(outputStream, name);
170 for (Entry<String, String> fieldEntry : fields.entrySet()) {
171 writeLine(outputStream, fieldEntry.getKey() + "=" + fieldEntry.getValue());
173 if (payloadInputStream != null) {
174 writeLine(outputStream, "Data");
175 FcpUtils.copy(payloadInputStream, outputStream);
177 writeLine(outputStream, "EndMessage");
179 outputStream.flush();
187 * Writes the given line (followed by {@link #LINEFEED} to the given output
188 * stream, using UTF-8 as encoding.
190 * @param outputStream
191 * The output stream to write to
194 * @throws IOException
195 * if an I/O error occurs
197 private void writeLine(OutputStream outputStream, String line) throws IOException {
198 outputStream.write((line + LINEFEED).getBytes("UTF-8"));