
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 032import java.io.ByteArrayInputStream; 033import java.io.FileNotFoundException; 034import java.io.IOException; 035import java.io.InputStream; 036import java.net.URISyntaxException; 037import java.util.ArrayList; 038import java.util.Collections; 039import java.util.HashMap; 040import java.util.List; 041import java.util.Map; 042import java.util.zip.ZipEntry; 043import java.util.zip.ZipInputStream; 044 045import org.hl7.fhir.dstu2.formats.IParser; 046import org.hl7.fhir.dstu2.formats.JsonParser; 047import org.hl7.fhir.dstu2.formats.ParserType; 048import org.hl7.fhir.dstu2.formats.XmlParser; 049import org.hl7.fhir.dstu2.model.Bundle; 050import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; 051import org.hl7.fhir.dstu2.model.ConceptMap; 052import org.hl7.fhir.dstu2.model.ElementDefinition.ElementDefinitionBindingComponent; 053import org.hl7.fhir.dstu2.model.Resource; 054import org.hl7.fhir.dstu2.model.StructureDefinition; 055import org.hl7.fhir.dstu2.model.StructureDefinition.StructureDefinitionKind; 056import org.hl7.fhir.dstu2.model.ValueSet; 057import org.hl7.fhir.dstu2.terminologies.ValueSetExpansionCache; 058import org.hl7.fhir.dstu2.utils.ProfileUtilities.ProfileKnowledgeProvider; 059import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient; 060import org.hl7.fhir.dstu2.utils.validation.IResourceValidator; 061import org.hl7.fhir.exceptions.DefinitionException; 062import org.hl7.fhir.exceptions.FHIRException; 063import org.hl7.fhir.utilities.Utilities; 064import org.hl7.fhir.utilities.filesystem.CSFileInputStream; 065import org.hl7.fhir.utilities.validation.ValidationMessage; 066import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 067 068/* 069 * This is a stand alone implementation of worker context for use inside a tool. 070 * It loads from the validation package (validation-min.xml.zip), and has a 071 * very light cient to connect to an open unauthenticated terminology service 072 */ 073 074@Deprecated 075public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerContext, ProfileKnowledgeProvider { 076 077 // all maps are to the full URI 078 private Map<String, StructureDefinition> structures = new HashMap<String, StructureDefinition>(); 079 080 // -- Initializations 081 /** 082 * Load the working context from the validation pack 083 * 084 * @param path filename of the validation pack 085 * @return 086 * @throws IOException 087 * @throws FileNotFoundException 088 * @throws FHIRException 089 * @throws Exception 090 */ 091 public static SimpleWorkerContext fromPack(String path) throws FileNotFoundException, IOException, FHIRException { 092 SimpleWorkerContext res = new SimpleWorkerContext(); 093 res.loadFromPack(path); 094 return res; 095 } 096 097 public static SimpleWorkerContext fromClassPath() throws IOException, FHIRException { 098 SimpleWorkerContext res = new SimpleWorkerContext(); 099 res.loadFromStream(SimpleWorkerContext.class.getResourceAsStream("validation.zip")); 100 return res; 101 } 102 103 public static SimpleWorkerContext fromDefinitions(Map<String, byte[]> source) throws IOException, FHIRException { 104 SimpleWorkerContext res = new SimpleWorkerContext(); 105 for (String name : source.keySet()) { 106 if (name.endsWith(".xml")) { 107 res.loadFromFile(new ByteArrayInputStream(source.get(name)), name); 108 } 109 } 110 return res; 111 } 112 113 public void connectToTSServer(String url, String userAgent) throws URISyntaxException { 114 txServer = new FHIRToolingClient(url, userAgent); 115 } 116 117 private void loadFromFile(InputStream stream, String name) throws IOException, FHIRException { 118 XmlParser xml = new XmlParser(); 119 Bundle f = (Bundle) xml.parse(stream); 120 for (BundleEntryComponent e : f.getEntry()) { 121 122 if (e.getFullUrl() == null) { 123 System.out.println("unidentified resource in " + name + " (no fullUrl)"); 124 } 125 seeResource(e.getFullUrl(), e.getResource()); 126 } 127 } 128 129 public void seeResource(String url, Resource r) throws FHIRException { 130 if (r instanceof StructureDefinition) 131 seeProfile(url, (StructureDefinition) r); 132 else if (r instanceof ValueSet) 133 seeValueSet(url, (ValueSet) r); 134 else if (r instanceof ConceptMap) 135 maps.put(((ConceptMap) r).getUrl(), (ConceptMap) r); 136 } 137 138 private void seeValueSet(String url, ValueSet vs) throws DefinitionException { 139 if (Utilities.noString(url)) 140 url = vs.getUrl(); 141 if (valueSets.containsKey(vs.getUrl())) 142 throw new DefinitionException("Duplicate Profile " + vs.getUrl()); 143 valueSets.put(vs.getId(), vs); 144 valueSets.put(vs.getUrl(), vs); 145 if (!vs.getUrl().equals(url)) 146 valueSets.put(url, vs); 147 if (vs.hasCodeSystem()) { 148 codeSystems.put(vs.getCodeSystem().getSystem().toString(), vs); 149 } 150 } 151 152 private void seeProfile(String url, StructureDefinition p) throws FHIRException { 153 if (Utilities.noString(url)) 154 url = p.getUrl(); 155 if (!p.hasSnapshot()) { 156 if (!p.hasBase()) 157 throw new DefinitionException("Profile " + p.getName() + " (" + p.getUrl() + ") has no base and no snapshot"); 158 StructureDefinition sd = fetchResource(StructureDefinition.class, p.getBase()); 159 if (sd == null) 160 throw new DefinitionException( 161 "Profile " + p.getName() + " (" + p.getUrl() + ") base " + p.getBase() + " could not be resolved"); 162 List<ValidationMessage> msgs = new ArrayList<ValidationMessage>(); 163 ProfileUtilities pu = new ProfileUtilities(this, msgs, this); 164 pu.generateSnapshot(sd, p, p.getUrl(), p.getName()); 165 for (ValidationMessage msg : msgs) { 166 if (msg.getLevel() == IssueSeverity.ERROR || msg.getLevel() == IssueSeverity.FATAL) 167 throw new DefinitionException( 168 "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 @Override 200 public IParser getParser(ParserType type) { 201 switch (type) { 202 case JSON: 203 return newJsonParser(); 204 case XML: 205 return newXmlParser(); 206 default: 207 throw new Error("Parser Type " + type.toString() + " not supported"); 208 } 209 } 210 211 @Override 212 public IParser getParser(String type) { 213 if (type.equalsIgnoreCase("JSON")) 214 return new JsonParser(); 215 if (type.equalsIgnoreCase("XML")) 216 return new XmlParser(); 217 throw new Error("Parser Type " + type.toString() + " not supported"); 218 } 219 220 @Override 221 public IParser newJsonParser() { 222 return new JsonParser(); 223 } 224 225 @Override 226 public IParser newXmlParser() { 227 return new XmlParser(); 228 } 229 230 @Override 231 public <T extends Resource> boolean hasResource(Class<T> class_, String uri) { 232 try { 233 return fetchResource(class_, uri) != null; 234 } catch (Exception e) { 235 return false; 236 } 237 } 238 239 @Override 240 public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath) { 241 return new NarrativeGenerator(prefix, basePath, this); 242 } 243 244 @Override 245 public IResourceValidator newValidator() { 246 throw new Error("not supported at this time"); 247 } 248 249 @SuppressWarnings("unchecked") 250 @Override 251 public <T extends Resource> T fetchResource(Class<T> class_, String uri) { 252 if (class_ == StructureDefinition.class && !uri.contains("/")) 253 uri = "http://hl7.org/fhir/StructureDefinition/" + uri; 254 255 if (uri.startsWith("http:")) { 256 if (uri.contains("#")) 257 uri = uri.substring(0, uri.indexOf("#")); 258 if (class_ == StructureDefinition.class) { 259 if (structures.containsKey(uri)) 260 return (T) structures.get(uri); 261 else 262 return null; 263 } else if (class_ == ValueSet.class) { 264 if (valueSets.containsKey(uri)) 265 return (T) valueSets.get(uri); 266 else if (codeSystems.containsKey(uri)) 267 return (T) codeSystems.get(uri); 268 else 269 return null; 270 } 271 } 272 if (class_ == null && uri.contains("/")) { 273 return null; 274 } 275 276 throw new Error("not done yet"); 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}