1 /******************************************************************
2 JADE - Java Agent DEvelopment Framework is a framework to develop
3 multi-agent systems in compliance with the FIPA specifications.
4 Copyright (C) 2000 CSELT S.p.A.
5
6 GNU Lesser General Public License
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation,
11 version 2.1 of the License.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the
20 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
22 *****************************************************************/
23
24 package JADE_SL;
25
26 import java.io.*;
27 import JADE_SL.AID;
28 import JADE_SL.CaseInsensitiveString;
29
30 import java.util.*;
31 import java.util.Date;
32
33 import JADE_SL.Base64;
34 import zeus.concepts.FIPAParser;
35 import zeus.concepts.FIPAPerformative;
36
37 /***
38 This class implements the FIPA String codec for ACLMessages.
39 Notice that it is not possible to convey
40 a sequence of bytes over a StringACLCodec because the bytes with
41 the 8th bit ON cannot properly converted into a char.
42 @author Fabio Bellifemine - CSELT S.p.A.
43 @version $Date: 2003/10/09 13:00:35 $ $Revision: 1.1.1.1 $
44 **/
45 public class StringACLCodec{
46
47 public static final String NAME ="STRING";
48
49 /*** Key of the user-defined parameter used to signal the automatic JADE
50 conversion of the content into Base64 encoding **/
51 private static final String BASE64ENCODING_KEY = new String("JADE-Encoding");
52 /*** Value of the user-defined parameter used to signal the automatic JADE
53 conversion of the content into Base64 encoding **/
54 private static final String BASE64ENCODING_VALUE = new String("Base64");
55
56 private static final String SENDER = new String(" :sender ");
57 private static final String RECEIVER = new String(" :receiver ");
58 private static final String CONTENT = new String(" :content ");
59 private static final String REPLY_WITH = new String(" :reply-with ");
60 private static final String IN_REPLY_TO = new String(" :in-reply-to ");
61 private static final String REPLY_TO = new String(" :reply-to ");
62 private static final String LANGUAGE = new String(" :language ");
63 private static final String ENCODING = new String(" :encoding ");
64 private static final String ONTOLOGY = new String(" :ontology ");
65 private static final String REPLY_BY = new String(" :reply-by ");
66 private static final String PROTOCOL = new String(" :protocol ");
67 private static final String CONVERSATION_ID = new String(" :conversation-id ");
68
69 FIPAParser parser;
70 Writer out;
71
72 /***
73 * constructor for the codec.
74 * The standard input is used as an input stream of ACL messages.
75 * The standard output is used to write encoded ACL messages.
76 */
77 public StringACLCodec() {
78 parser = new FIPAParser(System.in);
79 out = new OutputStreamWriter(System.out);
80 }
81
82
83 /***
84 * constructor for the codec.
85 * @parameter r is the input stream for the ACL Parser (pass
86 * <code>new InputStreamReader(System.in)</code>
87 * if you want to use the standard input)
88 * @parameter w is the writer to write encoded ACL messages (pass
89 * <code>new OutputStreamWriter(System.out)</code> if you want to
90 * use the standard output)
91 */
92 public StringACLCodec(Reader r, Writer w) {
93 parser = new FIPAParser(r);
94 out = w;
95 }
96
97
98 /***
99 * if there was an automatical Base64 encoding, then it performs
100 * automatic decoding.
101 **/
102 private void checkBase64Encoding(ACLMessage msg) {
103 String encoding = msg.getUserDefinedParameter(BASE64ENCODING_KEY);
104 if (CaseInsensitiveString.equalsIgnoreCase(BASE64ENCODING_VALUE,encoding)) {
105 try {
106 String content = msg.getContent();
107 if ((content != null) && (content.length() > 0)) {
108 char[] cc = new char[content.length()];
109 content.getChars(0,content.length(),cc,0);
110 msg.setByteSequenceContent(Base64.decode(cc));
111 msg.removeUserDefinedParameter(BASE64ENCODING_KEY);
112 }
113 } catch(java.lang.StringIndexOutOfBoundsException e){
114 e.printStackTrace();
115 } catch(java.lang.NullPointerException e2){
116 e2.printStackTrace();
117 } catch(java.lang.NoClassDefFoundError jlncdfe) {
118 System.err.println("\t\t===== E R R O R !!! =======\n");
119 System.err.println("Missing support for Base64 conversions");
120 System.err.println("Please refer to the documentation for details.");
121 System.err.println("=============================================\n\n");
122 try {
123 Thread.currentThread().sleep(3000);
124 }catch(InterruptedException ie) {
125 }
126 }
127 }
128 }
129
130 /***
131 * decode and parses the next message from the Reader passed in the
132 * constructor.
133 * @return the ACLMessage
134 * @throws ACLCodec.CodecException if any Exception occurs during the
135 * parsing/reading operation
136 */
137 public FIPAPerformative decode() throws Exception {
138 try {
139 FIPAPerformative msg = parser.Message();
140 return msg;
141 }
142 catch (Exception e) {
143 throw e; }
144
145 }
146
147 /***
148 * encodes the message and writes it into the Writer passed in the
149 * constructor.
150 * Notice that this method does not call <code>flush</code> on the writer.
151 @ param msg is the ACLMessage to encode and write into
152 */
153 public void write(ACLMessage msg) {
154 try {
155 out.write(toString(msg));
156 } catch (IOException ioe) {
157 ioe.printStackTrace();
158 }
159 }
160
161
162 static private String escape(String s) {
163
164
165
166
167 StringBuffer result = new StringBuffer(s.length()+20);
168 for( int i=0; i<s.length(); i++)
169 if( s.charAt(i) == '"' )
170 result.append("//\"");
171 else
172 result.append(s.charAt(i));
173 return result.toString();
174 }
175
176
177 /***
178 * @return a String encoded message
179 * @see ACLMessage#toString()
180 **/
181 static String toString(ACLMessage msg) {
182 StringBuffer str = new StringBuffer("(");
183 str.append(msg.getPerformative(msg.getPerformative()) + "\n");
184 AID sender = msg.getSender();
185 if (sender != null)
186 str.append(SENDER + " "+ sender.toString()+"\n");
187 Iterator it = msg.getAllReceiver();
188 if (it.hasNext()) {
189 str.append(RECEIVER + " (set ");
190 while(it.hasNext())
191 str.append(it.next().toString()+" ");
192 str.append(")\n");
193 }
194 it = msg.getAllReplyTo();
195 if (it.hasNext()) {
196 str.append(REPLY_TO + " (set \n");
197 while(it.hasNext())
198 str.append(it.next().toString()+" ");
199 str.append(")\n");
200 }
201 if (msg.hasByteSequenceContent()) {
202 str.append(":X-"+ BASE64ENCODING_KEY + " " + BASE64ENCODING_VALUE + "\n");
203 try {
204 String b64 = new String(Base64.encode(msg.getByteSequenceContent()));
205 str.append(CONTENT + " \"" + b64 + "\" \n");
206 } catch(java.lang.NoClassDefFoundError jlncdfe) {
207 System.err.println("\n\t===== E R R O R !!! =======\n");
208 System.err.println("Missing support for Base64 conversions");
209 System.err.println("Please refer to the documentation for details.");
210 System.err.println("=============================================\n\n");
211 System.err.println("");
212 try {
213 Thread.currentThread().sleep(3000);
214 } catch(InterruptedException ie) {
215 }
216 }
217 } else {
218 String content = msg.getContent();
219 if (content != null) {
220 content = content.trim();
221 if (content.length() > 0)
222 str.append(CONTENT + " \"" + escape(content) + "\" \n");
223 }
224 }
225 String tmp = msg.getReplyWith();
226 if (tmp != null) {
227 tmp = tmp.trim();
228 if (tmp.length() > 0)
229 str.append(REPLY_WITH + " " + tmp + "\n");
230 }
231 tmp = msg.getInReplyTo();
232 if (tmp != null) {
233 tmp = tmp.trim();
234 if (tmp.length() > 0)
235 str.append(IN_REPLY_TO + " " + tmp + "\n");
236 }
237 tmp = msg.getEncoding();
238 if (tmp != null) {
239 tmp = tmp.trim();
240 if (tmp.length() > 0)
241 str.append(ENCODING + " " + tmp + "\n");
242 }
243 tmp = msg.getLanguage();
244 if (tmp != null) {
245 tmp = tmp.trim();
246 if (tmp.length() > 0)
247 str.append(LANGUAGE + " " + tmp + "\n");
248 }
249 tmp = msg.getOntology();
250 if (tmp != null) {
251 tmp = tmp.trim();
252 if (tmp.length() > 0)
253 str.append(ONTOLOGY + " " + tmp + "\n");
254 }
255 Date d = msg.getReplyByDate();
256 if (d != null)
257 str.append(REPLY_BY + " " + ISO8601.toString(d) + "\n");
258 tmp = msg.getProtocol();
259 if (tmp != null) {
260 tmp = tmp.trim();
261 if (tmp.length() > 0)
262 str.append(PROTOCOL + " " + tmp + "\n");
263 }
264 tmp = msg.getConversationId();
265 if (tmp != null) {
266 tmp = tmp.trim();
267 if (tmp.length() > 0)
268 str.append(CONVERSATION_ID + " " + tmp + "\n");
269 }
270 Properties userDefProps = msg.getAllUserDefinedParameters();
271 Enumeration e = userDefProps.propertyNames();
272 while (e.hasMoreElements()) {
273 String key = ((String)e.nextElement()).trim();
274 String value = userDefProps.getProperty(key);
275 if (value != null) {
276 value = value.trim();
277 if (value.length()>0)
278 str.append(" :X-" + key + " " + value + "\n");
279 }
280 }
281 str.append(")");
282
283 return str.toString();
284 }
285
286 /***
287 * If the content of the message is a byteSequence, then this
288 * method encodes the content in Base64 and automatically sets the value
289 * of the encoding slot.
290 * @see ACLCodec#encode(ACLMessage msg)
291 */
292 public byte[] encode(ACLMessage msg) {
293 try {
294 return toString(msg).getBytes("US-ASCII");
295 }
296 catch(IOException ioe) {
297 ioe.printStackTrace();
298 return new byte[0];
299 }
300 }
301
302 /***
303 * @see ACLCodec#decode(byte[] data)
304 */
305
306
307
308
309
310
311
312
313
314
315
316
317 /***
318 * @return the name of this encoding according to the FIPA specifications
319 */
320 public String getName() {
321 return NAME;
322 }
323 }