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