001package org.hl7.fhir.dstu2.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.ByteArrayInputStream; 035import java.io.FileNotFoundException; 036import java.io.IOException; 037import java.io.InputStream; 038import java.net.URISyntaxException; 039import java.util.ArrayList; 040import java.util.Collections; 041import java.util.HashMap; 042import java.util.List; 043import java.util.Map; 044import java.util.zip.ZipEntry; 045import java.util.zip.ZipInputStream; 046 047import org.hl7.fhir.dstu2.formats.IParser; 048import org.hl7.fhir.dstu2.formats.JsonParser; 049import org.hl7.fhir.dstu2.formats.ParserType; 050import org.hl7.fhir.dstu2.formats.XmlParser; 051import org.hl7.fhir.dstu2.model.Bundle; 052import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; 053import org.hl7.fhir.dstu2.model.ConceptMap; 054import org.hl7.fhir.dstu2.model.ElementDefinition.ElementDefinitionBindingComponent; 055import org.hl7.fhir.dstu2.model.Resource; 056import org.hl7.fhir.dstu2.model.StructureDefinition; 057import org.hl7.fhir.dstu2.model.StructureDefinition.StructureDefinitionKind; 058import org.hl7.fhir.dstu2.model.ValueSet; 059import org.hl7.fhir.dstu2.terminologies.ValueSetExpansionCache; 060import org.hl7.fhir.dstu2.utils.ProfileUtilities.ProfileKnowledgeProvider; 061import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient; 062import org.hl7.fhir.dstu2.utils.validation.IResourceValidator; 063import org.hl7.fhir.exceptions.DefinitionException; 064import org.hl7.fhir.exceptions.FHIRException; 065import org.hl7.fhir.utilities.CSFileInputStream; 066import org.hl7.fhir.utilities.Utilities; 067import org.hl7.fhir.utilities.validation.ValidationMessage; 068import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 069 070/* 071 * This is a stand alone implementation of worker context for use inside a tool. 072 * It loads from the validation package (validation-min.xml.zip), and has a 073 * very light cient to connect to an open unauthenticated terminology service 074 */ 075 076public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerContext, ProfileKnowledgeProvider { 077 078 // all maps are to the full URI 079 private Map<String, StructureDefinition> structures = new HashMap<String, StructureDefinition>(); 080 081 // -- Initializations 082 /** 083 * Load the working context from the validation pack 084 * 085 * @param path 086 * filename of the validation pack 087 * @return 088 * @throws IOException 089 * @throws FileNotFoundException 090 * @throws FHIRException 091 * @throws Exception 092 */ 093 public static SimpleWorkerContext fromPack(String path) throws FileNotFoundException, IOException, FHIRException { 094 SimpleWorkerContext res = new SimpleWorkerContext(); 095 res.loadFromPack(path); 096 return res; 097 } 098 099 public static SimpleWorkerContext fromClassPath() throws IOException, FHIRException { 100 SimpleWorkerContext res = new SimpleWorkerContext(); 101 res.loadFromStream(SimpleWorkerContext.class.getResourceAsStream("validation.zip")); 102 return res; 103 } 104 105 public static SimpleWorkerContext fromDefinitions(Map<String, byte[]> source) throws IOException, FHIRException { 106 SimpleWorkerContext res = new SimpleWorkerContext(); 107 for (String name : source.keySet()) { 108 if (name.endsWith(".xml")) { 109 res.loadFromFile(new ByteArrayInputStream(source.get(name)), name); 110 } 111 } 112 return res; 113 } 114 115 public void connectToTSServer(String url, String userAgent) throws URISyntaxException { 116 txServer = new FHIRToolingClient(url, userAgent); 117 } 118 119 private void loadFromFile(InputStream stream, String name) throws IOException, FHIRException { 120 XmlParser xml = new XmlParser(); 121 Bundle f = (Bundle) xml.parse(stream); 122 for (BundleEntryComponent e : f.getEntry()) { 123 124 if (e.getFullUrl() == null) { 125 System.out.println("unidentified resource in " + name+" (no fullUrl)"); 126 } 127 seeResource(e.getFullUrl(), e.getResource()); 128 } 129 } 130 131 public void seeResource(String url, Resource r) throws FHIRException { 132 if (r instanceof StructureDefinition) 133 seeProfile(url, (StructureDefinition) r); 134 else if (r instanceof ValueSet) 135 seeValueSet(url, (ValueSet) r); 136 else if (r instanceof ConceptMap) 137 maps.put(((ConceptMap) r).getUrl(), (ConceptMap) r); 138 } 139 140 private void seeValueSet(String url, ValueSet vs) throws DefinitionException { 141 if (Utilities.noString(url)) 142 url = vs.getUrl(); 143 if (valueSets.containsKey(vs.getUrl())) 144 throw new DefinitionException("Duplicate Profile " + vs.getUrl()); 145 valueSets.put(vs.getId(), vs); 146 valueSets.put(vs.getUrl(), vs); 147 if (!vs.getUrl().equals(url)) 148 valueSets.put(url, vs); 149 if (vs.hasCodeSystem()) { 150 codeSystems.put(vs.getCodeSystem().getSystem().toString(), vs); 151 } 152 } 153 154 private void seeProfile(String url, StructureDefinition p) throws FHIRException { 155 if (Utilities.noString(url)) 156 url = p.getUrl(); 157 if (!p.hasSnapshot()) { 158 if (!p.hasBase()) 159 throw new DefinitionException("Profile "+p.getName()+" ("+p.getUrl()+") has no base and no snapshot"); 160 StructureDefinition sd = fetchResource(StructureDefinition.class, p.getBase()); 161 if (sd == null) 162 throw new DefinitionException("Profile "+p.getName()+" ("+p.getUrl()+") base "+p.getBase()+" could not be resolved"); 163 List<ValidationMessage> msgs = new ArrayList<ValidationMessage>(); 164 ProfileUtilities pu = new ProfileUtilities(this, msgs, this); 165 pu.generateSnapshot(sd, p, p.getUrl(), p.getName()); 166 for (ValidationMessage msg : msgs) { 167 if (msg.getLevel() == IssueSeverity.ERROR || msg.getLevel() == IssueSeverity.FATAL) 168 throw new DefinitionException("Profile "+p.getName()+" ("+p.getUrl()+"). Error generating snapshot: "+msg.getMessage()); 169 } 170 if (!p.hasSnapshot()) 171 throw new DefinitionException("Profile "+p.getName()+" ("+p.getUrl()+"). Error generating snapshot"); 172 pu = null; 173 } 174 if (structures.containsKey(p.getUrl())) 175 throw new DefinitionException("Duplicate structures " + p.getUrl()); 176 structures.put(p.getId(), p); 177 structures.put(p.getUrl(), p); 178 if (!p.getUrl().equals(url)) 179 structures.put(url, p); 180 } 181 182 private void loadFromPack(String path) throws FileNotFoundException, IOException, FHIRException { 183 loadFromStream(new CSFileInputStream(path)); 184 } 185 186 private void loadFromStream(InputStream stream) throws IOException, FHIRException { 187 ZipInputStream zip = new ZipInputStream(stream); 188 ZipEntry ze; 189 while ((ze = zip.getNextEntry()) != null) { 190 if (ze.getName().endsWith(".xml")) { 191 String name = ze.getName(); 192 loadFromFile(zip, name); 193 } 194 zip.closeEntry(); 195 } 196 zip.close(); 197 } 198 199 200 @Override 201 public IParser getParser(ParserType type) { 202 switch (type) { 203 case JSON: return newJsonParser(); 204 case XML: return newXmlParser(); 205 default: 206 throw new Error("Parser Type "+type.toString()+" not supported"); 207 } 208 } 209 210 @Override 211 public IParser getParser(String type) { 212 if (type.equalsIgnoreCase("JSON")) 213 return new JsonParser(); 214 if (type.equalsIgnoreCase("XML")) 215 return new XmlParser(); 216 throw new Error("Parser Type "+type.toString()+" not supported"); 217 } 218 219 @Override 220 public IParser newJsonParser() { 221 return new JsonParser(); 222 } 223 @Override 224 public IParser newXmlParser() { 225 return new XmlParser(); 226 } 227 228 @Override 229 public <T extends Resource> boolean hasResource(Class<T> class_, String uri) { 230 try { 231 return fetchResource(class_, uri) != null; 232 } catch (Exception e) { 233 return false; 234 } 235 } 236 237 @Override 238 public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath) { 239 return new NarrativeGenerator(prefix, basePath, this); 240 } 241 242 @Override 243 public IResourceValidator newValidator() { 244 throw new Error("not supported at this time"); 245 } 246 247 @SuppressWarnings("unchecked") 248 @Override 249 public <T extends Resource> T fetchResource(Class<T> class_, String uri) { 250 if (class_ == StructureDefinition.class && !uri.contains("/")) 251 uri = "http://hl7.org/fhir/StructureDefinition/"+uri; 252 253 if (uri.startsWith("http:")) { 254 if (uri.contains("#")) 255 uri = uri.substring(0, uri.indexOf("#")); 256 if (class_ == StructureDefinition.class) { 257 if (structures.containsKey(uri)) 258 return (T) structures.get(uri); 259 else 260 return null; 261 } else if (class_ == ValueSet.class) { 262 if (valueSets.containsKey(uri)) 263 return (T) valueSets.get(uri); 264 else if (codeSystems.containsKey(uri)) 265 return (T) codeSystems.get(uri); 266 else 267 return null; 268 } 269 } 270 if (class_ == null && uri.contains("/")) { 271 return null; 272 } 273 274 throw new Error("not done yet"); 275 } 276 277 278 279 public int totalCount() { 280 return valueSets.size() + maps.size() + structures.size(); 281 } 282 283 public void setCache(ValueSetExpansionCache cache) { 284 this.expansionCache = cache; 285 } 286 287 @Override 288 public List<String> getResourceNames() { 289 List<String> result = new ArrayList<String>(); 290 for (StructureDefinition sd : structures.values()) { 291 if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.hasConstrainedType()) 292 result.add(sd.getName()); 293 } 294 Collections.sort(result); 295 return result; 296 } 297 298 @Override 299 public String getAbbreviation(String name) { 300 return "xxx"; 301 } 302 303 @Override 304 public boolean isDatatype(String typeSimple) { 305 // TODO Auto-generated method stub 306 return false; 307 } 308 309 @Override 310 public boolean isResource(String t) { 311 StructureDefinition sd; 312 try { 313 sd = fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t); 314 } catch (Exception e) { 315 return false; 316 } 317 if (sd == null) 318 return false; 319 if (sd.hasConstrainedType()) 320 return false; 321 return sd.getKind() == StructureDefinitionKind.RESOURCE; 322 } 323 324 @Override 325 public boolean hasLinkFor(String typeSimple) { 326 return false; 327 } 328 329 @Override 330 public String getLinkFor(String typeSimple) { 331 return null; 332 } 333 334 @Override 335 public BindingResolution resolveBinding(ElementDefinitionBindingComponent binding) { 336 return null; 337 } 338 339 @Override 340 public String getLinkForProfile(StructureDefinition profile, String url) { 341 return null; 342 } 343 344 @Override 345 public List<StructureDefinition> allStructures() { 346 List<StructureDefinition> res = new ArrayList<StructureDefinition>(); 347 res.addAll(structures.values()); 348 return res ; 349 } 350 351 352 353}