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}