001package org.hl7.fhir.instance.terminologies; 002 003/* 004Copyright (c) 2011+, HL7, Inc 005All rights reserved. 006 007Redistribution and use in source and binary forms, with or without modification, 008are 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 019THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028POSSIBILITY OF SUCH DAMAGE. 029 030*/ 031 032import org.apache.commons.io.IOUtils; 033import org.hl7.fhir.exceptions.FHIRFormatError; 034import org.hl7.fhir.instance.formats.IParser; 035import org.hl7.fhir.instance.model.OperationOutcome; 036import org.hl7.fhir.instance.model.Resource; 037import org.hl7.fhir.instance.model.ValueSet; 038import org.hl7.fhir.instance.terminologies.ValueSetExpander; 039import org.hl7.fhir.instance.terminologies.ValueSetExpanderFactory; 040import org.hl7.fhir.instance.terminologies.ValueSetExpanderSimple; 041import org.hl7.fhir.instance.utils.IWorkerContext; 042import org.hl7.fhir.instance.utils.ToolingExtensions; 043import org.hl7.fhir.utilities.Utilities; 044import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 045 046import java.io.File; 047import java.io.FileInputStream; 048import java.io.FileOutputStream; 049import java.io.IOException; 050import java.util.HashMap; 051import java.util.Map; 052 053public class ValueSetExpansionCache implements ValueSetExpanderFactory { 054 055 public class CacheAwareExpander implements ValueSetExpander { 056 057 @Override 058 public ValueSetExpansionOutcome expand(ValueSet source) throws Exception { 059 if (expansions.containsKey(source.getUrl())) 060 return expansions.get(source.getUrl()); 061 ValueSetExpander vse = new ValueSetExpanderSimple(context, ValueSetExpansionCache.this); 062 ValueSetExpansionOutcome vso = vse.expand(source); 063 if (vso.getError() != null) { 064 // well, we'll see if the designated server can expand it, and if it can, we'll cache it locally 065 vso = context.expandVS(source, false); 066 FileOutputStream s = new FileOutputStream(Utilities.path(cacheFolder, makeFile(source.getUrl()))); 067 context.newXmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(s, vso.getValueset()); 068 s.close(); 069 } 070 expansions.put(source.getUrl(), vso); 071 return vso; 072 } 073 074 private String makeFile(String url) { 075 return url.replace("$", "").replace(":", "").replace("//", "/").replace("/", "_")+".xml"; 076 } 077 } 078 079 private static final String VS_ID_EXT = "http://tools/cache"; 080 081 private final Map<String, ValueSetExpander.ValueSetExpansionOutcome> expansions = new HashMap<String, ValueSetExpander.ValueSetExpansionOutcome>(); 082 private final IWorkerContext context; 083 private final String cacheFolder; 084 085 public ValueSetExpansionCache(IWorkerContext context) { 086 super(); 087 cacheFolder = null; 088 this.context = context; 089 } 090 091 public ValueSetExpansionCache(IWorkerContext context, String cacheFolder) throws FHIRFormatError, IOException { 092 super(); 093 this.context = context; 094 this.cacheFolder = cacheFolder; 095 if (this.cacheFolder != null) 096 loadCache(); 097 } 098 099 private void loadCache() throws FHIRFormatError, IOException { 100 File[] files = new File(cacheFolder).listFiles(); 101 for (File f : files) { 102 if (f.getName().endsWith(".xml")) { 103 final FileInputStream is = new FileInputStream(f); 104 try { 105 Resource r = context.newXmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).parse(is); 106 if (r instanceof OperationOutcome) { 107 OperationOutcome oo = (OperationOutcome) r; 108 expansions.put(ToolingExtensions.getExtension(oo,VS_ID_EXT).getValue().toString(), 109 new ValueSetExpander.ValueSetExpansionOutcome(new XhtmlComposer(true, false).composePlainText(oo.getText().getDiv()))); 110 } else { 111 ValueSet vs = (ValueSet) r; 112 expansions.put(vs.getUrl(), new ValueSetExpander.ValueSetExpansionOutcome(vs, null)); 113 } 114 } catch (Exception theE) { 115 throw new FHIRFormatError(theE); 116 } finally { 117 IOUtils.closeQuietly(is); 118 } 119 } 120 } 121 } 122 123 @Override 124 public ValueSetExpander getExpander() { 125 return new CacheAwareExpander(); 126 // return new ValueSetExpander(valuesets, codesystems); 127 } 128 129}