001package org.hl7.fhir.instance.utilities.xml;
002
003import java.io.IOException;
004import java.io.OutputStream;
005import java.io.UnsupportedEncodingException;
006import java.util.ArrayList;
007import java.util.List;
008
009import org.hl7.fhir.instance.utilities.TextStreamWriter;
010import org.hl7.fhir.instance.utilities.Utilities;
011import org.hl7.fhir.instance.utilities.xml.SchematronWriter.SchematronType;
012import org.hl7.fhir.instance.utilities.xml.SchematronWriter.Section;
013
014
015public class SchematronWriter  extends TextStreamWriter  {
016
017  public enum SchematronType {
018    ALL_RESOURCES,
019    RESOURCE,
020    PROFILE
021  }
022
023  public class Assert {
024    private String test;
025    private String message; 
026  }
027  
028  public class Rule {
029    private String name; 
030    private List<Assert> asserts = new ArrayList<Assert>();   
031    public void assrt(String test, String message) {
032      Assert a = new Assert();
033      a.test = test;
034      a.message = message;
035      asserts.add(a);
036    }
037  }
038  public class Section {
039    private String title;
040    private List<Rule> rules = new ArrayList<Rule>();
041    
042    public String getTitle() {
043      return title;
044    }
045
046    public void setTitle(String title) {
047      this.title = title;
048    }
049
050    public Rule rule(String name) {
051      for (Rule r : rules) {
052        if (r.name.equals(name))
053          return r;
054      }
055      Rule r = new Rule();
056      r.name = name;
057      rules.add(r);
058      return r;
059    }
060  }
061
062  private SchematronType type;
063  private String description;
064  private List<Section> sections = new ArrayList<Section>();
065
066
067  public SchematronWriter(OutputStream out, SchematronType type, String description) throws UnsupportedEncodingException {
068    super(out);
069    this.type = type;
070    this.description = description;
071  }
072
073  public Section section(String title) {
074    for (Section s : sections) {
075      if (s.title.equals(title))
076        return s;
077    }
078    Section s = new Section();
079    s.title = title;
080    sections.add(s);
081    return s;
082  }
083  
084  public void dump() throws IOException {
085    ln("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
086    ln_i("<sch:schema xmlns:sch=\"http://purl.oclc.org/dsdl/schematron\" queryBinding=\"xslt2\">");
087    ln("<sch:ns prefix=\"f\" uri=\"http://hl7.org/fhir\"/>");
088    ln("<sch:ns prefix=\"h\" uri=\"http://www.w3.org/1999/xhtml\"/>");
089    addNote();
090
091    for (Section s : sections) {
092      ln_i("<sch:pattern>");
093      ln("<sch:title>"+s.title+"</sch:title>");
094      for (Rule r : s.rules) {
095        if (!r.asserts.isEmpty()) {
096          ln_i("<sch:rule context=\""+r.name+"\">");
097          for (Assert a : r.asserts) 
098            ln("<sch:assert test=\""+Utilities.escapeXml(a.test)+"\">"+Utilities.escapeXml(a.message)+"</sch:assert>");
099          ln_o("</sch:rule>");
100        }
101      }
102      ln_o("</sch:pattern>");
103    }  
104    ln_o("</sch:schema>");
105    flush();
106    close();
107  }
108
109  private void addNote() throws IOException {
110    switch (type) {
111    case ALL_RESOURCES : addAllResourcesNote(); break;
112    case RESOURCE : addResourceNote(); break;
113    case PROFILE : addProfileNote(); break;
114    }
115  }
116
117  private void addAllResourcesNote() throws IOException {
118    ln("<!-- ");
119    ln("  This file contains constraints for all resources");
120    ln("  Because of the way containment works, this file should always )");
121    ln("  be used for validating resources. Alternatively you can use ");
122    ln("  the resource specific files to build a smaller version of");
123    ln("  this file (the contents are identical; only include those ");
124    ln("  resources relevant to your implementation).");
125    ln("-->");
126  }
127
128  private void addResourceNote() throws IOException {
129    ln("<!-- ");
130    ln("  This file contains just the constraints for the resource "+description);
131    ln("  It is provided for documentation purposes. When actually validating,");
132    ln("  always use fhir-invariants.sch (because of the way containment works)");
133    ln("  Alternatively you can use this file to build a smaller version of");
134    ln("  fhir-invariants.sch (the contents are identical; only include those ");
135    ln("  resources relevant to your implementation).");
136    ln("-->");
137  }
138
139  private void addProfileNote() throws IOException {
140    ln("<!-- ");
141    ln("  This file contains just the constraints for the profile "+description);
142    ln("  It includes the base constraints for the resource as well.");
143    ln("  Because of the way that schematrons and containment work, ");
144    ln("  you may need to use this schematron fragment to build a, ");
145    ln("  single schematron that validates contained resources (if you have any) ");
146    ln("-->");
147    
148  }
149
150}