001package org.hl7.fhir.r4.formats;
002
003/*-
004 * #%L
005 * org.hl7.fhir.r4
006 * %%
007 * Copyright (C) 2014 - 2019 Health Level 7
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023
024import java.io.IOException;
025import java.io.OutputStreamWriter;
026import java.math.BigDecimal;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.List;
030import java.util.Stack;
031
032public class JsonCreatorCanonical implements JsonCreator {
033
034  public class JsonCanValue {
035    String name;
036    private JsonCanValue(String name) {
037      this.name = name;  
038   }
039  }
040
041  private class JsonCanNumberValue extends JsonCanValue {
042    private BigDecimal value;
043    private JsonCanNumberValue(String name, BigDecimal value) {
044      super(name);
045      this.value = value;  
046    }
047  }
048
049  private class JsonCanPresentedNumberValue extends JsonCanValue {
050    private String value;
051    private JsonCanPresentedNumberValue(String name, String value) {
052      super(name);
053      this.value = value;  
054    }
055  }
056
057  private class JsonCanIntegerValue extends JsonCanValue {
058    private Integer value;
059    private JsonCanIntegerValue(String name, Integer value) {
060      super(name);
061      this.value = value;  
062    }
063  }
064
065  private class JsonCanBooleanValue extends JsonCanValue  {
066    private Boolean value;
067    private JsonCanBooleanValue(String name, Boolean value) {
068      super(name);
069      this.value = value;  
070    }
071  }
072
073  private class JsonCanStringValue extends JsonCanValue {
074    private String value;
075    private JsonCanStringValue(String name, String value) {
076      super(name);
077      this.value = value;  
078    }
079  }
080
081  private class JsonCanNullValue extends JsonCanValue  {
082    private JsonCanNullValue(String name) {
083      super(name);
084    }
085  }
086
087  public class JsonCanObject extends JsonCanValue {
088
089    boolean array;
090    List<JsonCanValue> children = new ArrayList<JsonCanValue>();
091    
092    public JsonCanObject(String name, boolean array) {
093      super(name);
094      this.array = array;
095    }
096
097    public void addProp(JsonCanValue obj) {
098      children.add(obj);
099    }
100  }
101
102  Stack<JsonCanObject> stack;
103  JsonCanObject root; 
104  JsonCreatorDirect jj;
105  String name;
106  
107  public JsonCreatorCanonical(OutputStreamWriter osw) {
108    stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
109    jj = new JsonCreatorDirect(osw);
110    name = null;
111  }
112
113  private String takeName() {
114    String res = name;
115    name = null;
116    return res;
117  }
118  
119  @Override
120  public void setIndent(String indent) {
121    if (!indent.equals(""))
122      throw new Error("do not use pretty when canonical is set");
123    jj.setIndent(indent);
124  }
125
126  @Override
127  public void beginObject() throws IOException {
128    JsonCanObject obj = new JsonCanObject(takeName(), false);
129    if (stack.isEmpty())
130      root = obj;
131    else
132      stack.peek().addProp(obj);
133    stack.push(obj);
134  }
135
136  @Override
137  public void endObject() throws IOException {
138    stack.pop();
139  }
140
141  @Override
142  public void nullValue() throws IOException {
143    stack.peek().addProp(new JsonCanNullValue(takeName()));
144  }
145
146  @Override
147  public void name(String name) throws IOException {
148    this.name = name;
149  }
150
151  @Override
152  public void value(String value) throws IOException {
153    stack.peek().addProp(new JsonCanStringValue(takeName(), value));    
154  }
155
156  @Override
157  public void value(Boolean value) throws IOException {
158    stack.peek().addProp(new JsonCanBooleanValue(takeName(), value));    
159  }
160
161  @Override
162  public void value(BigDecimal value) throws IOException {
163    stack.peek().addProp(new JsonCanNumberValue(takeName(), value));    
164  }
165  @Override
166  public void valueNum(String value) throws IOException {
167    stack.peek().addProp(new JsonCanPresentedNumberValue(takeName(), value));    
168  }
169
170
171  @Override
172  public void value(Integer value) throws IOException {
173    stack.peek().addProp(new JsonCanIntegerValue(takeName(), value));    
174  }
175
176  @Override
177  public void beginArray() throws IOException {
178    JsonCanObject obj = new JsonCanObject(takeName(), true);
179    if (!stack.isEmpty())
180      stack.peek().addProp(obj);
181    stack.push(obj);
182    
183  }
184
185  @Override
186  public void endArray() throws IOException {
187    stack.pop();    
188  }
189
190  @Override
191  public void finish() throws IOException {
192    writeObject(root);
193  }
194
195  private void writeObject(JsonCanObject obj) throws IOException {
196    jj.beginObject();
197    List<String> names = new ArrayList<String>();
198    for (JsonCanValue v : obj.children) 
199      names.add(v.name);
200    Collections.sort(names);
201    for (String n : names) {
202      jj.name(n);
203      JsonCanValue v = getPropForName(n, obj.children);
204      if (v instanceof JsonCanNumberValue)
205        jj.value(((JsonCanNumberValue) v).value);
206      else if (v instanceof JsonCanPresentedNumberValue)
207        jj.valueNum(((JsonCanPresentedNumberValue) v).value);
208      else if (v instanceof JsonCanIntegerValue)
209        jj.value(((JsonCanIntegerValue) v).value);
210      else if (v instanceof JsonCanBooleanValue)
211        jj.value(((JsonCanBooleanValue) v).value);
212      else if (v instanceof JsonCanStringValue)
213        jj.value(((JsonCanStringValue) v).value);
214      else if (v instanceof JsonCanNullValue)
215        jj.nullValue();
216      else if (v instanceof JsonCanObject) {
217        JsonCanObject o = (JsonCanObject) v;
218        if (o.array) 
219          writeArray(o);
220        else
221          writeObject(o);
222      } else
223        throw new Error("not possible");
224    }
225    jj.endObject();
226  }
227
228  private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
229    for (JsonCanValue child : children)
230      if (child.name.equals(name))
231        return child;
232    return null;
233  }
234
235  private void writeArray(JsonCanObject arr) throws IOException {
236    jj.beginArray();
237    for (JsonCanValue v : arr.children) { 
238      if (v instanceof JsonCanNumberValue)
239        jj.value(((JsonCanNumberValue) v).value);
240      else if (v instanceof JsonCanIntegerValue)
241          jj.value(((JsonCanIntegerValue) v).value);
242      else if (v instanceof JsonCanBooleanValue)
243        jj.value(((JsonCanBooleanValue) v).value);
244      else if (v instanceof JsonCanStringValue)
245        jj.value(((JsonCanStringValue) v).value);
246      else if (v instanceof JsonCanNullValue)
247        jj.nullValue();
248      else if (v instanceof JsonCanObject) {
249        JsonCanObject o = (JsonCanObject) v;
250        if (o.array) 
251          writeArray(o);
252        else
253          writeObject(o);
254      } else
255        throw new Error("not possible");
256    }
257    jj.endArray();    
258  }
259
260  @Override
261  public void link(String href) {
262    // not used
263  }
264       
265    
266}