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