View Javadoc
1   /**
2    * Copyright 2014 Internet2
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  /*
17   * Copyright (C) 2006 Joe Walnes.
18   * Copyright (C) 2006, 2007, 2008 XStream Committers.
19   * All rights reserved.
20   *
21   * The software in this package is published under the terms of the BSD
22   * style license a copy of which has been included with this distribution in
23   * the LICENSE.txt file.
24   * 
25   * Created on 22. June 2006 by Mauro Talevi
26   */
27  package edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.io.json;
28  
29  import edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.core.util.FastStack;
30  import edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.core.util.Primitives;
31  import edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.core.util.QuickWriter;
32  import edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriter;
33  import edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.io.HierarchicalStreamWriter;
34  
35  import java.io.Writer;
36  import java.util.Collection;
37  import java.util.Map;
38  
39  
40  /**
41   * A simple writer that outputs JSON in a pretty-printed indented stream. Arrays, Lists and Sets
42   * rely on you NOT using XStream.addImplicitCollection(..)
43   * 
44   * @author Paul Hammant
45   * @author Jörg Schaible
46   * @since 1.2
47   */
48  public class JsonHierarchicalStreamWriter implements ExtendedHierarchicalStreamWriter {
49  
50      private final QuickWriter writer;
51      private final FastStackre/grouperClientExt/com/thoughtworks/xstream/core/util/FastStack.html#FastStack">FastStack elementStack = new FastStack(16);
52      private final char[] lineIndenter;
53  
54      private int depth;
55      private boolean readyForNewLine;
56      private boolean tagIsEmpty;
57      private String newLine;
58  
59      public JsonHierarchicalStreamWriter(Writer writer, char[] lineIndenter, String newLine) {
60          this.writer = new QuickWriter(writer);
61          this.lineIndenter = lineIndenter;
62          this.newLine = newLine;
63      }
64  
65      public JsonHierarchicalStreamWriter(Writer writer, char[] lineIndenter) {
66          this(writer, lineIndenter, "\n");
67      }
68  
69      public JsonHierarchicalStreamWriter(Writer writer, String lineIndenter, String newLine) {
70          this(writer, lineIndenter.toCharArray(), newLine);
71      }
72  
73      public JsonHierarchicalStreamWriter(Writer writer, String lineIndenter) {
74          this(writer, lineIndenter.toCharArray());
75      }
76  
77      public JsonHierarchicalStreamWriter(Writer writer) {
78          this(writer, new char[]{' ', ' '});
79      }
80  
81      /**
82       * @deprecated Use startNode(String name, Class clazz) instead.
83       */
84  
85      public void startNode(String name) {
86          startNode(name, null);
87      }
88  
89      public void startNode(String name, Class clazz) {
90          Node currNode = (Node)elementStack.peek();
91          if (currNode == null) {
92              writer.write("{");
93          }
94          if (currNode != null && currNode.fieldAlready) {
95              writer.write(",");
96              readyForNewLine = true;
97          }
98          tagIsEmpty = false;
99          finishTag();
100         if (currNode == null
101             || currNode.clazz == null
102             || (currNode.clazz != null && !currNode.isCollection)) {
103             if (currNode != null && !currNode.fieldAlready) {
104                 writer.write("{");
105                 readyForNewLine = true;
106                 finishTag();
107             }
108             writer.write("\"");
109             writer.write(name);
110             writer.write("\": ");
111         }
112         if (isCollection(clazz)) {
113             writer.write("[");
114             readyForNewLine = true;
115         }
116         if (currNode != null) {
117             currNode.fieldAlready = true;
118         }
119         elementStack.push(new Node(name, clazz));
120         depth++ ;
121         tagIsEmpty = true;
122     }
123 
124     public class Node {
125         public final String name;
126         public final Class clazz;
127         public boolean fieldAlready;
128         public boolean isCollection;
129 
130         public Node(String name, Class clazz) {
131             this.name = name;
132             this.clazz = clazz;
133             isCollection = isCollection(clazz);
134         }
135     }
136 
137     public void setValue(String text) {
138         readyForNewLine = false;
139         tagIsEmpty = false;
140         finishTag();
141         writeText(writer, text);
142     }
143 
144     public void addAttribute(String key, String value) {
145         Node currNode = (Node)elementStack.peek();
146         if (currNode == null || !currNode.isCollection) {
147             startNode('@' + key, String.class);
148             tagIsEmpty = false;
149             writeText(value, String.class);
150             endNode();
151         }
152     }
153 
154     protected void writeAttributeValue(QuickWriter writer, String text) {
155         writeText(text, null);
156     }
157 
158     protected void writeText(QuickWriter writer, String text) {
159         Node foo = (Node)elementStack.peek();
160 
161         writeText(text, foo.clazz);
162     }
163 
164     private void writeText(String text, Class clazz) {
165         if (needsQuotes(clazz)) {
166             writer.write("\"");
167         }
168         if ((clazz == Character.class || clazz == Character.TYPE) && "".equals(text)) {
169             text = "\0";
170         }
171 
172         int length = text.length();
173         for (int i = 0; i < length; i++ ) {
174             char c = text.charAt(i);
175             switch (c) {
176             case '"':
177                 this.writer.write("\\\"");
178                 break;
179             case '\\':
180                 this.writer.write("\\\\");
181                 break;
182             default:
183                 if (c > 0x1f) {
184                     this.writer.write(c);
185                 } else {
186                     this.writer.write("\\u");
187                     String hex = "000" + Integer.toHexString(c);
188                     this.writer.write(hex.substring(hex.length() - 4));
189                 }
190             }
191         }
192 
193         if (needsQuotes(clazz)) {
194             writer.write("\"");
195         }
196     }
197 
198     private boolean isCollection(Class clazz) {
199         return clazz != null
200             && (Collection.class.isAssignableFrom(clazz)
201                 || clazz.isArray()
202                 || Map.class.isAssignableFrom(clazz) || Map.Entry.class.isAssignableFrom(clazz));
203     }
204 
205     private boolean needsQuotes(Class clazz) {
206         clazz = clazz != null && clazz.isPrimitive() ? clazz : Primitives.unbox(clazz);
207         return clazz == null || clazz == Character.TYPE;
208     }
209 
210     public void endNode() {
211         depth-- ;
212         Node node = (Node)elementStack.pop();
213         if (node.clazz != null && node.isCollection) {
214             if (node.fieldAlready) {
215                 readyForNewLine = true;
216             }
217             finishTag();
218             writer.write("]");
219         } else if (tagIsEmpty) {
220             readyForNewLine = false;
221             writer.write("{}");
222             finishTag();
223         } else {
224             finishTag();
225             if (node.fieldAlready) {
226                 writer.write("}");
227             }
228         }
229         readyForNewLine = true;
230         if (depth == 0) {
231             writer.write("}");
232             writer.flush();
233         }
234     }
235 
236     private void finishTag() {
237         if (readyForNewLine) {
238             endOfLine();
239         }
240         readyForNewLine = false;
241         tagIsEmpty = false;
242     }
243 
244     protected void endOfLine() {
245         writer.write(newLine);
246         for (int i = 0; i < depth; i++ ) {
247             writer.write(lineIndenter);
248         }
249     }
250 
251     public void flush() {
252         writer.flush();
253     }
254 
255     public void close() {
256         writer.close();
257     }
258 
259     public HierarchicalStreamWriter underlyingWriter() {
260         return this;
261     }
262 }