View Javadoc

1   /**
2    * Copyright (C) 2005-2009 Alfresco Software Limited.
3    *
4    * This file is part of the Spring Surf Extension project.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.springframework.extensions.webscripts.json;
20  
21  import java.io.IOException;
22  import java.io.Writer;
23  import java.util.Stack;
24  
25  /**
26   * Fast and simple JSON stream writer. Wraps a Writer to output a JSON object stream.
27   * No intermediate objects are created - writes are immediate to the underlying stream.
28   * Quoted and correct JSON encoding is performed on string values, - encoding is
29   * not performed on key names - it is assumed they are simple strings. The developer must
30   * call JSONWriter.encodeJSONString() on the key name if required.
31   * 
32   * @author Kevin Roast
33   */
34  public final class JSONWriter
35  {
36     private Writer out;
37     private Stack<Boolean> stack = new Stack<Boolean>();
38     
39     public JSONWriter(Writer out)
40     {
41        this.out = out;
42        stack.push(Boolean.FALSE);
43     }
44     
45     public void startArray() throws IOException
46     {
47        if (stack.peek() == true) out.write(", ");
48        out.write("[");
49        stack.pop();
50        stack.push(Boolean.TRUE);
51        stack.push(Boolean.FALSE);
52     }
53     
54     public void endArray() throws IOException
55     {
56        out.write("]");
57        stack.pop();
58     }
59     
60     public void startObject() throws IOException
61     {
62        if (stack.peek() == true) out.write(", ");
63        out.write("{");
64        stack.pop();
65        stack.push(Boolean.TRUE);
66        stack.push(Boolean.FALSE);
67     }
68     
69     public void endObject() throws IOException
70     {
71        out.write("}");
72        stack.pop();
73     }
74     
75     public void startValue(String name) throws IOException
76     {
77        if (stack.peek() == true) out.write(", ");
78        out.write('"');
79        out.write(name);
80        out.write("\": ");
81        stack.pop();
82        stack.push(Boolean.TRUE);
83        stack.push(Boolean.FALSE);
84     }
85     
86     public void endValue()
87     {
88        stack.pop();
89     }
90     
91     public void writeValue(String name, String value) throws IOException
92     {
93        if (stack.peek() == true) out.write(", ");
94        out.write('"');
95        out.write(name);
96        out.write("\": \"");
97        out.write(encodeJSONString(value));
98        out.write('"');
99        stack.pop();
100       stack.push(Boolean.TRUE);
101    }
102    
103    public void writeValue(String name, int value) throws IOException
104    {
105       if (stack.peek() == true) out.write(", ");
106       out.write('"');
107       out.write(name);
108       out.write("\": ");
109       out.write(Integer.toString(value));
110       stack.pop();
111       stack.push(Boolean.TRUE);
112    }
113    
114    public void writeValue(String name, boolean value) throws IOException
115    {
116       if (stack.peek() == true) out.write(", ");
117       out.write('"');
118       out.write(name);
119       out.write("\": ");
120       out.write(Boolean.toString(value));
121       stack.pop();
122       stack.push(Boolean.TRUE);
123    }
124    
125    public void writeNullValue(String name) throws IOException
126    {
127       if (stack.peek() == true) out.write(", ");
128       out.write('"');
129       out.write(name);
130       out.write("\": null");
131       stack.pop();
132       stack.push(Boolean.TRUE);
133    }
134 
135    public static String encodeJSONString(final String s)
136    {
137        if (s == null || s.length() == 0)
138        {
139            return "";
140        }
141        
142        StringBuilder sb = null;      // create on demand
143        String enc;
144        char c;
145        int len = s.length();
146        for (int i = 0; i < len; i++)
147        {
148            enc = null;
149            c = s.charAt(i);
150            switch (c)
151            {
152                case '\\':
153                    enc = "\\\\";
154                    break;
155                case '"':
156                    enc = "\\\"";
157                    break;
158                case '/':
159                    enc = "\\/";
160                    break;
161                case '\b':
162                    enc = "\\b";
163                    break;
164                case '\t':
165                    enc = "\\t";
166                    break;
167                case '\n':
168                    enc = "\\n";
169                    break;
170                case '\f':
171                    enc = "\\f";
172                    break;
173                case '\r':
174                    enc = "\\r";
175                    break;
176 
177                default:
178                    if (((int)c) >= 0x80)
179                    {
180                        //encode all non basic latin characters
181                        String u = "000" + Integer.toHexString((int)c);
182                        enc = "\\u" + u.substring(u.length() - 4);
183 
184                    }
185                break;
186            }
187 
188            if (enc != null)
189            {
190                if (sb == null)
191                {
192                    String soFar = s.substring(0, i);
193                    sb = new StringBuilder(i + 8);
194                    sb.append(soFar);
195                }
196                sb.append(enc);
197            }
198            else
199            {
200                if (sb != null)
201                {
202                    sb.append(c);
203                }
204            }
205        }
206 
207        if (sb == null)
208        {
209            return s;
210        }
211        else
212        {
213            return sb.toString();
214        }
215    }
216 }