1 package com.fasterxml.jackson.core.io;
2
3 import java.io.*;
4 import java.nio.ByteBuffer;
5
6 import com.fasterxml.jackson.core.SerializableString;
7
8 /**
9  * String token that can lazily serialize String contained and then reuse that
10  * serialization later on. This is similar to JDBC prepared statements, for example,
11  * in that instances should only be created when they are used more than use;
12  * prime candidates are various serializers.
13  *<p>
14  * Class is final for performance reasons and since this is not designed to
15  * be extensible or customizable (customizations would occur in calling code)
16  */

17 public class SerializedString
18     implements SerializableString, java.io.Serializable
19 {
20     private static final long serialVersionUID = 1L;
21
22     private static final JsonStringEncoder JSON_ENCODER = JsonStringEncoder.getInstance();
23     
24     protected final String _value;
25
26     /* 13-Dec-2010, tatu: Whether use volatile or not is actually an important
27      *   decision for multi-core use cases. Cost of volatility can be non-trivial
28      *   for heavy use cases, and serialized-string instances are accessed often.
29      *   Given that all code paths with common Jackson usage patterns go through
30      *   a few memory barriers (mostly with cache/reuse pool access) it seems safe
31      *   enough to omit volatiles here, given how simple lazy initialization is.
32      *   This can be compared to how {@link String#hashCode} works; lazily and
33      *   without synchronization or use of volatile keyword.
34      *
35      *   Change to remove volatile was a request by implementors of a high-throughput
36      *   search framework; and they believed this is an important optimization for
37      *   heaviest, multi-core deployed use cases.
38      */

39     /*
40      * 22-Sep-2013, tatu: FWIW, there have been no reports of problems in this
41      *   area, or anything pointing to it. So I think we are safe up to JDK7
42      *   and hopefully beyond.
43      */

44     
45     protected /*volatile*/ byte[] _quotedUTF8Ref;
46
47     protected /*volatile*/ byte[] _unquotedUTF8Ref;
48
49     protected /*volatile*/ char[] _quotedChars;
50
51     public SerializedString(String v) {
52         if (v == null) {
53             throw new IllegalStateException("Null String illegal for SerializedString");
54         }
55         _value = v;
56     }
57     
58     /*
59     /**********************************************************
60     /* Serializable overrides
61     /**********************************************************
62      */

63
64     /**
65      * Ugly hack, to work through the requirement that _value is indeed final,
66      * and that JDK serialization won't call ctor(s).
67      * 
68      * @since 2.1
69      */

70     protected transient String _jdkSerializeValue;
71
72     private void readObject(ObjectInputStream in) throws IOException {
73         _jdkSerializeValue = in.readUTF();
74     }
75
76     private void writeObject(ObjectOutputStream out) throws IOException {
77         out.writeUTF(_value);
78     }
79
80     protected Object readResolve() {
81         return new SerializedString(_jdkSerializeValue);
82     }
83
84     /*
85     /**********************************************************
86     /* API
87     /**********************************************************
88      */

89
90     @Override
91     public final String getValue() { return _value; }
92     
93     /**
94      * Returns length of the String as characters
95      */

96     @Override
97     public final int charLength() { return _value.length(); }
98
99     /**
100      * Accessor for accessing value that has been quoted (escaped) using JSON
101      * quoting rules (using backslash-prefixed codes) into a char array.
102      */

103     @Override
104     public final char[] asQuotedChars() {
105         char[] result = _quotedChars;
106         if (result == null) {
107             _quotedChars = result = JSON_ENCODER.quoteAsString(_value);
108         }
109         return result;
110     }
111
112     /**
113      * Accessor for accessing value that has been quoted (escaped) using JSON
114      * quoting rules (using backslash-prefixed codes), and encoded using
115      * UTF-8 encoding into a byte array.
116      */

117     @Override
118     public final byte[] asQuotedUTF8() {
119         byte[] result = _quotedUTF8Ref;
120         if (result == null) {
121             _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
122         }
123         return result;
124     }
125
126     /**
127      * Accessor for accessing value as is (without JSON quoting (ecaping))
128      * encoded as UTF-8 byte array.
129      */

130     @Override
131     public final byte[] asUnquotedUTF8() {
132         byte[] result = _unquotedUTF8Ref;
133         if (result == null) {
134             _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
135         }
136         return result;
137     }
138
139     /*
140     /**********************************************************
141     /* Additional 2.0 methods for appending/writing contents
142     /**********************************************************
143      */

144
145     @Override
146     public int appendQuoted(char[] buffer, int offset) {
147         char[] result = _quotedChars;
148         if (result == null) {
149             _quotedChars = result = JSON_ENCODER.quoteAsString(_value);
150         }
151         final int length = result.length;
152         if ((offset + length) > buffer.length) {
153             return -1;
154         }
155         System.arraycopy(result, 0, buffer, offset, length);
156         return length;
157     }
158
159     @Override
160     public int appendQuotedUTF8(byte[] buffer, int offset) {
161         byte[] result = _quotedUTF8Ref;
162         if (result == null) {
163             _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
164         }
165         final int length = result.length;
166         if ((offset + length) > buffer.length) {
167             return -1;
168         }
169         System.arraycopy(result, 0, buffer, offset, length);
170         return length;
171     }
172
173     @Override
174     public int appendUnquoted(char[] buffer, int offset) {
175         String str = _value;
176         final int length = str.length();
177         if ((offset + length) > buffer.length) {
178             return -1;
179         }
180         str.getChars(0,  length, buffer, offset);
181         return length;
182     }
183
184     @Override
185     public int appendUnquotedUTF8(byte[] buffer, int offset) {
186         byte[] result = _unquotedUTF8Ref;
187         if (result == null) {
188             _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
189         }
190         final int length = result.length;
191         if ((offset + length) > buffer.length) {
192             return -1;
193         }
194         System.arraycopy(result, 0, buffer, offset, length);
195         return length;
196     }
197
198     @Override
199     public int writeQuotedUTF8(OutputStream out) throws IOException {
200         byte[] result = _quotedUTF8Ref;
201         if (result == null) {
202             _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
203         }
204         final int length = result.length;
205         out.write(result, 0, length);
206         return length;
207     }
208
209     @Override
210     public int writeUnquotedUTF8(OutputStream out) throws IOException {
211         byte[] result = _unquotedUTF8Ref;
212         if (result == null) {
213             _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
214         }
215         final int length = result.length;
216         out.write(result, 0, length);
217         return length;
218     }
219
220     @Override
221     public int putQuotedUTF8(ByteBuffer buffer) {
222         byte[] result = _quotedUTF8Ref;
223         if (result == null) {
224             _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
225         }
226         final int length = result.length;
227         if (length > buffer.remaining()) {
228             return -1;
229         }
230         buffer.put(result, 0, length);
231         return length;
232     }
233
234     @Override
235     public int putUnquotedUTF8(ByteBuffer buffer) {
236         byte[] result = _unquotedUTF8Ref;
237         if (result == null) {
238             _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
239         }
240         final int length = result.length;
241         if (length > buffer.remaining()) {
242             return -1;
243         }
244         buffer.put(result, 0, length);
245         return length;
246     }
247
248     /*
249     /**********************************************************
250     /* Standard method overrides
251     /**********************************************************
252      */

253
254     @Override
255     public final String toString() { return _value; }
256     
257     @Override
258     public final int hashCode() { return _value.hashCode(); }
259
260     @Override
261     public final boolean equals(Object o) {
262         if (o == thisreturn true;
263         if (o == null || o.getClass() != getClass()) return false;
264         SerializedString other = (SerializedString) o;
265         return _value.equals(other._value);
266     }
267 }
268