
001package org.hl7.fhir.r4.test.utils; 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 032import java.io.BufferedInputStream; 033import java.io.ByteArrayInputStream; 034import java.io.ByteArrayOutputStream; 035import java.io.File; 036import java.io.FileInputStream; 037import java.io.FileOutputStream; 038import java.io.IOException; 039import java.io.InputStream; 040import java.net.URL; 041import java.util.Collection; 042import java.util.HashMap; 043import java.util.List; 044import java.util.Map; 045import java.util.zip.ZipEntry; 046import java.util.zip.ZipInputStream; 047 048import org.apache.commons.io.FileUtils; 049import org.apache.commons.io.IOUtils; 050import org.apache.commons.lang3.NotImplementedException; 051import org.hl7.fhir.exceptions.FHIRException; 052import org.hl7.fhir.exceptions.FHIRFormatError; 053import org.hl7.fhir.r4.context.SimpleWorkerContext; 054import org.hl7.fhir.r4.formats.IParser.OutputStyle; 055import org.hl7.fhir.r4.formats.JsonParser; 056import org.hl7.fhir.r4.formats.RdfParser; 057import org.hl7.fhir.r4.formats.RdfParserBase; 058import org.hl7.fhir.r4.formats.XmlParser; 059import org.hl7.fhir.r4.model.Constants; 060import org.hl7.fhir.r4.model.Resource; 061import org.hl7.fhir.utilities.FileUtilities; 062import org.hl7.fhir.utilities.Utilities; 063import org.hl7.fhir.utilities.filesystem.CSFile; 064import org.hl7.fhir.utilities.filesystem.CSFileInputStream; 065import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 066import org.xmlpull.v1.XmlPullParser; 067import org.xmlpull.v1.XmlPullParserException; 068import org.xmlpull.v1.XmlPullParserFactory; 069 070@Deprecated 071@SuppressWarnings("checkstyle:systemout") 072public class ToolsHelper { 073 074 public static void main(String[] args) { 075 try { 076 ToolsHelper self = new ToolsHelper(); 077 if (args.length == 0) 078 throw new FHIRException( 079 "Missing Command Parameter. Valid Commands: round, json, version, fragments, snapshot-maker"); 080 if (args[0].equals("round")) 081 self.executeRoundTrip(args); 082 else if (args[0].equals("test")) 083 self.executeTest(args); 084 else if (args[0].equals("examples")) 085 self.executeExamples(args); 086 else if (args[0].equals("json")) 087 self.executeJson(args); 088 else if (args[0].equals("cxml")) 089 self.executeCanonicalXml(args); 090 else if (args[0].equals("version")) 091 self.executeVersion(args); 092 else if (args[0].equals("fragments")) 093 self.executeFragments(args); 094 else if (args[0].equals("snapshot-maker")) 095 self.generateSnapshots(args); 096 else 097 throw new FHIRException("Unknown command '" + args[0] 098 + "'. Valid Commands: round, test, examples, json, cxml, version, fragments, snapshot-maker"); 099 } catch (Throwable e) { 100 try { 101 e.printStackTrace(); 102 FileUtilities.stringToFile(e.toString(), (args.length == 0 ? "tools" : args[0]) + ".err"); 103 } catch (Exception e1) { 104 e1.printStackTrace(); 105 } 106 } 107 } 108 109 private void executeExamples(String[] args) throws IOException { 110 try { 111 @SuppressWarnings("unchecked") 112 List<String> lines = FileUtils.readLines(ManagedFileAccess.file(args[1]), "UTF-8"); 113 String srcDir = lines.get(0); 114 lines.remove(0); 115 processExamples(srcDir, lines); 116 FileUtilities.stringToFile("ok", FileUtilities.changeFileExt(args[1], ".out")); 117 } catch (Exception e) { 118 FileUtilities.stringToFile(e.getMessage(), FileUtilities.changeFileExt(args[1], ".out")); 119 } 120 } 121 122 private void generateSnapshots(String[] args) throws IOException, FHIRException { 123 if (args.length == 1) { 124 System.out.println("tools.jar snapshot-maker [source] -defn [definitions]"); 125 System.out.println(""); 126 System.out.println( 127 "Generates a snapshot from a differential. The nominated profile must have a single struture that has a differential"); 128 System.out.println(""); 129 System.out.println( 130 "source - the profile to generate the snapshot for. Maybe a file name, or a URL reference to a server running FHIR RESTful API"); 131 System.out.println("definitions - filename for local copy of the validation.zip file"); 132 } 133 String address = args[1]; 134 String definitions = args[3]; 135 136 SimpleWorkerContext context = SimpleWorkerContext.fromDefinitions(getDefinitions(definitions)); 137 138 // if (address.startsWith("http:") || address.startsWith("http:")) { 139 // // this is on a restful interface 140 // String[] parts = address.split("\\/Profile\\/"); 141 // if (parts.length != 2) 142 // throw new FHIRException("Unable to understand address of profile"); 143 // StructureDefinition profile = 144 // context.fetchResource(StructureDefinition.class, parts[1]); 145 // ProfileUtilities utils = new ProfileUtilities(context); 146 // StructureDefinition base = utils.getProfile(profile, profile.getBase()); 147 // if (base == null) 148 // throw new FHIRException("Unable to resolve profile "+profile.getBase()); 149 // utils.generateSnapshot(base, profile, address, profile.getName(), null, 150 // null); 151 // // client.update(StructureDefinition.class, profile, parts[1]); 152 // } else { 153 throw new NotImplementedException("generating snapshots not done yet (address = " + address + ")"); 154 // } 155 } 156 157 private Map<String, byte[]> getDefinitions(String definitions) throws IOException, FHIRException { 158 Map<String, byte[]> results = new HashMap<String, byte[]>(); 159 readDefinitions(results, loadDefinitions(definitions)); 160 return results; 161 } 162 163 private void readDefinitions(Map<String, byte[]> map, byte[] defn) throws IOException { 164 ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(defn)); 165 ZipEntry ze; 166 while ((ze = zip.getNextEntry()) != null) { 167 if (!ze.getName().endsWith(".zip") && !ze.getName().endsWith(".jar")) { // skip saxon .zip 168 String name = ze.getName(); 169 InputStream in = zip; 170 ByteArrayOutputStream b = new ByteArrayOutputStream(); 171 int n; 172 byte[] buf = new byte[1024]; 173 while ((n = in.read(buf, 0, 1024)) > -1) { 174 b.write(buf, 0, n); 175 } 176 map.put(name, b.toByteArray()); 177 } 178 zip.closeEntry(); 179 } 180 zip.close(); 181 } 182 183 private byte[] loadDefinitions(String definitions) throws FHIRException, IOException { 184 byte[] defn; 185 // if (Utilities.noString(definitions)) { 186 // defn = loadFromUrl(MASTER_SOURCE); 187 // } else 188 if (definitions.startsWith("https:") || definitions.startsWith("http:")) { 189 defn = loadFromUrl(definitions); 190 } else if (ManagedFileAccess.file(definitions).exists()) { 191 defn = loadFromFile(definitions); 192 } else 193 throw new FHIRException("Unable to find FHIR validation Pack (source = " + definitions + ")"); 194 return defn; 195 } 196 197 private byte[] loadFromUrl(String src) throws IOException { 198 URL url = new URL(src); 199 byte[] str = IOUtils.toByteArray(url.openStream()); 200 return str; 201 } 202 203 private byte[] loadFromFile(String src) throws IOException { 204 FileInputStream in = ManagedFileAccess.inStream(src); 205 byte[] b = new byte[in.available()]; 206 in.read(b); 207 in.close(); 208 return b; 209 } 210 211 protected XmlPullParser loadXml(InputStream stream) throws XmlPullParserException, IOException { 212 BufferedInputStream input = new BufferedInputStream(stream); 213 XmlPullParserFactory factory = XmlPullParserFactory 214 .newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); 215 factory.setNamespaceAware(true); 216 XmlPullParser xpp = factory.newPullParser(); 217 xpp.setInput(input, "UTF-8"); 218 xpp.next(); 219 return xpp; 220 } 221 222 protected int nextNoWhitespace(XmlPullParser xpp) throws XmlPullParserException, IOException { 223 int eventType = xpp.getEventType(); 224 while (eventType == XmlPullParser.TEXT && xpp.isWhitespace()) 225 eventType = xpp.next(); 226 return eventType; 227 } 228 229 public void executeFragments(String[] args) throws IOException { 230 try { 231 File source = ManagedFileAccess.csfile(args[1]); 232 if (!source.exists()) 233 throw new FHIRException("Source File \"" + source.getAbsolutePath() + "\" not found"); 234 XmlPullParser xpp = loadXml(ManagedFileAccess.inStream(source)); 235 nextNoWhitespace(xpp); 236 if (!xpp.getName().equals("tests")) 237 throw new FHIRFormatError("Unable to parse file - starts with " + xpp.getName()); 238 xpp.next(); 239 nextNoWhitespace(xpp); 240 StringBuilder s = new StringBuilder(); 241 s.append("<results>\r\n"); 242 int fail = 0; 243 while (xpp.getEventType() == XmlPullParser.START_TAG && xpp.getName().equals("test")) { 244 String id = xpp.getAttributeValue(null, "id"); 245 String type = xpp.getAttributeValue(null, "type"); 246 // test 247 xpp.next(); 248 nextNoWhitespace(xpp); 249 // pre 250 xpp.next(); 251 nextNoWhitespace(xpp); 252 XmlParser p = new XmlParser(); 253 try { 254 p.parseFragment(xpp, type); 255 s.append("<result id=\"" + id + "\" outcome=\"ok\"/>\r\n"); 256 nextNoWhitespace(xpp); 257 } catch (Exception e) { 258 s.append( 259 "<result id=\"" + id + "\" outcome=\"error\" msg=\"" + Utilities.escapeXml(e.getMessage()) + "\"/>\r\n"); 260 fail++; 261 } 262 while (xpp.getEventType() != XmlPullParser.END_TAG || !xpp.getName().equals("pre")) 263 xpp.next(); 264 xpp.next(); 265 nextNoWhitespace(xpp); 266 xpp.next(); 267 nextNoWhitespace(xpp); 268 } 269 s.append("</results>\r\n"); 270 271 FileUtilities.stringToFile(s.toString(), args[2]); 272 } catch (Exception e) { 273 e.printStackTrace(); 274 FileUtilities.stringToFile(e.getMessage(), args[2]); 275 } 276 } 277 278 public void executeRoundTrip(String[] args) throws IOException, FHIRException { 279 FileInputStream in; 280 File source = ManagedFileAccess.csfile(args[1]); 281 File dest = ManagedFileAccess.csfile(args[2]); 282 if (args.length >= 4) { 283 FileUtilities.copyFile(args[1], args[3]); 284 } 285 286 if (!source.exists()) 287 throw new FHIRException("Source File \"" + source.getAbsolutePath() + "\" not found"); 288 in = new CSFileInputStream(source); 289 XmlParser p = new XmlParser(); 290 JsonParser parser = new JsonParser(); 291 JsonParser pj = parser; 292 Resource rf = p.parse(in); 293 ByteArrayOutputStream json = new ByteArrayOutputStream(); 294 parser.setOutputStyle(OutputStyle.PRETTY); 295 parser.compose(json, rf); 296 json.close(); 297 FileUtilities.stringToFile(new String(json.toByteArray()), FileUtilities.changeFileExt(dest.getAbsolutePath(), ".json")); 298 rf = pj.parse(new ByteArrayInputStream(json.toByteArray())); 299 FileOutputStream s = ManagedFileAccess.outStream(dest); 300 new XmlParser().compose(s, rf, true); 301 s.close(); 302 } 303 304 public String executeJson(String[] args) throws IOException, FHIRException { 305 FileInputStream in; 306 File source = ManagedFileAccess.csfile(args[1]); 307 File dest = ManagedFileAccess.csfile(args[2]); 308 File destc = ManagedFileAccess.csfile(FileUtilities.changeFileExt(args[2], ".canonical.json")); 309 File destt = ManagedFileAccess.csfile(args[2] + ".tmp"); 310 File destr = ManagedFileAccess.csfile(FileUtilities.changeFileExt(args[2], ".ttl")); 311 312 if (!source.exists()) 313 throw new FHIRException("Source File \"" + source.getAbsolutePath() + "\" not found"); 314 in = new CSFileInputStream(source); 315 XmlParser p = new XmlParser(); 316 Resource rf = p.parse(in); 317 JsonParser json = new JsonParser(); 318 json.setOutputStyle(OutputStyle.PRETTY); 319 FileOutputStream s = ManagedFileAccess.outStream(dest); 320 json.compose(s, rf); 321 s.close(); 322 json.setOutputStyle(OutputStyle.CANONICAL); 323 s = ManagedFileAccess.outStream(destc); 324 json.compose(s, rf); 325 s.close(); 326 json.setSuppressXhtml("Snipped for Brevity"); 327 json.setOutputStyle(OutputStyle.PRETTY); 328 s = ManagedFileAccess.outStream(destt); 329 json.compose(s, rf); 330 s.close(); 331 332 RdfParserBase rdf = new RdfParser(); 333 s = ManagedFileAccess.outStream(destr); 334 rdf.compose(s, rf); 335 s.close(); 336 337 return FileUtilities.fileToString(destt.getAbsolutePath()); 338 } 339 340 public void executeCanonicalXml(String[] args) throws FHIRException, IOException { 341 FileInputStream in; 342 File source = ManagedFileAccess.csfile(args[1]); 343 File dest = ManagedFileAccess.csfile(args[2]); 344 345 if (!source.exists()) 346 throw new FHIRException("Source File \"" + source.getAbsolutePath() + "\" not found"); 347 in = new CSFileInputStream(source); 348 XmlParser p = new XmlParser(); 349 Resource rf = p.parse(in); 350 XmlParser cxml = new XmlParser(); 351 cxml.setOutputStyle(OutputStyle.NORMAL); 352 cxml.compose(ManagedFileAccess.outStream(dest), rf); 353 } 354 355 private void executeVersion(String[] args) throws IOException { 356 FileUtilities.stringToFile(org.hl7.fhir.r4.utils.Version.VERSION + ":" + Constants.VERSION, args[1]); 357 } 358 359 public void processExamples(String rootDir, Collection<String> list) throws FHIRException { 360 for (String n : list) { 361 try { 362 String filename = rootDir + n + ".xml"; 363 // 1. produce canonical XML 364 CSFileInputStream source = new CSFileInputStream(filename); 365 FileOutputStream dest = ManagedFileAccess.outStream(FileUtilities.changeFileExt(filename, ".canonical.xml")); 366 XmlParser p = new XmlParser(); 367 Resource r = p.parse(source); 368 XmlParser cxml = new XmlParser(); 369 cxml.setOutputStyle(OutputStyle.CANONICAL); 370 cxml.compose(dest, r); 371 372 // 2. produce JSON 373 source = new CSFileInputStream(filename); 374 dest = ManagedFileAccess.outStream(FileUtilities.changeFileExt(filename, ".json")); 375 r = p.parse(source); 376 JsonParser json = new JsonParser(); 377 json.setOutputStyle(OutputStyle.PRETTY); 378 json.compose(dest, r); 379 json = new JsonParser(); 380 json.setOutputStyle(OutputStyle.CANONICAL); 381 dest = ManagedFileAccess.outStream(FileUtilities.changeFileExt(filename, ".canonical.json")); 382 json.compose(dest, r); 383 384 // 2. produce JSON 385 dest = ManagedFileAccess.outStream(FileUtilities.changeFileExt(filename, ".ttl")); 386 RdfParserBase rdf = new RdfParser(); 387 rdf.compose(dest, r); 388 } catch (Exception e) { 389 e.printStackTrace(); 390 throw new FHIRException("Error Processing " + n + ".xml: " + e.getMessage(), e); 391 } 392 } 393 } 394 395 public void testRoundTrip(String rootDir, String tmpDir, Collection<String> names) throws Throwable { 396 try { 397 System.err 398 .println("Round trip from " + rootDir + " to " + tmpDir + ":" + Integer.toString(names.size()) + " files"); 399 for (String n : names) { 400 System.err.print(" " + n); 401 String source = rootDir + n + ".xml"; 402 // String tmpJson = tmpDir + n + ".json"; 403 String tmp = tmpDir + n.replace(File.separator, "-") + ".tmp"; 404 String dest = tmpDir + n.replace(File.separator, "-") + ".java.xml"; 405 406 FileInputStream in = ManagedFileAccess.inStream(source); 407 XmlParser xp = new XmlParser(); 408 Resource r = xp.parse(in); 409 System.err.print("."); 410 JsonParser jp = new JsonParser(); 411 FileOutputStream out = ManagedFileAccess.outStream(tmp); 412 jp.setOutputStyle(OutputStyle.PRETTY); 413 jp.compose(out, r); 414 out.close(); 415 r = null; 416 System.err.print("."); 417 418 in = ManagedFileAccess.inStream(tmp); 419 System.err.print(","); 420 r = jp.parse(in); 421 System.err.print("."); 422 out = ManagedFileAccess.outStream(dest); 423 new XmlParser().compose(out, r, true); 424 System.err.println("!"); 425 out.close(); 426 r = null; 427 System.gc(); 428 } 429 } catch (Throwable e) { 430 System.err.println("Error: " + e.getMessage()); 431 throw e; 432 } 433 } 434 435 private void executeTest(String[] args) throws Throwable { 436 try { 437 @SuppressWarnings("unchecked") 438 List<String> lines = FileUtils.readLines(ManagedFileAccess.file(args[1]), "UTF-8"); 439 String srcDir = lines.get(0); 440 lines.remove(0); 441 String dstDir = lines.get(0).trim(); 442 lines.remove(0); 443 testRoundTrip(srcDir, dstDir, lines); 444 FileUtilities.stringToFile("ok", FileUtilities.changeFileExt(args[1], ".out")); 445 } catch (Exception e) { 446 FileUtilities.stringToFile(e.getMessage(), FileUtilities.changeFileExt(args[1], ".out")); 447 } 448 } 449}