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