]> sjero.net Git - linphone/blob - p2pproxy/dependencies-src/jxse-src-2.5/api/src/net/jxta/endpoint/StringMessageElement.java
remove mediastreamer2 and add it as a submodule instead.
[linphone] / p2pproxy / dependencies-src / jxse-src-2.5 / api / src / net / jxta / endpoint / StringMessageElement.java
1 /*
2  * Copyright (c) 2001-2007 Sun Microsystems, Inc.  All rights reserved.
3  *  
4  *  The Sun Project JXTA(TM) Software License
5  *  
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions are met:
8  *  
9  *  1. Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *  
12  *  2. Redistributions in binary form must reproduce the above copyright notice, 
13  *     this list of conditions and the following disclaimer in the documentation 
14  *     and/or other materials provided with the distribution.
15  *  
16  *  3. The end-user documentation included with the redistribution, if any, must 
17  *     include the following acknowledgment: "This product includes software 
18  *     developed by Sun Microsystems, Inc. for JXTA(TM) technology." 
19  *     Alternately, this acknowledgment may appear in the software itself, if 
20  *     and wherever such third-party acknowledgments normally appear.
21  *  
22  *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must 
23  *     not be used to endorse or promote products derived from this software 
24  *     without prior written permission. For written permission, please contact 
25  *     Project JXTA at http://www.jxta.org.
26  *  
27  *  5. Products derived from this software may not be called "JXTA", nor may 
28  *     "JXTA" appear in their name, without prior written permission of Sun.
29  *  
30  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
32  *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN 
33  *  MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
34  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
35  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
36  *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
37  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
38  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
39  *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *  
41  *  JXTA is a registered trademark of Sun Microsystems, Inc. in the United 
42  *  States and other countries.
43  *  
44  *  Please see the license information page at :
45  *  <http://www.jxta.org/project/www/license.html> for instructions on use of 
46  *  the license in source files.
47  *  
48  *  ====================================================================
49  *  
50  *  This software consists of voluntary contributions made by many individuals 
51  *  on behalf of Project JXTA. For more information on Project JXTA, please see 
52  *  http://www.jxta.org.
53  *  
54  *  This license is based on the BSD license adopted by the Apache Foundation. 
55  */
56 package net.jxta.endpoint;
57
58
59 import net.jxta.document.MimeMediaType;
60 import net.jxta.logging.Logging;
61
62 import java.io.ByteArrayInputStream;
63 import java.io.IOException;
64 import java.io.InputStream;
65 import java.io.InputStreamReader;
66 import java.io.OutputStream;
67 import java.io.Reader;
68 import java.io.StringReader;
69 import java.io.UnsupportedEncodingException;
70 import java.io.Writer;
71 import java.lang.ref.SoftReference;
72 import java.nio.ByteBuffer;
73 import java.nio.CharBuffer;
74 import java.nio.charset.Charset;
75 import java.nio.charset.CharsetEncoder;
76 import java.nio.charset.CoderResult;
77 import java.nio.charset.CodingErrorAction;
78 import java.util.logging.Level;
79 import java.util.logging.Logger;
80
81
82 /**
83  * A Message Element using character strings for the element data.
84  */
85 public class StringMessageElement extends TextMessageElement {
86
87     /**
88      * Logger
89      */
90     private final static transient Logger LOG = Logger.getLogger(StringMessageElement.class.getName());
91
92     /**
93      * The MIME media type we will be use for encoding {@code String}s when no
94      * encoding is specified.
95      */
96     private static final MimeMediaType DEFAULT_TEXT_ENCODING = new MimeMediaType(MimeMediaType.TEXT_DEFAULTENCODING, "charset=\"" + Charset.defaultCharset().name() + "\"", true).intern();
97
98     /**
99      * The data for this Message Element.
100      */
101     protected String data;
102
103     /**
104      * Returns an appropriate mime type for the given encoding name. The
105      * mimetype will contain the canonical name of the encoding.
106      *
107      * @param encoding name of the desired encoding.
108      * @return the mime type.
109      * @throws java.io.UnsupportedEncodingException
110      *          if the mime is unsupported
111      */
112     private static MimeMediaType makeMimeType(String encoding) throws UnsupportedEncodingException {
113         InputStreamReader getEncoding = new InputStreamReader(new ByteArrayInputStream(new byte[0]), encoding);
114
115         String canonicalName = getEncoding.getEncoding();
116
117         return new MimeMediaType(MimeMediaType.TEXT_DEFAULTENCODING, "charset=\"" + canonicalName + "\"", true).intern();
118     }
119
120     /**
121      * Create a new Message Element from the provided String. The String will
122      * be encoded for transmission using UTF-8.
123      *
124      * @param name  Name of the Element. May be the empty string ("") or null if
125      *              the Element is not named.
126      * @param value A String containing the contents of this element.
127      * @param sig   Message digest/digital signature element. If no signature is
128      *              to be specified, pass <code>null</code>.
129      * @throws IllegalArgumentException if <code>value</code> is
130      *                                  <code>null</code>.
131      */
132     public StringMessageElement(String name, String value, MessageElement sig) {
133         super(name, MimeMediaType.TEXTUTF8, sig);
134
135         if (null == value) {
136             throw new IllegalArgumentException("value must be non-null");
137         }
138
139         data = value;
140     }
141
142     /**
143      * Create a new Message Element from the provided String. The string will
144      * be encoded for transmission using specified character encoding.
145      *
146      * @param name     Name of the MessageElement. May be the empty string ("") or
147      *                 <code>null</code> if the MessageElement is not named.
148      * @param value    A String containing the contents of this element.
149      * @param encoding Name of the character encoding to use. If
150      *                 <code>null</code> then the system default character encoding will be
151      *                 used. (Using the system default character encoding should be used with
152      *                 extreme caution).
153      * @param sig      Message digest/digital signature element. If no signature is
154      *                 to be specified, pass <code>null</code>.
155      * @throws IllegalArgumentException     if <code>value</code> is
156      *                                      <code>null</code>.
157      * @throws UnsupportedEncodingException if the requested encoding is not
158      *                                      supported.
159      */
160     public StringMessageElement(String name, String value, String encoding, MessageElement sig) throws UnsupportedEncodingException {
161         super(name, (null == encoding) ? DEFAULT_TEXT_ENCODING : makeMimeType(encoding), sig);
162
163         if (null == value) {
164             throw new IllegalArgumentException("value must be non-null");
165         }
166
167         data = value;
168     }
169
170     /**
171      * {@inheritDoc}
172      */
173     @Override
174     public boolean equals(Object target) {
175         if (this == target) {
176             return true;
177         }
178
179         if (target instanceof MessageElement) {
180             if (!super.equals(target)) {
181                 return false;
182             }
183
184             if (target instanceof StringMessageElement) {
185                 StringMessageElement likeMe = (StringMessageElement) target;
186
187                 return data.equals(likeMe.data); // same chars?
188             } else if (target instanceof TextMessageElement) {
189                 // have to do a slow char by char comparison. Still better than the stream since it saves encoding.
190                 // XXX 20020615 bondolo@jxta.org the performance of this could be much improved.
191
192                 TextMessageElement likeMe = (TextMessageElement) target;
193
194                 try {
195                     Reader myReader = getReader();
196                     Reader itsReader = likeMe.getReader();
197
198                     int mine;
199                     int its;
200
201                     do {
202                         mine = myReader.read();
203                         its = itsReader.read();
204
205                         if (mine != its) {
206                             return false;
207                         }       // content didn't match
208
209                     } while ((-1 != mine) && (-1 != its));
210
211                     return ((-1 == mine) && (-1 == its)); // end at the same time?
212                 } catch (IOException fatal) {
213                     IllegalStateException failure = new IllegalStateException("MessageElements could not be compared.");
214
215                     failure.initCause(fatal);
216                     throw failure;
217                 }
218             } else {
219                 // have to do a slow stream comparison.
220                 // XXX 20020615 bondolo@jxta.org the performance of this could be much improved.
221
222                 MessageElement likeMe = (MessageElement) target;
223
224                 try {
225                     InputStream myStream = getStream();
226                     InputStream itsStream = likeMe.getStream();
227
228                     int mine;
229                     int its;
230
231                     do {
232                         mine = myStream.read();
233                         its = itsStream.read();
234
235                         if (mine != its) {
236                             return false;
237                         }       // content didn't match
238
239                     } while ((-1 != mine) && (-1 != its));
240
241                     return ((-1 == mine) && (-1 == its)); // end at the same time?
242                 } catch (IOException fatal) {
243                     IllegalStateException failure = new IllegalStateException("MessageElements could not be compared.");
244
245                     failure.initCause(fatal);
246                     throw failure;
247                 }
248             }
249         }
250
251         return false; // not a new message element
252     }
253
254     /**
255      * {@inheritDoc}
256      */
257     @Override
258     public int hashCode() {
259         int result = super.hashCode() * 6037 + // a prime
260                 data.hashCode();
261
262         return result;
263     }
264
265     /**
266      * {@inheritDoc}
267      */
268     @Override
269     public String toString() {
270         return data;
271     }
272
273     /**
274      * {@inheritDoc}
275      */
276     @Override
277     public synchronized byte[] getBytes(boolean copy) {
278         byte[] cachedBytes = null;
279
280         if (null != cachedGetBytes) {
281             cachedBytes = cachedGetBytes.get();
282         }
283
284         if (null == cachedBytes) {
285             if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) {
286                 LOG.finer(
287                         "Creating getBytes of " + getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(this)));
288             }
289
290             String charset = type.getParameter("charset");
291
292             try {
293                 cachedBytes = data.getBytes(charset);
294             } catch (UnsupportedEncodingException caught) {
295                 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
296                     LOG.log(Level.WARNING, "MessageElement Data could not be generated", caught);
297                 }
298                 IllegalStateException failure = new IllegalStateException("MessageElement Data could not be generated");
299
300                 failure.initCause(caught);
301                 throw failure;
302             }
303
304             cachedGetBytes = new SoftReference<byte[]>(cachedBytes);
305         }
306
307         if (!copy) {
308             return cachedBytes;
309         }
310
311         byte[] bytesCopy = cachedBytes.clone();
312
313         return bytesCopy;
314     }
315
316     /**
317      * {@inheritDoc}
318      */
319     @Override
320     public long getCharLength() {
321         return data.length();
322     }
323
324     /**
325      * {@inheritDoc}
326      */
327     @Override
328     public synchronized char[] getChars(boolean copy) {
329         char[] cachedChars = null;
330
331         if (null != cachedGetChars) {
332             cachedChars = cachedGetChars.get();
333         }
334
335         if (null == cachedChars) {
336             if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) {
337                 LOG.finer("creating cachedGetChars of " + getClass().getName() + '@' + Integer.toHexString(hashCode()));
338             }
339
340             cachedChars = new char[data.length()];
341
342             data.getChars(0, data.length(), cachedChars, 0);
343
344             // if this is supposed to be a shared buffer then we can cache it.
345
346             cachedGetChars = new SoftReference<char[]>(cachedChars);
347         }
348
349         if (!copy) {
350             return cachedChars;
351         }
352
353         char[] copyChars = cachedChars.clone();
354
355         return copyChars;
356     }
357
358     /**
359      * {@inheritDoc}
360      */
361     public InputStream getStream() throws IOException {
362         byte cachedBytes[] = null;
363
364         synchronized (this) {
365             if (null != cachedGetBytes) {
366                 cachedBytes = cachedGetBytes.get();
367             }
368         }
369
370         if (null != cachedBytes) {
371             return new ByteArrayInputStream(cachedBytes);
372         } else {
373             String charset = type.getParameter("charset");
374             return new CharSequenceInputStream(data, charset);
375         }
376     }
377
378     /**
379      * {@inheritDoc}
380      *
381      * @return InputStream of the stream containing element data.
382      * @throws IOException when there is a problem getting a reader.
383      */
384     public Reader getReader() throws IOException {
385
386         return new StringReader(data);
387     }
388
389     /**
390      * {@inheritDoc}
391      */
392     @Override
393     public void sendToStream(OutputStream sendTo) throws IOException {
394
395         sendTo.write(getBytes(false));
396     }
397
398     /**
399      * {@inheritDoc}
400      */
401     @Override
402     public void sendToWriter(Writer sendTo) throws IOException {
403         sendTo.write(data);
404     }
405
406     /**
407      *
408      **/
409     private static class CharSequenceInputStream extends InputStream {
410
411         private final CharBuffer charData;
412
413         private final CharsetEncoder conversion;
414
415         private boolean marked = false;
416         private byte mark_multiByteChar[];
417         private int mark_position;
418
419         private byte multiByteChar[];
420         private int position;
421
422         /**
423          * @param s        the char sequence
424          * @param encoding the charset encoding
425          */
426         CharSequenceInputStream(CharSequence s, String encoding) {
427             charData = CharBuffer.wrap(s);
428
429             Charset encodingCharset = Charset.forName(encoding);
430
431             conversion = encodingCharset.newEncoder();
432             conversion.onMalformedInput(CodingErrorAction.REPLACE);
433             conversion.onUnmappableCharacter(CodingErrorAction.REPLACE);
434
435             int maxBytes = new Float(conversion.maxBytesPerChar()).intValue();
436
437             multiByteChar = new byte[maxBytes];
438             position = multiByteChar.length;
439         }
440
441         /**
442          * {@inheritDoc}
443          */
444         @Override
445         public void mark(int ignored) {
446             charData.mark();
447             mark_multiByteChar = multiByteChar.clone();
448             mark_position = position;
449             marked = true;
450         }
451
452         /**
453          * {@inheritDoc}
454          */
455         @Override
456         public boolean markSupported() {
457             return true;
458         }
459
460         /**
461          * {@inheritDoc}
462          */
463         @Override
464         public void reset() throws IOException {
465
466             if (!marked) {
467                 throw new IOException("reset() called before mark()");
468             }
469
470             charData.reset();
471             multiByteChar = mark_multiByteChar.clone();
472             position = mark_position;
473         }
474
475         /**
476          * {@inheritDoc}
477          */
478         @Override
479         public int read() throws IOException {
480             // prefill the buffer
481             while (multiByteChar.length == position) {
482                 int readsome = read(multiByteChar, 0, multiByteChar.length);
483
484                 if (-1 == readsome) {
485                     return -1;
486                 }
487
488                 position = multiByteChar.length - readsome;
489
490                 if ((0 != position) && (0 != readsome)) {
491                     System.arraycopy(multiByteChar, 0, multiByteChar, position, readsome);
492                 }
493             }
494
495             return (multiByteChar[position++] & 0xFF);
496         }
497
498         /**
499          * {@inheritDoc}
500          */
501         @Override
502         public int read(byte[] buffer) throws IOException {
503             return read(buffer, 0, buffer.length);
504         }
505
506         /**
507          * {@inheritDoc}
508          */
509         @Override
510         public int read(byte[] buffer, int offset, int length) throws IOException {
511             // handle partial characters;
512             if (multiByteChar.length != position) {
513                 int copying = Math.min(length, multiByteChar.length - position);
514
515                 System.arraycopy(multiByteChar, position, buffer, offset, copying);
516                 position += copying;
517                 return copying;
518             }
519
520             ByteBuffer bb = ByteBuffer.wrap(buffer, offset, length);
521
522             int before = bb.remaining();
523
524             CoderResult result = conversion.encode(charData, bb, true);
525
526             int readin = before - bb.remaining();
527
528             if (CoderResult.UNDERFLOW == result) {
529                 if (0 == readin) {
530                     return -1;
531                 } else {
532                     return readin;
533                 }
534             }
535
536             if (CoderResult.OVERFLOW == result) {
537                 return readin;
538             }
539
540             result.throwException();
541
542             // NEVERREACHED
543             return 0;
544         }
545     }
546 }