Merge pull request #817 from hapifhir/gg-202205-performance

Performance work in the validator
This commit is contained in:
Grahame Grieve 2022-05-25 06:35:33 +10:00 committed by GitHub
commit ce48c435fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 640 additions and 186 deletions

View File

@ -341,6 +341,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private XVerExtensionManager xver; private XVerExtensionManager xver;
private boolean wantFixDifferentialFirstElementType; private boolean wantFixDifferentialFirstElementType;
private Set<String> masterSourceFileNames; private Set<String> masterSourceFileNames;
private Map<ElementDefinition, List<ElementDefinition>> childMapCache = new HashMap<>();
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) { public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) {
super(); super();
@ -413,6 +414,9 @@ public class ProfileUtilities extends TranslatingUtilities {
public List<ElementDefinition> getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { public List<ElementDefinition> getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException {
if (childMapCache .containsKey(element)) {
return childMapCache.get(element);
}
if (element.getContentReference() != null) { if (element.getContentReference() != null) {
List<ElementDefinition> list = null; List<ElementDefinition> list = null;
String id = null; String id = null;
@ -452,6 +456,7 @@ public class ProfileUtilities extends TranslatingUtilities {
} else } else
break; break;
} }
childMapCache.put(element, res);
return res; return res;
} }
} }

View File

@ -54,6 +54,7 @@ import org.hl7.fhir.exceptions.NoTerminologyServiceException;
import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
import org.hl7.fhir.r5.context.IWorkerContext.IPackageLoadingTracker;
import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory; import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory;
import org.hl7.fhir.r5.context.TerminologyCache.CacheToken; import org.hl7.fhir.r5.context.TerminologyCache.CacheToken;
import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.BooleanType;
@ -216,6 +217,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private final CanonicalResourceManager<OperationDefinition> operations = new CanonicalResourceManager<OperationDefinition>(false); private final CanonicalResourceManager<OperationDefinition> operations = new CanonicalResourceManager<OperationDefinition>(false);
private final CanonicalResourceManager<PlanDefinition> plans = new CanonicalResourceManager<PlanDefinition>(false); private final CanonicalResourceManager<PlanDefinition> plans = new CanonicalResourceManager<PlanDefinition>(false);
private final CanonicalResourceManager<NamingSystem> systems = new CanonicalResourceManager<NamingSystem>(false); private final CanonicalResourceManager<NamingSystem> systems = new CanonicalResourceManager<NamingSystem>(false);
private Map<String, NamingSystem> systemUrlMap;
private UcumService ucumService; private UcumService ucumService;
protected Map<String, byte[]> binaries = new HashMap<String, byte[]>(); protected Map<String, byte[]> binaries = new HashMap<String, byte[]>();
@ -280,6 +283,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
questionnaires.copy(other.questionnaires); questionnaires.copy(other.questionnaires);
operations.copy(other.operations); operations.copy(other.operations);
systems.copy(other.systems); systems.copy(other.systems);
systemUrlMap = null;
guides.copy(other.guides); guides.copy(other.guides);
capstmts.copy(other.capstmts); capstmts.copy(other.capstmts);
measures.copy(other.measures); measures.copy(other.measures);
@ -443,11 +447,28 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
transforms.see((StructureMap) m, packageInfo); transforms.see((StructureMap) m, packageInfo);
} else if (r instanceof NamingSystem) { } else if (r instanceof NamingSystem) {
systems.see((NamingSystem) m, packageInfo); systems.see((NamingSystem) m, packageInfo);
systemUrlMap = null;
} }
} }
} }
} }
public Map<String, NamingSystem> getNSUrlMap() {
if (systemUrlMap == null) {
systemUrlMap = new HashMap<>();
List<NamingSystem> nsl = new ArrayList<>();
for (NamingSystem ns : nsl) {
for (NamingSystemUniqueIdComponent uid : ns.getUniqueId()) {
if (uid.getType() == NamingSystemIdentifierType.URI && uid.hasValue()) {
systemUrlMap.put(uid.getValue(), ns) ;
}
}
}
}
return systemUrlMap;
}
public void fixOldSD(StructureDefinition sd) { public void fixOldSD(StructureDefinition sd) {
if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && sd.getType().equals("Extension") && sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) { if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && sd.getType().equals("Extension") && sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) {
sd.setSnapshot(null); sd.setSnapshot(null);
@ -1661,6 +1682,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private Set<String> notCanonical = new HashSet<String>(); private Set<String> notCanonical = new HashSet<String>();
private String overrideVersionNs; private String overrideVersionNs;
protected IPackageLoadingTracker packageTracker;
@Override @Override
public Resource fetchResourceById(String type, String uri) { public Resource fetchResourceById(String type, String uri) {
@ -1832,6 +1854,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
transforms.drop(id); transforms.drop(id);
} else if (fhirType.equals("NamingSystem")) { } else if (fhirType.equals("NamingSystem")) {
systems.drop(id); systems.drop(id);
systemUrlMap = null;
} }
} }
} }
@ -2269,4 +2292,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return false; return false;
} }
public IPackageLoadingTracker getPackageTracker() {
return packageTracker;
}
public IWorkerContext setPackageTracker(IPackageLoadingTracker packageTracker) {
this.packageTracker = packageTracker;
return this;
}
} }

View File

@ -57,6 +57,7 @@ import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ConceptMap; import org.hl7.fhir.r5.model.ConceptMap;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.NamingSystem;
import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
@ -101,6 +102,10 @@ import javax.annotation.Nonnull;
*/ */
public interface IWorkerContext { public interface IWorkerContext {
public interface IPackageLoadingTracker {
public void packageLoaded(String pid, String version);
}
public class CodingValidationRequest { public class CodingValidationRequest {
private Coding coding; private Coding coding;
private ValidationResult result; private ValidationResult result;
@ -790,6 +795,7 @@ public interface IWorkerContext {
* @return * @return
*/ */
public String oid2Uri(String code); public String oid2Uri(String code);
public Map<String, NamingSystem> getNSUrlMap();
/** /**
* @return true if the contxt has a terminology caching service internally * @return true if the contxt has a terminology caching service internally
@ -877,6 +883,8 @@ public interface IWorkerContext {
public IWorkerContext setClientRetryCount(int value); public IWorkerContext setClientRetryCount(int value);
public TimeTracker clock(); public TimeTracker clock();
public IPackageLoadingTracker getPackageTracker();
public IWorkerContext setPackageTracker(IPackageLoadingTracker packageTracker);
public PackageVersion getPackageForUrl(String url); public PackageVersion getPackageForUrl(String url);
} }

View File

@ -469,7 +469,9 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return 0; return 0;
} }
loadedPackages.add(pi.id()+"#"+pi.version()); loadedPackages.add(pi.id()+"#"+pi.version());
if (packageTracker != null) {
packageTracker.packageLoaded(pi.id(), pi.version());
}
if ((types == null || types.length == 0) && loader != null) { if ((types == null || types.length == 0) && loader != null) {
types = loader.getTypes(); types = loader.getTypes();

View File

@ -117,6 +117,9 @@ public class Element extends Base {
private List<ValidationMessage> messages; private List<ValidationMessage> messages;
private boolean prohibited; private boolean prohibited;
private boolean required; private boolean required;
private Map<String, List<Element>> childMap;
private int descendentCount;
private int instanceId;
public Element(String name) { public Element(String name) {
super(); super();
@ -210,11 +213,19 @@ public class Element extends Base {
public List<Element> getChildrenByName(String name) { public List<Element> getChildrenByName(String name) {
List<Element> res = new ArrayList<Element>(); List<Element> res = new ArrayList<Element>();
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l != null) {
res.addAll(l);
}
} else {
if (hasChildren()) { if (hasChildren()) {
for (Element child : children) for (Element child : children)
if (name.equals(child.getName())) if (name.equals(child.getName()))
res.add(child); res.add(child);
} }
}
return res; return res;
} }
@ -272,6 +283,7 @@ public class Element extends Base {
child.setValue(value); child.setValue(value);
} }
} }
childMap = null;
try { try {
setProperty(name.hashCode(), name, new StringType(value)); setProperty(name.hashCode(), name, new StringType(value));
} catch (FHIRException e) { } catch (FHIRException e) {
@ -281,10 +293,18 @@ public class Element extends Base {
public List<Element> getChildren(String name) { public List<Element> getChildren(String name) {
List<Element> res = new ArrayList<Element>(); List<Element> res = new ArrayList<Element>();
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l != null) {
res.addAll(l);
}
} else {
if (children != null) if (children != null)
for (Element child : children) { for (Element child : children) {
if (name.equals(child.getName())) if (name.equals(child.getName()))
res.add(child); res.add(child);
}
} }
return res; return res;
} }
@ -313,12 +333,22 @@ public class Element extends Base {
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (children != null) { if (children != null) {
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l != null) {
result.addAll(l);
}
} else {
for (Element child : children) { for (Element child : children) {
if (child.getName().equals(name)) if (child.getName().equals(name)) {
result.add(child); result.add(child);
if (child.getName().startsWith(name) && child.getProperty().isChoice() && child.getProperty().getName().equals(name+"[x]")) }
if (child.getName().startsWith(name) && child.getProperty().isChoice() && child.getProperty().getName().equals(name+"[x]")) {
result.add(child); result.add(child);
} }
}
}
} }
if (result.isEmpty() && checkValid) { if (result.isEmpty() && checkValid) {
// throw new FHIRException("not determined yet"); // throw new FHIRException("not determined yet");
@ -326,6 +356,24 @@ public class Element extends Base {
return result.toArray(new Base[result.size()]); return result.toArray(new Base[result.size()]);
} }
private void populateChildMap() {
if (childMap == null) {
childMap = new HashMap<>();
for (Element child : children) {
String n = child.getName();
if (n.endsWith("[x]")) {
n = n.substring(0, n.length()-3);
}
List<Element> l = childMap.get(n);
if (l == null) {
l = new ArrayList<Element>();
childMap.put(n,l);
}
l.add(child);
}
}
}
@Override @Override
protected void listChildren(List<org.hl7.fhir.r5.model.Property> childProps) { protected void listChildren(List<org.hl7.fhir.r5.model.Property> childProps) {
if (children != null) { if (children != null) {
@ -362,6 +410,7 @@ public class Element extends Base {
throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type"); throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type");
} }
childMap = null;
if (children == null) if (children == null)
children = new ArrayList<Element>(); children = new ArrayList<Element>();
Element childForValue = null; Element childForValue = null;
@ -532,9 +581,11 @@ public class Element extends Base {
public void clearDecorations() { public void clearDecorations() {
clearUserData("fhir.decorations"); clearUserData("fhir.decorations");
for (Element e : children) for (Element e : children) {
e.clearDecorations(); e.clearDecorations();
} }
childMap = null;
}
public void markValidation(StructureDefinition profile, ElementDefinition definition) { public void markValidation(StructureDefinition profile, ElementDefinition definition) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -557,7 +608,21 @@ public class Element extends Base {
public Element getNamedChild(String name) { public Element getNamedChild(String name) {
if (children == null) if (children == null)
return null; return null;
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l == null) {
// try the other way (in case of complicated naming rules)
} else if (l.size() > 1) {
throw new Error("Attempt to read a single element when there is more than one present ("+name+")");
} else {
return l.get(0);
}
} else {
}
Element result = null; Element result = null;
for (Element child : children) { for (Element child : children) {
if (child.getName() != null && name != null && child.getProperty() != null && child.getProperty().getDefinition() != null && child.fhirType() != null) { if (child.getName() != null && name != null && child.getProperty() != null && child.getProperty().getDefinition() != null && child.fhirType() != null) {
if (child.getName().equals(name) || (child.getName().length() > child.fhirType().length() && child.getName().substring(0, child.getName().length() - child.fhirType().length()).equals(name) && child.getProperty().getDefinition().isChoice())) { if (child.getName().equals(name) || (child.getName().length() > child.fhirType().length() && child.getName().substring(0, child.getName().length() - child.fhirType().length()).equals(name) && child.getProperty().getDefinition().isChoice())) {
@ -573,10 +638,18 @@ public class Element extends Base {
public void getNamedChildren(String name, List<Element> list) { public void getNamedChildren(String name, List<Element> list) {
if (children != null) if (children != null)
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l != null) {
list.addAll(l);
}
} else {
for (Element child : children) for (Element child : children)
if (child.getName().equals(name)) if (child.getName().equals(name))
list.add(child); list.add(child);
} }
}
public String getNamedChildValue(String name) { public String getNamedChildValue(String name) {
Element child = getNamedChild(name); Element child = getNamedChild(name);
@ -761,6 +834,7 @@ public class Element extends Base {
} }
children.removeAll(remove); children.removeAll(remove);
Collections.sort(children, new ElementSortComparator(this, this.property)); Collections.sort(children, new ElementSortComparator(this, this.property));
childMap = null;
} }
} }
@ -965,7 +1039,8 @@ public class Element extends Base {
public void clear() { public void clear() {
comments = null; comments = null;
children.clear();; children.clear();
childMap = null;
property = null; property = null;
elementProperty = null; elementProperty = null;
xhtml = null; xhtml = null;
@ -997,6 +1072,7 @@ public class Element extends Base {
public void removeChild(String name) { public void removeChild(String name) {
children.removeIf(n -> name.equals(n.getName())); children.removeIf(n -> name.equals(n.getName()));
childMap = null;
} }
public boolean isProhibited() { public boolean isProhibited() {
@ -1015,5 +1091,33 @@ public class Element extends Base {
this.required = required; this.required = required;
} }
public int getDescendentCount() {
return descendentCount;
}
public void setDescendentCount(int descendentCount) {
this.descendentCount = descendentCount;
}
public int countDescendents() {
if (descendentCount > 0) {
return descendentCount;
} else {
descendentCount = children.size();
for (Element e : children) {
descendentCount = descendentCount + e.countDescendents();
}
}
return descendentCount;
}
public int getInstanceId() {
return instanceId;
}
public void setInstanceId(int instanceId) {
this.instanceId = instanceId;
}
} }

View File

@ -0,0 +1,90 @@
package org.hl7.fhir.utilities;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class SimpleTimeTracker {
private long start;
private String name;
private String context;
private int count;
public static int level = 0;
public static BufferedWriter bw;
public SimpleTimeTracker(String name) {
level++;
this.name = name;
count = -1;
start = System.currentTimeMillis();
init();
log(name);
}
public SimpleTimeTracker(String name, String context) {
level++;
this.name = name;
this.context = context;
start = System.currentTimeMillis();
count = -1;
init();
log(name+" ["+context+"]");
}
private void init() {
if (bw == null) {
try {
File fout = new File("/Users/grahamegrieve/temp/time.txt");
FileOutputStream fos = new FileOutputStream(fout);
bw = new BufferedWriter(new OutputStreamWriter(fos));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void report() {
String s = count == -1 ? "" : " ("+count+")";
if (context == null) {
s = name+": "+(System.currentTimeMillis()-start)+"ms "+s;
} else {
s = name+": "+(System.currentTimeMillis()-start)+"ms "+s+" ["+context+"]";
}
log(s);
level--;
if (level == 0) {
try {
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void log(String s) {
System.out.println(Utilities.padLeft("", '#', level)+" "+s);
try {
bw.write(s);
bw.newLine();
} catch (Exception e) {
System.out.println("e: " +e.getMessage());
}
}
public void count() {
if (count == -1) {
count = 1;
} else {
count++;
}
}
}

View File

@ -1731,4 +1731,12 @@ public class Utilities {
return splitTimezone(value)[0].replace("T", "").replace(":", "").replace(".", "").length(); return splitTimezone(value)[0].replace("T", "").replace(":", "").replace(".", "").length();
} }
public static String padInt(int i, int len) {
return Utilities.padLeft(Integer.toString(i), ' ', len);
}
public static String padInt(long i, int len) {
return Utilities.padLeft(Long.toString(i), ' ', len);
}
} }

View File

@ -15,6 +15,7 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
public abstract class BasePackageCacheManager implements IPackageCacheManager { public abstract class BasePackageCacheManager implements IPackageCacheManager {
private static final Logger ourLog = LoggerFactory.getLogger(BasePackageCacheManager.class); private static final Logger ourLog = LoggerFactory.getLogger(BasePackageCacheManager.class);
private List<String> myPackageServers = new ArrayList<>(); private List<String> myPackageServers = new ArrayList<>();
private Function<String, PackageClient> myClientFactory = address -> new CachingPackageClient(address); private Function<String, PackageClient> myClientFactory = address -> new CachingPackageClient(address);

View File

@ -83,6 +83,8 @@ import java.util.Map.Entry;
*/ */
public class FilesystemPackageCacheManager extends BasePackageCacheManager implements IPackageCacheManager { public class FilesystemPackageCacheManager extends BasePackageCacheManager implements IPackageCacheManager {
// private static final String SECONDARY_SERVER = "http://local.fhir.org:8080/packages"; // private static final String SECONDARY_SERVER = "http://local.fhir.org:8080/packages";
public static final String PACKAGE_REGEX = "^[a-zA-Z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+$"; public static final String PACKAGE_REGEX = "^[a-zA-Z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+$";
public static final String PACKAGE_VERSION_REGEX = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+\\#[A-Za-z0-9\\-\\_\\$]+(\\.[A-Za-z0-9\\-\\_\\$]+)*$"; public static final String PACKAGE_VERSION_REGEX = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+\\#[A-Za-z0-9\\-\\_\\$]+(\\.[A-Za-z0-9\\-\\_\\$]+)*$";

View File

@ -802,15 +802,33 @@ public class BaseValidator implements IValidationContextResourceLoader {
return null; return null;
} }
protected Element resolveInBundle(List<Element> entries, String ref, String fullUrl, String type, String id) { protected Element resolveInBundle(Element bundle, List<Element> entries, String ref, String fullUrl, String type, String id) {
if (Utilities.isAbsoluteUrl(ref)) { @SuppressWarnings("unchecked")
// if the reference is absolute, then you resolve by fullUrl. No other thinking is required. Map<String, Element> map = (Map<String, Element>) bundle.getUserData("validator.entrymap");
if (map == null) {
map = new HashMap<>();
bundle.setUserData("validator.entrymap", map);
for (Element entry : entries) { for (Element entry : entries) {
String fu = entry.getNamedChildValue(FULL_URL); String fu = entry.getNamedChildValue(FULL_URL);
if (ref.equals(fu)) map.put(fu, entry);
return entry; Element resource = entry.getNamedChild(RESOURCE);
if (resource != null) {
String et = resource.getType();
String eid = resource.getNamedChildValue(ID);
map.put(et+"/"+eid, entry);
} }
return null; }
}
if (Utilities.isAbsoluteUrl(ref)) {
// if the reference is absolute, then you resolve by fullUrl. No other thinking is required.
return map.get(ref);
// for (Element entry : entries) {
// String fu = entry.getNamedChildValue(FULL_URL);
// if (ref.equals(fu))
// return entry;
// }
// return null;
} else { } else {
// split into base, type, and id // split into base, type, and id
String u = null; String u = null;
@ -822,20 +840,25 @@ public class BaseValidator implements IValidationContextResourceLoader {
if (parts.length >= 2) { if (parts.length >= 2) {
String t = parts[0]; String t = parts[0];
String i = parts[1]; String i = parts[1];
for (Element entry : entries) { Element res = map.get(u);
String fu = entry.getNamedChildValue(FULL_URL); if (res == null) {
if (fu != null && fu.equals(u)) res = map.get(t+"/"+i);
return entry;
if (u == null) {
Element resource = entry.getNamedChild(RESOURCE);
if (resource != null) {
String et = resource.getType();
String eid = resource.getNamedChildValue(ID);
if (t.equals(et) && i.equals(eid))
return entry;
}
}
} }
return res;
// for (Element entry : entries) {
// String fu = entry.getNamedChildValue(FULL_URL);
// if (fu != null && fu.equals(u))
// return entry;
// if (u == null) {
// Element resource = entry.getNamedChild(RESOURCE);
// if (resource != null) {
// String et = resource.getType();
// String eid = resource.getNamedChildValue(ID);
// if (t.equals(et) && i.equals(eid))
// return entry;
// }
// }
// }
} }
return null; return null;
} }

View File

@ -37,6 +37,7 @@ import org.w3c.dom.Document;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -47,6 +48,7 @@ public class IgLoader {
private static final String[] IGNORED_EXTENSIONS = {"md", "css", "js", "png", "gif", "jpg", "html", "tgz", "pack", "zip"}; private static final String[] IGNORED_EXTENSIONS = {"md", "css", "js", "png", "gif", "jpg", "html", "tgz", "pack", "zip"};
private static final String[] EXEMPT_FILES = {"spec.internals", "version.info", "schematron.zip", "package.json"}; private static final String[] EXEMPT_FILES = {"spec.internals", "version.info", "schematron.zip", "package.json"};
private static final int SCAN_HEADER_SIZE = 2048;
@Getter private final FilesystemPackageCacheManager packageCacheManager; @Getter private final FilesystemPackageCacheManager packageCacheManager;
@Getter private final SimpleWorkerContext context; @Getter private final SimpleWorkerContext context;
@ -254,8 +256,15 @@ public class IgLoader {
boolean recursive, boolean recursive,
VersionSourceInformation versions) throws Exception { VersionSourceInformation versions) throws Exception {
Map<String, byte[]> source = loadIgSourceForVersion(src, recursive, true, versions); Map<String, byte[]> source = loadIgSourceForVersion(src, recursive, true, versions);
if (source != null && source.containsKey("version.info")) if (source != null) {
if (source.containsKey("version.info")) {
versions.see(readInfoVersion(source.get("version.info")), "version.info in " + src); versions.see(readInfoVersion(source.get("version.info")), "version.info in " + src);
} else if (source.size() == 1) {
for (byte[] v : source.values()) {
scanForFhirVersion(versions, src, v);
}
}
}
} }
public void scanForVersions(List<String> sources, VersionSourceInformation versions) throws FHIRException, IOException { public void scanForVersions(List<String> sources, VersionSourceInformation versions) throws FHIRException, IOException {
@ -263,36 +272,71 @@ public class IgLoader {
ValidatorUtils.parseSources(sources, refs, context); ValidatorUtils.parseSources(sources, refs, context);
for (String ref : refs) { for (String ref : refs) {
Content cnt = loadContent(ref, "validate", false); Content cnt = loadContent(ref, "validate", false);
String s = TextFile.bytesToString(cnt.focus); scanForFhirVersion(versions, ref, cnt.focus);
if (s.contains("http://hl7.org/fhir/3.0")) {
versions.see("3.0", "Profile in " + ref);
} }
if (s.contains("http://hl7.org/fhir/1.0")) {
versions.see("1.0", "Profile in " + ref);
}
if (s.contains("http://hl7.org/fhir/4.0")) {
versions.see("4.0", "Profile in " + ref);
}
if (s.contains("http://hl7.org/fhir/1.4")) {
versions.see("1.4", "Profile in " + ref);
} }
private void scanForFhirVersion(VersionSourceInformation versions, String ref, byte[] cnt) throws IOException {
String s = TextFile.bytesToString(cnt.length > SCAN_HEADER_SIZE ? Arrays.copyOfRange(cnt, 0, SCAN_HEADER_SIZE) : cnt).trim();
try { try {
if (s.startsWith("{")) { int i = s.indexOf("fhirVersion");
JsonObject json = JsonTrackingParser.parse(s, null); if (i > 1) {
if (json.has("fhirVersion")) { boolean xml = s.charAt(i) == '<';
versions.see(VersionUtilities.getMajMin(JSONUtil.str(json, "fhirVersion")), "fhirVersion in " + ref); i = find(s, i, '"');
if (!xml) {
i = find(s, i+1, '"');
} }
} else { if (i > 0) {
Document doc = ValidatorUtils.parseXml(cnt.focus); int j = find(s, i+1, '"');
String v = XMLUtil.getNamedChildValue(doc.getDocumentElement(), "fhirVersion"); if (j > 0) {
if (v != null) { String v = s.substring(i+1, j);
if (VersionUtilities.isSemVer(v)) {
versions.see(VersionUtilities.getMajMin(v), "fhirVersion in " + ref); versions.see(VersionUtilities.getMajMin(v), "fhirVersion in " + ref);
return;
}
}
}
i = find(s, i, '\'');
if (!xml) {
i = find(s, i+1, '\'');
}
if (i > 0) {
int j = find(s, i+1, '\'');
if (j > 0) {
String v = s.substring(i, j);
if (VersionUtilities.isSemVer(v)) {
versions.see(VersionUtilities.getMajMin(v), "fhirVersion in " + ref);
return;
}
}
} }
} }
} catch (Exception e) { } catch (Exception e) {
// nothing // nothing
} }
if (s.contains("http://hl7.org/fhir/3.0")) {
versions.see("3.0", "Profile in " + ref);
return;
} }
if (s.contains("http://hl7.org/fhir/1.0")) {
versions.see("1.0", "Profile in " + ref);
return;
}
if (s.contains("http://hl7.org/fhir/4.0")) {
versions.see("4.0", "Profile in " + ref);
return;
}
if (s.contains("http://hl7.org/fhir/1.4")) {
versions.see("1.4", "Profile in " + ref);
return;
}
}
private int find(String s, int i, char c) {
while (i < s.length() && s.charAt(i) != c) {
i++;
}
return i == s.length() ? -1 : i;
} }
protected Map<String, byte[]> readZip(InputStream stream) throws IOException { protected Map<String, byte[]> readZip(InputStream stream) throws IOException {

View File

@ -33,9 +33,9 @@ public class ResourceChecker {
// return checkIsResource(context, debug, TextFile.fileToBytes(path), path); // return checkIsResource(context, debug, TextFile.fileToBytes(path), path);
// } // }
public static Manager.FhirFormat checkIsResource(SimpleWorkerContext context, boolean debug, byte[] cnt, String filename, boolean guessFromExtension) { public static Manager.FhirFormat checkIsResource(SimpleWorkerContext context, boolean debug, byte[] cnt, String filename, boolean guessFromExtension) {
System.out.println(" ..Detect format for " + filename); // System.out.println(" ..Detect format for " + filename);
if (cnt.length == 0) { if (cnt.length == 0) {
System.out.println(" " + filename+" is empty"); System.out.println("Loader: " + filename+" is empty");
return null; return null;
} }
if (guessFromExtension) { if (guessFromExtension) {
@ -53,6 +53,9 @@ public class ResourceChecker {
return Manager.FhirFormat.SHC; return Manager.FhirFormat.SHC;
} }
if (Utilities.existsInList(ext, "json")) { if (Utilities.existsInList(ext, "json")) {
if (cnt.length > 2048) {
return FhirFormat.JSON;
}
// no, we have to look inside, and decide. // no, we have to look inside, and decide.
try { try {
JsonObject json = JsonTrackingParser.parseJson(cnt); JsonObject json = JsonTrackingParser.parseJson(cnt);

View File

@ -14,6 +14,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.ICanonicalResourceLocator; import org.hl7.fhir.r5.context.IWorkerContext.ICanonicalResourceLocator;
import org.hl7.fhir.r5.context.IWorkerContext.IPackageLoadingTracker;
import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion; import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion;
import org.hl7.fhir.r5.context.SimpleWorkerContext; import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.context.SystemOutLoggingService; import org.hl7.fhir.r5.context.SystemOutLoggingService;
@ -133,7 +134,7 @@ POSSIBILITY OF SUCH DAMAGE.
* @author Grahame Grieve * @author Grahame Grieve
*/ */
@Accessors(chain = true) @Accessors(chain = true)
public class ValidationEngine implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IPackageInstaller { public class ValidationEngine implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IPackageInstaller, IPackageLoadingTracker {
@Getter @Setter private SimpleWorkerContext context; @Getter @Setter private SimpleWorkerContext context;
@Getter @Setter private Map<String, byte[]> binaries = new HashMap<>(); @Getter @Setter private Map<String, byte[]> binaries = new HashMap<>();
@ -185,6 +186,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
* the validation framework in their own implementation context * the validation framework in their own implementation context
*/ */
@Getter @Setter private Map<String, ValidationControl> validationControl = new HashMap<>(); @Getter @Setter private Map<String, ValidationControl> validationControl = new HashMap<>();
private Map<String, Boolean> resolvedUrls = new HashMap<>();
private ValidationEngine() { private ValidationEngine() {
@ -259,7 +261,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
ValidationEngine engine = new ValidationEngine(); ValidationEngine engine = new ValidationEngine();
engine.loadCoreDefinitions(src, false, terminologyCachePath, userAgent, timeTracker, loggingService); engine.loadCoreDefinitions(src, false, terminologyCachePath, userAgent, timeTracker, loggingService);
engine.getContext().setCanRunWithoutTerminology(canRunWithoutTerminologyServer); engine.getContext().setCanRunWithoutTerminology(canRunWithoutTerminologyServer);
engine.getContext().setPackageTracker(engine);
if (txServer != null) { if (txServer != null) {
engine.setTerminologyServer(txServer, txLog, txVersion); engine.setTerminologyServer(txServer, txLog, txVersion);
} }
@ -429,7 +431,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
for (String ref : refs) { for (String ref : refs) {
TimeTracker.Session tts = context.clock().start("validation"); TimeTracker.Session tts = context.clock().start("validation");
context.clock().milestone(); context.clock().milestone();
System.out.print(" Validate " + ref); System.out.println(" Validate " + ref);
Content cnt = igLoader.loadContent(ref, "validate", false); Content cnt = igLoader.loadContent(ref, "validate", false);
try { try {
OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles, record); OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles, record);
@ -863,46 +865,49 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
@Override @Override
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws FHIRException { public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws FHIRException {
// some of this logic might take a while, and it's not going to change once loaded
if (resolvedUrls .containsKey(type+"|"+url)) {
return resolvedUrls.get(type+"|"+url);
}
if (!url.startsWith("http://") && !url.startsWith("https://")) { // ignore these if (!url.startsWith("http://") && !url.startsWith("https://")) { // ignore these
resolvedUrls.put(type+"|"+url, true);
return true; return true;
} }
if (context.fetchResource(Resource.class, url) != null) if (context.fetchResource(Resource.class, url) != null) {
resolvedUrls.put(type+"|"+url, true);
return true; return true;
}
if (SIDUtilities.isKnownSID(url) || if (SIDUtilities.isKnownSID(url) ||
Utilities.existsInList(url, "http://hl7.org/fhir/w5", "http://hl7.org/fhir/fivews", "http://hl7.org/fhir/workflow", "http://hl7.org/fhir/ConsentPolicy/opt-out", "http://hl7.org/fhir/ConsentPolicy/opt-in")) { Utilities.existsInList(url, "http://hl7.org/fhir/w5", "http://hl7.org/fhir/fivews", "http://hl7.org/fhir/workflow", "http://hl7.org/fhir/ConsentPolicy/opt-out", "http://hl7.org/fhir/ConsentPolicy/opt-in")) {
resolvedUrls.put(type+"|"+url, true);
return true; return true;
} }
if (Utilities.existsInList(url, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct")) { if (Utilities.existsInList(url, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct")) {
resolvedUrls.put(type+"|"+url, true);
return true; return true;
} }
for (CanonicalResource cr : context.allConformanceResources()) { if (context.getNSUrlMap().containsKey(url)) {
if (cr instanceof NamingSystem) { resolvedUrls.put(type+"|"+url, true);
if (hasURL((NamingSystem) cr, url)) {
return true; return true;
} }
}
}
if (url.contains("example.org") || url.contains("acme.com")) { if (url.contains("example.org") || url.contains("acme.com")) {
resolvedUrls.put(type+"|"+url, false);
return false; // todo... how to access settings from here? return false; // todo... how to access settings from here?
} }
if (fetcher != null) { if (fetcher != null) {
try { try {
return fetcher.resolveURL(validator, appContext, path, url, type); boolean ok = fetcher.resolveURL(validator, appContext, path, url, type);
resolvedUrls.put(type+"|"+url, ok);
return ok;
} catch (Exception e) { } catch (Exception e) {
resolvedUrls.put(type+"|"+url, false);
return false; return false;
} }
} }
resolvedUrls.put(type+"|"+url, false);
return false; return false;
} }
private boolean hasURL(NamingSystem ns, String url) {
for (NamingSystemUniqueIdComponent uid : ns.getUniqueId()) {
if (uid.getType() == NamingSystemIdentifierType.URI && uid.hasValue() && uid.getValue().equals(url)) {
return true;
}
}
return false;
}
@Override @Override
public CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException { public CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException {
@ -922,4 +927,10 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
return fetcher != null && fetcher.fetchesCanonicalResource(validator, url); return fetcher != null && fetcher.fetchesCanonicalResource(validator, url);
} }
@Override
public void packageLoaded(String pid, String version) {
resolvedUrls.clear();
}
} }

View File

@ -248,7 +248,11 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV
@Override @Override
public CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException { public CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException {
if (url.contains("|")) {
url = url.substring(0, url.indexOf("|"));
}
String[] p = url.split("\\/"); String[] p = url.split("\\/");
String root = getRoot(p, url); String root = getRoot(p, url);
if (root != null) { if (root != null) {
TerminologyClient c; TerminologyClient c;

View File

@ -19,6 +19,7 @@ import org.hl7.fhir.r5.renderers.spreadsheets.ValueSetSpreadsheetGenerator;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.SimpleTimeTracker;
import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;

View File

@ -143,6 +143,8 @@ import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.utils.validation.constants.*; import org.hl7.fhir.r5.utils.validation.constants.*;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.SIDUtilities; import org.hl7.fhir.utilities.SIDUtilities;
import org.hl7.fhir.utilities.SimpleTimeTracker;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.UnicodeUtilities; import org.hl7.fhir.utilities.UnicodeUtilities;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.Utilities.DecimalStatus; import org.hl7.fhir.utilities.Utilities.DecimalStatus;
@ -159,6 +161,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.cli.utils.QuestionnaireMode;
import org.hl7.fhir.validation.cli.utils.ValidationLevel; import org.hl7.fhir.validation.cli.utils.ValidationLevel;
import org.hl7.fhir.validation.instance.InstanceValidator.CanonicalResourceLookupResult;
import org.hl7.fhir.validation.instance.type.BundleValidator; import org.hl7.fhir.validation.instance.type.BundleValidator;
import org.hl7.fhir.validation.instance.type.CodeSystemValidator; import org.hl7.fhir.validation.instance.type.CodeSystemValidator;
import org.hl7.fhir.validation.instance.type.MeasureValidator; import org.hl7.fhir.validation.instance.type.MeasureValidator;
@ -195,6 +198,20 @@ import com.google.gson.JsonObject;
*/ */
public class InstanceValidator extends BaseValidator implements IResourceValidator { public class InstanceValidator extends BaseValidator implements IResourceValidator {
public class CanonicalResourceLookupResult {
private CanonicalResource resource;
private String error;
public CanonicalResourceLookupResult(CanonicalResource resource) {
this.resource = resource;
}
public CanonicalResourceLookupResult(String error) {
this.error = error;
}
}
private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list"; private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list";
private static final String EXECUTION_ID = "validator.execution.id"; private static final String EXECUTION_ID = "validator.execution.id";
private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)"; private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)";
@ -301,18 +318,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try { try {
Element e = new ObjectConverter(context).convert((Resource) item); Element e = new ObjectConverter(context).convert((Resource) item);
setParents(e); setParents(e);
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage)); self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null);
} catch (IOException e1) { } catch (IOException e1) {
throw new FHIRException(e1); throw new FHIRException(e1);
} }
} else if (item instanceof Element) { } else if (item instanceof Element) {
Element e = (Element) item; Element e = (Element) item;
if (e.getSpecial() == SpecialElement.CONTAINED) { if (e.getSpecial() == SpecialElement.CONTAINED) {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage)); self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null);
} else if (e.getSpecial() != null) { } else if (e.getSpecial() != null) {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage)); self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null);
} else { } else {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage)); self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null);
} }
} else } else
throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT)); throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT));
@ -387,6 +404,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private IValidatorResourceFetcher fetcher; private IValidatorResourceFetcher fetcher;
private IValidationPolicyAdvisor policyAdvisor; private IValidationPolicyAdvisor policyAdvisor;
long time = 0; long time = 0;
long start = 0;
long lastlog = 0;
private IEvaluationContext externalHostServices; private IEvaluationContext externalHostServices;
private boolean noExtensibleWarnings; private boolean noExtensibleWarnings;
private String serverBase; private String serverBase;
@ -404,9 +423,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean validateValueSetCodesOnTxServer = true; private boolean validateValueSetCodesOnTxServer = true;
private QuestionnaireMode questionnaireMode; private QuestionnaireMode questionnaireMode;
private ValidationOptions baseOptions = new ValidationOptions(); private ValidationOptions baseOptions = new ValidationOptions();
private Map<String, CanonicalResourceLookupResult> crLookups = new HashMap<>();
public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices, XVerExtensionManager xverManager) { public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices, XVerExtensionManager xverManager) {
super(theContext, xverManager); super(theContext, xverManager);
start = System.currentTimeMillis();
this.externalHostServices = hostServices; this.externalHostServices = hostServices;
this.profileUtilities = new ProfileUtilities(theContext, null, null); this.profileUtilities = new ProfileUtilities(theContext, null, null);
fpe = new FHIRPathEngine(context); fpe = new FHIRPathEngine(context);
@ -761,10 +782,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime(); long t = System.nanoTime();
if (profiles == null || profiles.isEmpty()) { if (profiles == null || profiles.isEmpty()) {
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(context, path, element, validationLanguage).resetIds()); validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(context, path, element, validationLanguage).resetIds(), null);
} else { } else {
for (StructureDefinition defn : profiles) { for (StructureDefinition defn : profiles) {
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(context, path, element, validationLanguage).resetIds()); validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(context, path, element, validationLanguage).resetIds(), null);
} }
} }
if (hintAboutNonMustSupport) { if (hintAboutNonMustSupport) {
@ -1669,7 +1690,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
private StructureDefinition checkExtension(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl) throws FHIRException { private StructureDefinition checkExtension(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl, PercentageTracker pct) throws FHIRException {
String url = element.getNamedChildValue("url"); String url = element.getNamedChildValue("url");
boolean isModifier = element.getName().equals("modifierExtension"); boolean isModifier = element.getName().equals("modifierExtension");
assert def.getIsModifier() == isModifier; assert def.getIsModifier() == isModifier;
@ -1719,7 +1740,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType), I18nConstants.EXTENSION_EXT_TYPE, url, allowedTypes.toString(), actualType); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType), I18nConstants.EXTENSION_EXT_TYPE, url, allowedTypes.toString(), actualType);
// 3. is the content of the extension valid? // 3. is the content of the extension valid?
validateElement(hostContext, errors, ex, ex.getSnapshot().getElement().get(0), null, null, resource, element, "Extension", stack, false, true, url); validateElement(hostContext, errors, ex, ex.getSnapshot().getElement().get(0), null, null, resource, element, "Extension", stack, false, true, url, pct);
} }
return ex; return ex;
@ -2926,7 +2947,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
StructureDefinition profile, StructureDefinition profile,
ElementDefinition container, ElementDefinition container,
String parentType, String parentType,
NodeStack stack) throws FHIRException { NodeStack stack, PercentageTracker pct) throws FHIRException {
Reference reference = ObjectConverter.readAsReference(element); Reference reference = ObjectConverter.readAsReference(element);
String ref = reference.getReference(); String ref = reference.getReference();
@ -3058,7 +3079,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
for (StructureDefinition pr : profiles) { for (StructureDefinition pr : profiles) {
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>(); List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr,
IdStatus.OPTIONAL, we.getStack().resetIds()); IdStatus.OPTIONAL, we.getStack().resetIds(), pct);
if (!hasErrors(profileErrors)) { if (!hasErrors(profileErrors)) {
goodCount++; goodCount++;
goodProfiles.put(pr, profileErrors); goodProfiles.put(pr, profileErrors);
@ -4366,16 +4387,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
// checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up)
private void start(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException { private void start(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, PercentageTracker pct) throws FHIRException {
checkLang(resource, stack); checkLang(resource, stack);
if (crumbTrails) { if (crumbTrails) {
element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST, defn.getUrl())); element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST, defn.getUrl()));
} }
boolean pctOwned = false;
if (pct == null) {
// this method is reentrant, but also the right place to tell the user what is going on if it's the root.
// if we're not at the root, we don't report progress
pctOwned = true;
pct = new PercentageTracker(resource.countDescendents()+1, resource.fhirType(), defn.getUrl());
}
if (BUNDLE.equals(element.fhirType())) { if (BUNDLE.equals(element.fhirType())) {
if (debug) {
System.out.println("Resolve Bundle Entries "+time());
}
resolveBundleReferences(element, new ArrayList<Element>()); resolveBundleReferences(element, new ArrayList<Element>());
} }
startInner(hostContext, errors, resource, element, defn, stack, hostContext.isCheckSpecials()); startInner(hostContext, errors, resource, element, defn, stack, hostContext.isCheckSpecials(), pct);
if (pctOwned) {
pct.done();
}
Element meta = element.getNamedChild(META); Element meta = element.getNamedChild(META);
if (meta != null) { if (meta != null) {
@ -4403,11 +4436,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN, profile.primitiveValue()); warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN, profile.primitiveValue());
} else if (!fetcher.fetchesCanonicalResource(this, profile.primitiveValue())) { } else if (!fetcher.fetchesCanonicalResource(this, profile.primitiveValue())) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY, profile.primitiveValue()); warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY, profile.primitiveValue());
} else {
sd = null;
String url = profile.primitiveValue();
CanonicalResourceLookupResult cr = crLookups.get(url);
if (cr != null) {
if (cr.error != null) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_ERROR, url, cr.error);
} else {
sd = (StructureDefinition) cr.resource;
}
} else { } else {
try { try {
sd = (StructureDefinition) fetcher.fetchCanonicalResource(this, profile.primitiveValue()); sd = (StructureDefinition) fetcher.fetchCanonicalResource(this, url);
crLookups.put(url, new CanonicalResourceLookupResult(sd));
} catch (Exception e) { } catch (Exception e) {
if (STACK_TRACE) e.printStackTrace(); if (STACK_TRACE) { e.printStackTrace(); }
crLookups.put(url, new CanonicalResourceLookupResult(e.getMessage()));
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_ERROR, profile.primitiveValue(), e.getMessage()); warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_ERROR, profile.primitiveValue(), e.getMessage());
} }
if (sd != null) { if (sd != null) {
@ -4415,12 +4460,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
} }
}
if (sd != null) { if (sd != null) {
if (crumbTrails) { if (crumbTrails) {
element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_META, sd.getUrl())); element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_META, sd.getUrl()));
} }
stack.resetIds(); stack.resetIds();
startInner(hostContext, errors, resource, element, sd, stack, false); if (pctOwned) {
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getUrl());
}
startInner(hostContext, errors, resource, element, sd, stack, false, pct);
if (pctOwned) {
pct.done();
}
} }
} }
} }
@ -4437,12 +4489,27 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL, sd.getUrl(), ig.getUrl())); element.addMessage(signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_GLOBAL, sd.getUrl(), ig.getUrl()));
} }
stack.resetIds(); stack.resetIds();
startInner(hostContext, errors, resource, element, sd, stack, false); if (pctOwned) {
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getUrl());
}
startInner(hostContext, errors, resource, element, sd, stack, false, pct);
if (pctOwned) {
pct.done();
} }
} }
} }
} }
} }
// System.out.println("start: "+(System.currentTimeMillis()-st)+" ("+resource.fhirType()+")");
}
// private void plog(String msg) {
// long n = System.currentTimeMillis();
// String elapsed = Utilities.padLeft(Long.toString(n-start), ' ', 5);
// String delta = Utilities.padLeft(lastlog == 0 ? "0" : Long.toString(n-lastlog), ' ', 5);
// lastlog = n;
// System.out.println("-- "+elapsed+" "+delta+" "+msg);
// }
private void resolveBundleReferences(Element element, List<Element> bundles) { private void resolveBundleReferences(Element element, List<Element> bundles) {
if (!element.hasUserData("validator.bundle.resolved")) { if (!element.hasUserData("validator.bundle.resolved")) {
@ -4462,7 +4529,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
private void resolveBundleReferencesInResource(List<Element> bundles, Element r, String fu) { private void resolveBundleReferencesInResource(List<Element> bundles, Element r, String fu) {
r.setUserData("validator.bundle.resolution-resource", null);
if (BUNDLE.equals(r.fhirType())) { if (BUNDLE.equals(r.fhirType())) {
resolveBundleReferences(r, bundles); resolveBundleReferences(r, bundles);
} else { } else {
@ -4478,16 +4544,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!Utilities.noString(ref)) { if (!Utilities.noString(ref)) {
for (Element bundle : bundles) { for (Element bundle : bundles) {
List<Element> entries = bundle.getChildren(ENTRY); List<Element> entries = bundle.getChildren(ENTRY);
Element tgt = resolveInBundle(entries, ref, fu, resource.fhirType(), resource.getIdBase()); Element tgt = resolveInBundle(bundle, entries, ref, fu, resource.fhirType(), resource.getIdBase());
if (tgt != null) { if (tgt != null) {
element.setUserData("validator.bundle.resolution", tgt.getNamedChild(RESOURCE)); element.setUserData("validator.bundle.resolution", tgt.getNamedChild(RESOURCE));
return; return;
} }
} }
element.setUserData("validator.bundle.resolution-failed", ref);
} }
} else { } else {
element.setUserData("validator.bundle.resolution-noref", null);
for (Element child : element.getChildren()) { for (Element child : element.getChildren()) {
resolveBundleReferencesForElement(bundles, resource, fu, child); resolveBundleReferencesForElement(bundles, resource, fu, child);
} }
@ -4495,7 +4559,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
public void startInner(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials) { public void startInner(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials, PercentageTracker pct) {
// the first piece of business is to see if we've validated this resource against this profile before. // the first piece of business is to see if we've validated this resource against this profile before.
// if we have (*or if we still are*), then we'll just return our existing errors // if we have (*or if we still are*), then we'll just return our existing errors
ResourceValidationTracker resTracker = getResourceTracker(element); ResourceValidationTracker resTracker = getResourceTracker(element);
@ -4512,7 +4576,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
List<ValidationMessage> localErrors = new ArrayList<ValidationMessage>(); List<ValidationMessage> localErrors = new ArrayList<ValidationMessage>();
resTracker.startValidating(defn); resTracker.startValidating(defn);
trackUsage(defn, hostContext, element); trackUsage(defn, hostContext, element);
validateElement(hostContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null); validateElement(hostContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null, pct);
resTracker.storeOutcomes(defn, localErrors); resTracker.storeOutcomes(defn, localErrors);
for (ValidationMessage vm : localErrors) { for (ValidationMessage vm : localErrors) {
if (!errors.contains(vm)) { if (!errors.contains(vm)) {
@ -4521,15 +4585,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
if (checkSpecials) { if (checkSpecials) {
checkSpecials(hostContext, errors, element, stack, checkSpecials); checkSpecials(hostContext, errors, element, stack, checkSpecials, pct);
validateResourceRules(errors, element, stack); validateResourceRules(errors, element, stack);
} }
} }
public void checkSpecials(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials) { public void checkSpecials(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct) {
// specific known special validations // specific known special validations
if (element.getType().equals(BUNDLE)) { if (element.getType().equals(BUNDLE)) {
new BundleValidator(context, serverBase, this, xverManager, jurisdiction).validateBundle(errors, element, stack, checkSpecials, hostContext); new BundleValidator(context, serverBase, this, xverManager, jurisdiction).validateBundle(errors, element, stack, checkSpecials, hostContext, pct);
} else if (element.getType().equals("Observation")) { } else if (element.getType().equals("Observation")) {
validateObservation(errors, element, stack); validateObservation(errors, element, stack);
} else if (element.getType().equals("Questionnaire")) { } else if (element.getType().equals("Questionnaire")) {
@ -4635,7 +4699,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private void validateContains(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, private void validateContains(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path,
ElementDefinition child, ElementDefinition context, Element resource, ElementDefinition child, ElementDefinition context, Element resource,
Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile) throws FHIRException { Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile, PercentageTracker pct) throws FHIRException {
SpecialElement special = element.getSpecial(); SpecialElement special = element.getSpecial();
@ -4701,7 +4765,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
trackUsage(profile, hostContext, element); trackUsage(profile, hostContext, element);
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) { profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) {
validateResource(hc, errors, resource, element, profile, idstatus, stack); validateResource(hc, errors, resource, element, profile, idstatus, stack, pct);
} }
} else if (typeForResource.getProfile().isEmpty()) { } else if (typeForResource.getProfile().isEmpty()) {
long t = System.nanoTime(); long t = System.nanoTime();
@ -4711,7 +4775,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
trackUsage(profile, hostContext, element); trackUsage(profile, hostContext, element);
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) { profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) {
validateResource(hc, errors, resource, element, profile, idstatus, stack); validateResource(hc, errors, resource, element, profile, idstatus, stack, pct);
} }
} else { } else {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
@ -4774,7 +4838,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private void validateElement(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, StructureDefinition cprofile, ElementDefinition context, private void validateElement(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, StructureDefinition cprofile, ElementDefinition context,
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, String extensionUrl) throws FHIRException { Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, String extensionUrl, PercentageTracker pct) throws FHIRException {
pct.seeElement(element);
String id = element.getChildValue("id"); String id = element.getChildValue("id");
if (!Utilities.noString(id)) { if (!Utilities.noString(id)) {
@ -4822,7 +4888,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// 5. inspect each child for validity // 5. inspect each child for validity
for (ElementInfo ei : children) { for (ElementInfo ei : children) {
checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl); checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, pct);
} }
} }
@ -4860,7 +4926,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
public void checkChild(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, public void checkChild(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition,
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, PercentageTracker pct)
throws FHIRException, DefinitionException { throws FHIRException, DefinitionException {
if (debug && ei.definition != null && ei.slice != null) { if (debug && ei.definition != null && ei.slice != null) {
@ -4868,25 +4934,33 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
if (ei.definition != null) { if (ei.definition != null) {
if (debug) { if (debug) {
System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against defn "+ei.definition.getId()+" from "+profile.getUrl()); System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against defn "+ei.definition.getId()+" from "+profile.getUrl()+time());
} }
checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.definition, false); checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.definition, false, pct);
} }
if (ei.slice != null) { if (ei.slice != null) {
if (debug) { if (debug) {
System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against slice "+ei.slice.getId()); System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against slice "+ei.slice.getId()+time());
} }
checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.slice, true); checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.slice, true, pct);
} }
} }
private String time() {
long t = System.currentTimeMillis();
String s = " "+(t - start)+"ms";
start = t;
return s;
}
public void checkChildByDefinition(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, public void checkChildByDefinition(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile,
ElementDefinition definition, Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, ElementDefinition definition, Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept,
boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, ElementDefinition checkDefn, boolean isSlice) { boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, ElementDefinition checkDefn, boolean isSlice, PercentageTracker pct) {
List<String> profiles = new ArrayList<String>(); List<String> profiles = new ArrayList<String>();
String type = null; String type = null;
ElementDefinition typeDefn = null; ElementDefinition typeDefn = null;
checkMustSupport(profile, ei); checkMustSupport(profile, ei);
long s = System.currentTimeMillis();
if (checkDefn.getType().size() == 1 && !"*".equals(checkDefn.getType().get(0).getWorkingCode()) && !"Element".equals(checkDefn.getType().get(0).getWorkingCode()) if (checkDefn.getType().size() == 1 && !"*".equals(checkDefn.getType().get(0).getWorkingCode()) && !"Element".equals(checkDefn.getType().get(0).getWorkingCode())
&& !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) { && !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) {
@ -4968,9 +5042,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
NodeStack localStack = stack.push(ei.getElement(), "*".equals(ei.getDefinition().getBase().getMax()) && ei.count == -1 ? 0 : ei.count, checkDefn, type == null ? typeDefn : resolveType(type, checkDefn.getType())); NodeStack localStack = stack.push(ei.getElement(), "*".equals(ei.getDefinition().getBase().getMax()) && ei.count == -1 ? 0 : ei.count, checkDefn, type == null ? typeDefn : resolveType(type, checkDefn.getType()));
// if (debug) { if (debug) {
// System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()); System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()+time());
// } }
String localStackLiteralPath = localStack.getLiteralPath(); String localStackLiteralPath = localStack.getLiteralPath();
String eiPath = ei.getPath(); String eiPath = ei.getPath();
if (!eiPath.equals(localStackLiteralPath)) { if (!eiPath.equals(localStackLiteralPath)) {
@ -5012,7 +5086,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, checkDefn, stack); checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, checkDefn, stack);
thisIsCodeableConcept = true; thisIsCodeableConcept = true;
} else if (type.equals("Reference")) { } else if (type.equals("Reference")) {
checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, checkDefn, actualType, localStack); checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, checkDefn, actualType, localStack, pct);
// We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension // We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension
} else if (type.equals("Extension")) { } else if (type.equals("Extension")) {
Element eurl = ei.getElement().getNamedChild("url"); Element eurl = ei.getElement().getNamedChild("url");
@ -5021,13 +5095,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
thisExtension = url; thisExtension = url;
if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) { if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) {
if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), I18nConstants.EXTENSION_EXT_URL_ABSOLUTE)) { if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), I18nConstants.EXTENSION_EXT_URL_ABSOLUTE)) {
checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), checkDefn, profile, localStack, stack, extensionUrl); checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), checkDefn, profile, localStack, stack, extensionUrl, pct);
} }
} }
} }
} else if (type.equals("Resource") || isResource(type)) { } else if (type.equals("Resource") || isResource(type)) {
validateContains(hostContext, errors, ei.getPath(), checkDefn, definition, resource, ei.getElement(), validateContains(hostContext, errors, ei.getPath(), checkDefn, definition, resource, ei.getElement(),
localStack, idStatusForEntry(element, ei), profile); // if localStack, idStatusForEntry(element, ei), profile, pct); // if
elementValidated = true; elementValidated = true;
// (str.matches(".*([.,/])work\\1$")) // (str.matches(".*([.,/])work\\1$"))
} else if (Utilities.isAbsoluteUrl(type)) { } else if (Utilities.isAbsoluteUrl(type)) {
@ -5044,7 +5118,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} else { } else {
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), checkDefn != null, I18nConstants.VALIDATION_VAL_CONTENT_UNKNOWN, ei.getName())) if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), checkDefn != null, I18nConstants.VALIDATION_VAL_CONTENT_UNKNOWN, ei.getName()))
validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, false, true, null); validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, false, true, null, pct);
} }
StructureDefinition p = null; StructureDefinition p = null;
String tail = null; String tail = null;
@ -5083,7 +5157,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
p = this.context.fetchResource(StructureDefinition.class, typeProfile); p = this.context.fetchResource(StructureDefinition.class, typeProfile);
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, typeProfile)) { if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, typeProfile)) {
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>(); List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct);
if (hasErrors(profileErrors)) if (hasErrors(profileErrors))
badProfiles.put(typeProfile, profileErrors); badProfiles.put(typeProfile, profileErrors);
else else
@ -5117,15 +5191,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!elementValidated) { if (!elementValidated) {
if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER)
validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, ei.getElement(), ei.getElement(), type, localStack.resetIds(), thisIsCodeableConcept, checkDisplay, thisExtension); validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, ei.getElement(), ei.getElement(), type, localStack.resetIds(), thisIsCodeableConcept, checkDisplay, thisExtension, pct);
else else
validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct);
} }
int index = profile.getSnapshot().getElement().indexOf(checkDefn); int index = profile.getSnapshot().getElement().indexOf(checkDefn);
if (index < profile.getSnapshot().getElement().size() - 1) { if (index < profile.getSnapshot().getElement().size() - 1) {
String nextPath = profile.getSnapshot().getElement().get(index + 1).getPath(); String nextPath = profile.getSnapshot().getElement().get(index + 1).getPath();
if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath())) if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath()))
validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct);
} }
} }
} }
@ -5357,8 +5431,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// 1. List the children, and remember their exact path (convenience) // 1. List the children, and remember their exact path (convenience)
List<ElementInfo> children = new ArrayList<ElementInfo>(); List<ElementInfo> children = new ArrayList<ElementInfo>();
ChildIterator iter = new ChildIterator(this, stack.getLiteralPath(), element); ChildIterator iter = new ChildIterator(this, stack.getLiteralPath(), element);
while (iter.next()) while (iter.next()) {
children.add(new ElementInfo(iter.name(), iter.element(), iter.path(), iter.count())); children.add(new ElementInfo(iter.name(), iter.element(), iter.path(), iter.count()));
}
return children; return children;
} }
@ -5535,9 +5610,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
public void checkInvariant(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException { public void checkInvariant(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException {
// if (debug) { if (debug) {
// System.out.println("inv "+inv.getKey()+" on "+path+" in "+resource.fhirType()+" {{ "+inv.getExpression()+" }}"); System.out.println("inv "+inv.getKey()+" on "+path+" in "+resource.fhirType()+" {{ "+inv.getExpression()+" }}"+time());
// } }
ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache");
if (n == null) { if (n == null) {
long t = System.nanoTime(); long t = System.nanoTime();
@ -5600,7 +5675,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
* The actual base entry point for internal use (re-entrant) * The actual base entry point for internal use (re-entrant)
*/ */
private void validateResource(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, private void validateResource(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource,
Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException { Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct) throws FHIRException {
// check here if we call validation policy here, and then change it to the new interface // check here if we call validation policy here, and then change it to the new interface
assert stack != null; assert stack != null;
@ -5646,7 +5721,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// validate // validate
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), resourceName.equals(defn.getType()), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), resourceName.equals(defn.getType()), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE,
defn.getType(), resourceName, defn.getUrl())) { defn.getType(), resourceName, defn.getUrl())) {
start(hostContext, errors, element, element, defn, stack); // root is both definition and type start(hostContext, errors, element, element, defn, stack, pct); // root is both definition and type
} }
} }
} }

View File

@ -0,0 +1,41 @@
package org.hl7.fhir.validation.instance;
import org.hl7.fhir.r5.elementmodel.Element;
public class PercentageTracker {
private int total;
private int last;
private int current;
private static int instance;
public PercentageTracker(int total, String fhirType, String url) {
this.total = total;
instance++;
last = 0;
System.out.print("Validate "+fhirType+" against "+url);
}
public void done() {
System.out.println("|");
}
public void seeElement(Element e) {
if (e.getInstanceId() != instance) {
e.setInstanceId(instance);
current++;
int pct = total == 0 ? 0: (current*100) / total;
if (pct > last + 2) {
while (last + 2 < pct) {
System.out.print(".");
last = last + 2;
if (last % 20 == 0) {
System.out.print(""+last);
}
}
}
}
}
}

View File

@ -23,6 +23,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.validation.instance.InstanceValidator;
import org.hl7.fhir.validation.instance.PercentageTracker;
import org.hl7.fhir.validation.instance.utils.EntrySummary; import org.hl7.fhir.validation.instance.utils.EntrySummary;
import org.hl7.fhir.validation.instance.utils.IndexedElement; import org.hl7.fhir.validation.instance.utils.IndexedElement;
import org.hl7.fhir.validation.instance.utils.NodeStack; import org.hl7.fhir.validation.instance.utils.NodeStack;
@ -40,7 +41,7 @@ public class BundleValidator extends BaseValidator {
this.jurisdiction = jurisdiction; this.jurisdiction = jurisdiction;
} }
public void validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials, ValidatorHostContext hostContext) { public void validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials, ValidatorHostContext hostContext, PercentageTracker pct) {
List<Element> entries = new ArrayList<Element>(); List<Element> entries = new ArrayList<Element>();
bundle.getNamedChildren(ENTRY, entries); bundle.getNamedChildren(ENTRY, entries);
String type = bundle.getNamedChildValue(TYPE); String type = bundle.getNamedChildValue(TYPE);
@ -60,7 +61,7 @@ public class BundleValidator extends BaseValidator {
Element resource = firstEntry.getNamedChild(RESOURCE); Element resource = firstEntry.getNamedChild(RESOURCE);
if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath(ENTRY, PATH_ARG), resource != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE)) { if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath(ENTRY, PATH_ARG), resource != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE)) {
String id = resource.getNamedChildValue(ID); String id = resource.getNamedChildValue(ID);
validateDocument(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); validateDocument(errors, bundle, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id);
} }
if (!VersionUtilities.isThisOrLater(FHIRVersion._4_0_1.getDisplay(), bundle.getProperty().getStructure().getFhirVersion().getDisplay())) { if (!VersionUtilities.isThisOrLater(FHIRVersion._4_0_1.getDisplay(), bundle.getProperty().getStructure().getFhirVersion().getDisplay())) {
handleSpecialCaseForLastUpdated(bundle, errors, stack); handleSpecialCaseForLastUpdated(bundle, errors, stack);
@ -118,7 +119,7 @@ public class BundleValidator extends BaseValidator {
res.addMessage(signpost(errors, IssueType.INFORMATIONAL, res.line(), res.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_BUNDLE_PARAM, defn.getUrl())); res.addMessage(signpost(errors, IssueType.INFORMATIONAL, res.line(), res.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_BUNDLE_PARAM, defn.getUrl()));
} }
stack.resetIds(); stack.resetIds();
validator.startInner(hostContext, errors, res, res, defn, rstack, false); validator.startInner(hostContext, errors, res, res, defn, rstack, false, pct);
} }
} }
} }
@ -282,23 +283,23 @@ public class BundleValidator extends BaseValidator {
return null; return null;
} }
private void validateDocument(List<ValidationMessage> errors, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id) { private void validateDocument(List<ValidationMessage> errors, Element bundle, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id) {
// first entry must be a composition // first entry must be a composition
if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), I18nConstants.BUNDLE_BUNDLE_ENTRY_DOCUMENT)) { if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), I18nConstants.BUNDLE_BUNDLE_ENTRY_DOCUMENT)) {
// the composition subject etc references must resolve in the bundle // the composition subject etc references must resolve in the bundle
validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "subject", "Composition"); validateDocumentReference(errors, bundle, entries, composition, stack, fullUrl, id, false, "subject", "Composition");
validateDocumentReference(errors, entries, composition, stack, fullUrl, id, true, "author", "Composition"); validateDocumentReference(errors, bundle, entries, composition, stack, fullUrl, id, true, "author", "Composition");
validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "encounter", "Composition"); validateDocumentReference(errors, bundle, entries, composition, stack, fullUrl, id, false, "encounter", "Composition");
validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "custodian", "Composition"); validateDocumentReference(errors, bundle, entries, composition, stack, fullUrl, id, false, "custodian", "Composition");
validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "attester", false, "party"); validateDocumentSubReference(errors, bundle, entries, composition, stack, fullUrl, id, "Composition", "attester", false, "party");
validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "event", true, "detail"); validateDocumentSubReference(errors, bundle, entries, composition, stack, fullUrl, id, "Composition", "event", true, "detail");
validateSections(errors, entries, composition, stack, fullUrl, id); validateSections(errors, bundle, entries, composition, stack, fullUrl, id);
} }
} }
private void validateSections(List<ValidationMessage> errors, List<Element> entries, Element focus, NodeStack stack, String fullUrl, String id) { private void validateSections(List<ValidationMessage> errors, Element bundle, List<Element> entries, Element focus, NodeStack stack, String fullUrl, String id) {
List<Element> sections = new ArrayList<Element>(); List<Element> sections = new ArrayList<Element>();
focus.getNamedChildren("section", sections); focus.getNamedChildren("section", sections);
int i = 1; int i = 1;
@ -306,48 +307,48 @@ public class BundleValidator extends BaseValidator {
NodeStack localStack = stack.push(section, i, null, null); NodeStack localStack = stack.push(section, i, null, null);
// technically R4+, but there won't be matches from before that // technically R4+, but there won't be matches from before that
validateDocumentReference(errors, entries, section, stack, fullUrl, id, true, "author", "Section"); validateDocumentReference(errors, bundle, entries, section, stack, fullUrl, id, true, "author", "Section");
validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "focus", "Section"); validateDocumentReference(errors, bundle, entries, section, stack, fullUrl, id, false, "focus", "Section");
List<Element> sectionEntries = new ArrayList<Element>(); List<Element> sectionEntries = new ArrayList<Element>();
section.getNamedChildren(ENTRY, sectionEntries); section.getNamedChildren(ENTRY, sectionEntries);
int j = 1; int j = 1;
for (Element sectionEntry : sectionEntries) { for (Element sectionEntry : sectionEntries) {
NodeStack localStack2 = localStack.push(sectionEntry, j, null, null); NodeStack localStack2 = localStack.push(sectionEntry, j, null, null);
validateBundleReference(errors, entries, sectionEntry, "Section Entry", localStack2, fullUrl, "Composition", id); validateBundleReference(errors, bundle, entries, sectionEntry, "Section Entry", localStack2, fullUrl, "Composition", id);
j++; j++;
} }
validateSections(errors, entries, section, localStack, fullUrl, id); validateSections(errors, bundle, entries, section, localStack, fullUrl, id);
i++; i++;
} }
} }
public void validateDocumentSubReference(List<ValidationMessage> errors, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id, String title, String parent, boolean repeats, String propName) { public void validateDocumentSubReference(List<ValidationMessage> errors, Element bundle, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id, String title, String parent, boolean repeats, String propName) {
List<Element> list = new ArrayList<>(); List<Element> list = new ArrayList<>();
composition.getNamedChildren(parent, list); composition.getNamedChildren(parent, list);
int i = 1; int i = 1;
for (Element elem : list) { for (Element elem : list) {
validateDocumentReference(errors, entries, elem, stack.push(elem, i, null, null), fullUrl, id, repeats, propName, title + "." + parent); validateDocumentReference(errors, bundle, entries, elem, stack.push(elem, i, null, null), fullUrl, id, repeats, propName, title + "." + parent);
i++; i++;
} }
} }
public void validateDocumentReference(List<ValidationMessage> errors, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id, boolean repeats, String propName, String title) { public void validateDocumentReference(List<ValidationMessage> errors, Element bundle, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id, boolean repeats, String propName, String title) {
if (repeats) { if (repeats) {
List<Element> list = new ArrayList<>(); List<Element> list = new ArrayList<>();
composition.getNamedChildren(propName, list); composition.getNamedChildren(propName, list);
int i = 1; int i = 1;
for (Element elem : list) { for (Element elem : list) {
validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, i, null, null), fullUrl, "Composition", id); validateBundleReference(errors, bundle, entries, elem, title + "." + propName, stack.push(elem, i, null, null), fullUrl, "Composition", id);
i++; i++;
} }
} else { } else {
Element elem = composition.getNamedChild(propName); Element elem = composition.getNamedChild(propName);
if (elem != null) { if (elem != null) {
validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, -1, null, null), fullUrl, "Composition", id); validateBundleReference(errors, bundle, entries, elem, title + "." + propName, stack.push(elem, -1, null, null), fullUrl, "Composition", id);
} }
} }
} }
@ -357,11 +358,11 @@ public class BundleValidator extends BaseValidator {
if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"), I18nConstants.VALIDATION_BUNDLE_MESSAGE)) { if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"), I18nConstants.VALIDATION_BUNDLE_MESSAGE)) {
List<Element> elements = messageHeader.getChildren("focus"); List<Element> elements = messageHeader.getChildren("focus");
for (Element elem : elements) for (Element elem : elements)
validateBundleReference(errors, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id); validateBundleReference(errors, messageHeader, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id);
} }
} }
private void validateBundleReference(List<ValidationMessage> errors, List<Element> entries, Element ref, String name, NodeStack stack, String fullUrl, String type, String id) { private void validateBundleReference(List<ValidationMessage> errors, Element bundle, List<Element> entries, Element ref, String name, NodeStack stack, String fullUrl, String type, String id) {
String reference = null; String reference = null;
try { try {
reference = ref.getNamedChildValue("reference"); reference = ref.getNamedChildValue("reference");
@ -370,7 +371,7 @@ public class BundleValidator extends BaseValidator {
} }
if (ref != null && !Utilities.noString(reference) && !reference.startsWith("#")) { if (ref != null && !Utilities.noString(reference) && !reference.startsWith("#")) {
Element target = resolveInBundle(entries, reference, fullUrl, type, id); Element target = resolveInBundle(bundle, entries, reference, fullUrl, type, id);
rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null,
I18nConstants.BUNDLE_BUNDLE_ENTRY_NOTFOUND, reference, name); I18nConstants.BUNDLE_BUNDLE_ENTRY_NOTFOUND, reference, name);
} }
@ -413,7 +414,7 @@ public class BundleValidator extends BaseValidator {
for (EntrySummary e : entryList) { for (EntrySummary e : entryList) {
Set<String> references = findReferences(e.getEntry()); Set<String> references = findReferences(e.getEntry());
for (String ref : references) { for (String ref : references) {
Element tgt = resolveInBundle(entries, ref, e.getEntry().getChildValue(FULL_URL), e.getResource().fhirType(), e.getResource().getIdBase()); Element tgt = resolveInBundle(bundle, entries, ref, e.getEntry().getChildValue(FULL_URL), e.getResource().fhirType(), e.getResource().getIdBase());
if (tgt != null) { if (tgt != null) {
EntrySummary t = entryForTarget(entryList, tgt); EntrySummary t = entryForTarget(entryList, tgt);
if (t != null ) { if (t != null ) {