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