001package org.hl7.fhir.r5.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 @Override 276 public void anchor(String name) { 277 // not used 278 } 279 280 281}