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