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