001package org.hl7.fhir.dstu2.formats;
002
003/*
004  Copyright (c) 2011+, HL7, Inc.
005  All rights reserved.
006  
007  Redistribution and use in source and binary forms, with or without modification, 
008  are permitted provided that the following conditions are met:
009    
010   * Redistributions of source code must retain the above copyright notice, this 
011     list of conditions and the following disclaimer.
012   * Redistributions in binary form must reproduce the above copyright notice, 
013     this list of conditions and the following disclaimer in the documentation 
014     and/or other materials provided with the distribution.
015   * Neither the name of HL7 nor the names of its contributors may be used to 
016     endorse or promote products derived from this software without specific 
017     prior written permission.
018  
019  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
020  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
021  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
022  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
023  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
024  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
025  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
026  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
027  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
028  POSSIBILITY OF SUCH DAMAGE.
029  
030 */
031
032import java.io.IOException;
033import java.io.OutputStreamWriter;
034import java.math.BigDecimal;
035import java.util.ArrayList;
036import java.util.Collections;
037import java.util.List;
038import java.util.Stack;
039
040import com.google.gson.stream.JsonWriter;
041
042public class JsonCreatorCanonical implements JsonCreator {
043
044  public class JsonCanValue {
045    String name;
046
047    private JsonCanValue(String name) {
048      this.name = name;
049    }
050  }
051
052  private class JsonCanNumberValue extends JsonCanValue {
053    private BigDecimal value;
054
055    private JsonCanNumberValue(String name, BigDecimal value) {
056      super(name);
057      this.value = value;
058    }
059  }
060
061  private class JsonCanIntegerValue extends JsonCanValue {
062    private Integer value;
063
064    private JsonCanIntegerValue(String name, Integer value) {
065      super(name);
066      this.value = value;
067    }
068  }
069
070  private class JsonCanBooleanValue extends JsonCanValue {
071    private Boolean value;
072
073    private JsonCanBooleanValue(String name, Boolean value) {
074      super(name);
075      this.value = value;
076    }
077  }
078
079  private class JsonCanStringValue extends JsonCanValue {
080    private String value;
081
082    private JsonCanStringValue(String name, String value) {
083      super(name);
084      this.value = value;
085    }
086  }
087
088  private class JsonCanNullValue extends JsonCanValue {
089    private JsonCanNullValue(String name) {
090      super(name);
091    }
092  }
093
094  public class JsonCanObject extends JsonCanValue {
095
096    boolean array;
097    List<JsonCanValue> children = new ArrayList<JsonCanValue>();
098
099    public JsonCanObject(String name, boolean array) {
100      super(name);
101      this.array = array;
102    }
103
104    public void addProp(JsonCanValue obj) {
105      children.add(obj);
106    }
107  }
108
109  Stack<JsonCanObject> stack;
110  JsonCanObject root;
111  JsonWriter gson;
112  String name;
113
114  public JsonCreatorCanonical(OutputStreamWriter osw) {
115    stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
116    gson = new JsonWriter(osw);
117    name = null;
118  }
119
120  private String takeName() {
121    String res = name;
122    name = null;
123    return res;
124  }
125
126  @Override
127  public void setIndent(String indent) {
128    if (!indent.equals(""))
129      throw new Error("do not use pretty when canonical is set");
130    gson.setIndent(indent);
131  }
132
133  @Override
134  public void beginObject() throws IOException {
135    JsonCanObject obj = new JsonCanObject(takeName(), false);
136    if (stack.isEmpty())
137      root = obj;
138    else
139      stack.peek().addProp(obj);
140    stack.push(obj);
141  }
142
143  @Override
144  public void endObject() throws IOException {
145    stack.pop();
146  }
147
148  @Override
149  public void nullValue() throws IOException {
150    stack.peek().addProp(new JsonCanNullValue(takeName()));
151  }
152
153  @Override
154  public void name(String name) throws IOException {
155    this.name = name;
156  }
157
158  @Override
159  public void value(String value) throws IOException {
160    stack.peek().addProp(new JsonCanStringValue(takeName(), value));
161  }
162
163  @Override
164  public void value(Boolean value) throws IOException {
165    stack.peek().addProp(new JsonCanBooleanValue(takeName(), value));
166  }
167
168  @Override
169  public void value(BigDecimal value) throws IOException {
170    stack.peek().addProp(new JsonCanNumberValue(takeName(), value));
171  }
172
173  @Override
174  public void value(Integer value) throws IOException {
175    stack.peek().addProp(new JsonCanIntegerValue(takeName(), value));
176  }
177
178  @Override
179  public void beginArray() throws IOException {
180    JsonCanObject obj = new JsonCanObject(takeName(), true);
181    if (!stack.isEmpty())
182      stack.peek().addProp(obj);
183    stack.push(obj);
184
185  }
186
187  @Override
188  public void endArray() throws IOException {
189    stack.pop();
190  }
191
192  @Override
193  public void finish() throws IOException {
194    writeObject(root);
195  }
196
197  private void writeObject(JsonCanObject obj) throws IOException {
198    gson.beginObject();
199    List<String> names = new ArrayList<String>();
200    for (JsonCanValue v : obj.children)
201      names.add(v.name);
202    Collections.sort(names);
203    for (String n : names) {
204      gson.name(n);
205      JsonCanValue v = getPropForName(n, obj.children);
206      if (v instanceof JsonCanNumberValue)
207        gson.value(((JsonCanNumberValue) v).value);
208      else if (v instanceof JsonCanIntegerValue)
209        gson.value(((JsonCanIntegerValue) v).value);
210      else if (v instanceof JsonCanBooleanValue)
211        gson.value(((JsonCanBooleanValue) v).value);
212      else if (v instanceof JsonCanStringValue)
213        gson.value(((JsonCanStringValue) v).value);
214      else if (v instanceof JsonCanNullValue)
215        gson.nullValue();
216      else if (v instanceof JsonCanObject) {
217        JsonCanObject o = (JsonCanObject) v;
218        if (o.array)
219          writeArray(o);
220        else
221          writeObject(o);
222      } else
223        throw new Error("not possible");
224    }
225    gson.endObject();
226  }
227
228  private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
229    for (JsonCanValue child : children)
230      if (child.name.equals(name))
231        return child;
232    return null;
233  }
234
235  private void writeArray(JsonCanObject arr) throws IOException {
236    gson.beginArray();
237    for (JsonCanValue v : arr.children) {
238      if (v instanceof JsonCanNumberValue)
239        gson.value(((JsonCanNumberValue) v).value);
240      else if (v instanceof JsonCanIntegerValue)
241        gson.value(((JsonCanIntegerValue) v).value);
242      else if (v instanceof JsonCanBooleanValue)
243        gson.value(((JsonCanBooleanValue) v).value);
244      else if (v instanceof JsonCanStringValue)
245        gson.value(((JsonCanStringValue) v).value);
246      else if (v instanceof JsonCanNullValue)
247        gson.nullValue();
248      else if (v instanceof JsonCanObject) {
249        JsonCanObject o = (JsonCanObject) v;
250        if (o.array)
251          writeArray(o);
252        else
253          writeObject(o);
254      } else
255        throw new Error("not possible");
256    }
257    gson.endArray();
258  }
259
260}