1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
42
43
44
45
46
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
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 }