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