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