001package org.hl7.fhir.utilities.xml;
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.OutputStream;
036import java.io.UnsupportedEncodingException;
037import java.util.ArrayList;
038import java.util.List;
039
040import org.hl7.fhir.utilities.TextStreamWriter;
041import org.hl7.fhir.utilities.Utilities;
042
043
044public class SchematronWriter  extends TextStreamWriter  {
045
046  public enum SchematronType {
047    ALL_RESOURCES,
048    RESOURCE,
049    PROFILE
050  }
051
052  public class Assert {
053    private String test;
054    private String message; 
055  }
056  
057  public class Rule {
058    private String name; 
059    private List<Assert> asserts = new ArrayList<Assert>();   
060    public void assrt(String test, String message) {
061      Assert a = new Assert();
062      a.test = test;
063      a.message = message;
064      asserts.add(a);
065    }
066    
067    public boolean isSpecial() {
068      return name.contains("*") || name.contains("[");
069    }
070  }
071  public class Section {
072    private String title;
073    private List<Rule> rules = new ArrayList<Rule>();
074    
075    public String getTitle() {
076      return title;
077    }
078
079    public void setTitle(String title) {
080      this.title = title;
081    }
082
083    public Rule rule(String name) {
084      for (Rule r : rules) {
085        if (r.name.equals(name))
086          return r;
087      }
088      Rule r = new Rule();
089      r.name = name;
090      rules.add(r);
091      return r;
092    }
093
094    public boolean hasRegularContent() {
095      for (Rule r : rules) 
096        if (!r.asserts.isEmpty() && !r.isSpecial())
097          return true;
098      return false;
099    }
100
101    public boolean hasSpecialContent() {
102      for (Rule r : rules) 
103        if (!r.asserts.isEmpty() && r.isSpecial())
104          return true;
105      return false;
106    }
107    
108    public List<Rule> getRegularRules() {
109      List<Rule> regular = new ArrayList<Rule>();
110      for (Rule r : rules) 
111        if (!r.asserts.isEmpty() && !r.isSpecial())
112          regular.add(r);
113      return regular;
114    }
115
116    public List<Rule> getSpecialRules() {
117      List<Rule> regular = new ArrayList<Rule>();
118      for (Rule r : rules) 
119        if (!r.asserts.isEmpty() && r.isSpecial())
120          regular.add(r);
121      return regular;
122    }
123}
124
125  private SchematronType type;
126  private String description;
127  private List<Section> sections = new ArrayList<Section>();
128
129
130  public SchematronWriter(OutputStream out, SchematronType type, String description) throws UnsupportedEncodingException {
131    super(out);
132    this.type = type;
133    this.description = description;
134  }
135
136  public Section section(String title) {
137    for (Section s : sections) {
138      if (s.title.equals(title))
139        return s;
140    }
141    Section s = new Section();
142    s.title = title;
143    sections.add(s);
144    return s;
145  }
146  
147  public void dump() throws IOException {
148    ln("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
149    ln_i("<sch:schema xmlns:sch=\"http://purl.oclc.org/dsdl/schematron\" queryBinding=\"xslt2\">");
150    ln("<sch:ns prefix=\"f\" uri=\"http://hl7.org/fhir\"/>");
151    ln("<sch:ns prefix=\"h\" uri=\"http://www.w3.org/1999/xhtml\"/>");
152    addNote();
153
154    for (Section s : sections) {
155      if (s.hasRegularContent()) {
156        ln_i("<sch:pattern>");
157        ln("<sch:title>"+Utilities.escapeXml(s.title)+"</sch:title>");
158        for (Rule r : s.getRegularRules()) {
159          if (!r.asserts.isEmpty()) {
160            ln_i("<sch:rule context=\""+Utilities.escapeXml(r.name)+"\">");
161            for (Assert a : r.asserts) 
162              ln("<sch:assert test=\""+Utilities.escapeXml(a.test)+"\">"+Utilities.escapeXml(a.message)+"</sch:assert>");
163            ln_o("</sch:rule>");
164          }
165        }
166        ln_o("</sch:pattern>");
167      }
168      if (s.hasSpecialContent()) {
169        int i = 1;
170        for (Rule r : s.getSpecialRules()) {
171          ln_i("<sch:pattern>");
172          ln("<sch:title>"+Utilities.escapeXml(s.title)+" "+i+"</sch:title>");
173          i++;
174          if (!r.asserts.isEmpty()) {
175            ln_i("<sch:rule context=\""+Utilities.escapeXml(r.name)+"\">");
176            for (Assert a : r.asserts) 
177              ln("<sch:assert test=\""+Utilities.escapeXml(a.test)+"\">"+Utilities.escapeXml(a.message)+"</sch:assert>");
178            ln_o("</sch:rule>");
179          }
180          ln_o("</sch:pattern>");
181        }
182      }
183    }  
184    ln_o("</sch:schema>");
185    flush();
186    close();
187  }
188
189  private void addNote() throws IOException {
190    switch (type) {
191    case ALL_RESOURCES : addAllResourcesNote(); break;
192    case RESOURCE : addResourceNote(); break;
193    case PROFILE : addProfileNote(); break;
194    }
195  }
196
197  private void addAllResourcesNote() throws IOException {
198    ln("<!-- ");
199    ln("  This file contains constraints for all resources");
200    ln("  Because of the way containment works, this file should always )");
201    ln("  be used for validating resources. Alternatively you can use ");
202    ln("  the resource specific files to build a smaller version of");
203    ln("  this file (the contents are identical; only include those ");
204    ln("  resources relevant to your implementation).");
205    ln("-->");
206  }
207
208  private void addResourceNote() throws IOException {
209    ln("<!-- ");
210    ln("  This file contains just the constraints for the resource "+description);
211    ln("  It is provided for documentation purposes. When actually validating,");
212    ln("  always use fhir-invariants.sch (because of the way containment works)");
213    ln("  Alternatively you can use this file to build a smaller version of");
214    ln("  fhir-invariants.sch (the contents are identical; only include those ");
215    ln("  resources relevant to your implementation).");
216    ln("-->");
217  }
218
219  private void addProfileNote() throws IOException {
220    ln("<!-- ");
221    ln("  This file contains just the constraints for the profile "+description);
222    ln("  It includes the base constraints for the resource as well.");
223    ln("  Because of the way that schematrons and containment work, ");
224    ln("  you may need to use this schematron fragment to build a, ");
225    ln("  single schematron that validates contained resources (if you have any) ");
226    ln("-->");
227    
228  }
229
230}