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