001package org.hl7.fhir.dstu3.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
032
033
034import java.io.IOException;
035import java.io.OutputStreamWriter;
036import java.math.BigDecimal;
037import java.util.ArrayList;
038import java.util.Collections;
039import java.util.List;
040import java.util.Stack;
041
042import com.google.gson.stream.JsonWriter;
043
044public class JsonCreatorCanonical implements JsonCreator {
045
046  public class JsonCanValue {
047    String name;
048    private JsonCanValue(String name) {
049      this.name = name;  
050   }
051  }
052
053  private class JsonCanNumberValue extends JsonCanValue {
054    private BigDecimal value;
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    private JsonCanIntegerValue(String name, Integer value) {
064      super(name);
065      this.value = value;  
066    }
067  }
068
069  private class JsonCanBooleanValue extends JsonCanValue  {
070    private Boolean value;
071    private JsonCanBooleanValue(String name, Boolean value) {
072      super(name);
073      this.value = value;  
074    }
075  }
076
077  private class JsonCanStringValue extends JsonCanValue {
078    private String value;
079    private JsonCanStringValue(String name, String value) {
080      super(name);
081      this.value = value;  
082    }
083  }
084
085  private class JsonCanNullValue extends JsonCanValue  {
086    private JsonCanNullValue(String name) {
087      super(name);
088    }
089  }
090
091  public class JsonCanObject extends JsonCanValue {
092
093    boolean array;
094    List<JsonCanValue> children = new ArrayList<JsonCanValue>();
095    
096    public JsonCanObject(String name, boolean array) {
097      super(name);
098      this.array = array;
099    }
100
101    public void addProp(JsonCanValue obj) {
102      children.add(obj);
103    }
104  }
105
106  Stack<JsonCanObject> stack;
107  JsonCanObject root; 
108  JsonWriter gson;
109  String name;
110  
111  public JsonCreatorCanonical(OutputStreamWriter osw) {
112    stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
113    gson = new JsonWriter(osw);
114    name = null;
115  }
116
117  private String takeName() {
118    String res = name;
119    name = null;
120    return res;
121  }
122  
123  @Override
124  public void setIndent(String indent) {
125    if (!indent.equals(""))
126      throw new Error("do not use pretty when canonical is set");
127    gson.setIndent(indent);
128  }
129
130  @Override
131  public void beginObject() throws IOException {
132    JsonCanObject obj = new JsonCanObject(takeName(), false);
133    if (stack.isEmpty())
134      root = obj;
135    else
136      stack.peek().addProp(obj);
137    stack.push(obj);
138  }
139
140  @Override
141  public void endObject() throws IOException {
142    stack.pop();
143  }
144
145  @Override
146  public void nullValue() throws IOException {
147    stack.peek().addProp(new JsonCanNullValue(takeName()));
148  }
149
150  @Override
151  public void name(String name) throws IOException {
152    this.name = name;
153  }
154
155  @Override
156  public void value(String value) throws IOException {
157    stack.peek().addProp(new JsonCanStringValue(takeName(), value));    
158  }
159
160  @Override
161  public void value(Boolean value) throws IOException {
162    stack.peek().addProp(new JsonCanBooleanValue(takeName(), value));    
163  }
164
165  @Override
166  public void value(BigDecimal value) throws IOException {
167    stack.peek().addProp(new JsonCanNumberValue(takeName(), value));    
168  }
169
170  @Override
171  public void value(Integer value) throws IOException {
172    stack.peek().addProp(new JsonCanIntegerValue(takeName(), value));    
173  }
174
175  @Override
176  public void beginArray() throws IOException {
177    JsonCanObject obj = new JsonCanObject(takeName(), true);
178    if (!stack.isEmpty())
179      stack.peek().addProp(obj);
180    stack.push(obj);
181    
182  }
183
184  @Override
185  public void endArray() throws IOException {
186    stack.pop();    
187  }
188
189  @Override
190  public void finish() throws IOException {
191    writeObject(root);
192  }
193
194  private void writeObject(JsonCanObject obj) throws IOException {
195    gson.beginObject();
196    List<String> names = new ArrayList<String>();
197    for (JsonCanValue v : obj.children) 
198      names.add(v.name);
199    Collections.sort(names);
200    for (String n : names) {
201      gson.name(n);
202      JsonCanValue v = getPropForName(n, obj.children);
203      if (v instanceof JsonCanNumberValue)
204        gson.value(((JsonCanNumberValue) v).value);
205      else if (v instanceof JsonCanIntegerValue)
206          gson.value(((JsonCanIntegerValue) v).value);
207      else if (v instanceof JsonCanBooleanValue)
208        gson.value(((JsonCanBooleanValue) v).value);
209      else if (v instanceof JsonCanStringValue)
210        gson.value(((JsonCanStringValue) v).value);
211      else if (v instanceof JsonCanNullValue)
212        gson.nullValue();
213      else if (v instanceof JsonCanObject) {
214        JsonCanObject o = (JsonCanObject) v;
215        if (o.array) 
216          writeArray(o);
217        else
218          writeObject(o);
219      } else
220        throw new Error("not possible");
221    }
222    gson.endObject();
223  }
224
225  private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
226    for (JsonCanValue child : children)
227      if (child.name.equals(name))
228        return child;
229    return null;
230  }
231
232  private void writeArray(JsonCanObject arr) throws IOException {
233    gson.beginArray();
234    for (JsonCanValue v : arr.children) { 
235      if (v instanceof JsonCanNumberValue)
236        gson.value(((JsonCanNumberValue) v).value);
237      else if (v instanceof JsonCanIntegerValue)
238          gson.value(((JsonCanIntegerValue) v).value);
239      else if (v instanceof JsonCanBooleanValue)
240        gson.value(((JsonCanBooleanValue) v).value);
241      else if (v instanceof JsonCanStringValue)
242        gson.value(((JsonCanStringValue) v).value);
243      else if (v instanceof JsonCanNullValue)
244        gson.nullValue();
245      else if (v instanceof JsonCanObject) {
246        JsonCanObject o = (JsonCanObject) v;
247        if (o.array) 
248          writeArray(o);
249        else
250          writeObject(o);
251      } else
252        throw new Error("not possible");
253    }
254    gson.endArray();    
255  }
256
257  @Override
258  public void link(String href) {
259    // not used
260  }
261       
262    
263}