Merge pull request #1438 from hapifhir/2023-09-gg-performance

2023 09 gg performance
This commit is contained in:
Grahame Grieve 2023-09-19 09:59:12 +10:00 committed by GitHub
commit dd9c2fb9d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 321 additions and 83 deletions

View File

@ -2,6 +2,7 @@ package org.hl7.fhir.convertors.misc;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -10,6 +11,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;
import javax.annotation.Nonnull;
@ -35,6 +37,7 @@ import org.hl7.fhir.r5.model.Enumerations.FHIRVersionEnumFactory;
import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.json.model.JsonArray;
import org.hl7.fhir.utilities.json.model.JsonObject;
@ -118,11 +121,11 @@ public class NpmPackageVersionConverter {
NpmPackageIndexBuilder indexer = indexers.get(n);
if (indexer == null) {
indexer = new NpmPackageIndexBuilder();
indexer.start();
indexer.start(Utilities.path("[tmp]", "tmp-"+UUID.randomUUID().toString()+".db"));
indexers.put(n, indexer);
}
indexer.seeFile(s, b);
if (!s.equals(".index.json") && !s.equals("package.json")) {
if (!s.equals(".index.json") && !s.equals("package.json") && !s.equals(".index.db")) {
TarArchiveEntry entry = new TarArchiveEntry(e.getKey());
entry.setSize(b.length);
tar.putArchiveEntry(entry);
@ -137,6 +140,13 @@ public class NpmPackageVersionConverter {
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
cnt = TextFile.fileToBytes(e.getValue().getDbFilename());
new File(e.getValue().getDbFilename()).delete();
entry = new TarArchiveEntry(e.getKey() + "/.index.db");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
}
byte[] cnt = output.get("package/package.json");

View File

@ -57,6 +57,7 @@ import org.hl7.fhir.r4b.model.Enumeration;
import org.hl7.fhir.r4b.model.Enumerations.FHIRVersion;
import org.hl7.fhir.r4b.model.ImplementationGuide;
import org.hl7.fhir.r4b.model.ImplementationGuide.ImplementationGuideDependsOnComponent;
import org.hl7.fhir.r4b.utils.NPMPackageGenerator.Category;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
@ -110,6 +111,7 @@ public class NPMPackageGenerator {
private JsonObject packageManifest;
private NpmPackageIndexBuilder indexer;
private String igVersion;
private String indexdb;
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig,
Date date, boolean notForPublication) throws FHIRException, IOException {
@ -324,8 +326,9 @@ public class NPMPackageGenerator {
bufferedOutputStream = new BufferedOutputStream(OutputStream);
gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream);
tar = new TarArchiveOutputStream(gzipOutputStream);
indexdb = Utilities.path("[tmp]", "tmp-"+UUID.randomUUID().toString()+".db");
indexer = new NpmPackageIndexBuilder();
indexer.start();
indexer.start(indexdb);
}
public void addFile(Category cat, String name, byte[] content) throws IOException {
@ -367,6 +370,9 @@ public class NPMPackageGenerator {
private void buildIndexJson() throws IOException {
byte[] content = TextFile.stringToBytes(indexer.build(), false);
addFile(Category.RESOURCE, ".index.json", content);
content = TextFile.fileToBytes(indexdb);
new File(indexdb).delete();
addFile(Category.RESOURCE, ".index.db", content);
}
public String filename() {

View File

@ -121,7 +121,7 @@ public class JsonParser extends ParserBase {
@Override
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRException {
// long start = System.currentTimeMillis();
long start = System.currentTimeMillis();
byte[] content = TextFile.streamToBytes(inStream);
NamedElement ctxt = new NamedElement("focus", "json", content);
@ -145,8 +145,8 @@ public class JsonParser extends ParserBase {
List<NamedElement> res = new ArrayList<>();
res.add(ctxt);
// long t=System.currentTimeMillis()-start;
// System.out.println("json parser: "+(t)+": "+content.length+(t == 0 ? "" : " @ "+(content.length / t)));
long t=System.currentTimeMillis()-start;
System.out.println("json parser: "+(t)+"ms, "+(content.length/1024)+"kb "+(t == 0 ? "" : " @ "+(content.length / t)+"kb/s"));
return res;
}
@ -213,55 +213,88 @@ public class JsonParser extends ParserBase {
// save time refetching these if we're in a loop
properties = element.getProperty().getChildProperties(element.getName(), null);
}
Map<String, JsonProperty> recognisedChildren = processChildren(errors, path, object);
processChildren(errors, path, object);
// note that we do not trouble ourselves to maintain the wire format order here
// first pass: process the properties
for (Property property : properties) {
parseChildItem(errors, path, recognisedChildren, element, property);
parseChildItem(errors, path, object.getProperties(), element, property);
}
// second pass: check for things not processed
checkNotProcessed(errors, path, element, hasResourceType, recognisedChildren);
// second pass: check for things not processed (including duplicates)
checkNotProcessed(errors, path, element, hasResourceType, object.getProperties());
if (object.isExtraComma()) {
logError(errors, "2022-11-26", object.getEnd().getLine(), object.getEnd().getCol(), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Object"), IssueSeverity.ERROR);
}
return properties;
}
private void checkNotProcessed(List<ValidationMessage> errors, String path, Element element, boolean hasResourceType, Map<String, JsonProperty> recognisedChildren) {
private void checkNotProcessed(List<ValidationMessage> errors, String path, Element element, boolean hasResourceType, List<JsonProperty> children) {
if (policy != ValidationPolicy.NONE) {
for (Entry<String, JsonProperty> e : recognisedChildren.entrySet()) {
if (e.getValue().getTag() == 0) {
StructureDefinition sd = element.getProperty().isLogical() ? contextUtilities.fetchByJsonName(e.getKey()) : null;
for (JsonProperty e : children) {
if (e.getTag() == 0) {
StructureDefinition sd = element.getProperty().isLogical() ? contextUtilities.fetchByJsonName(e.getName()) : null;
if (sd != null) {
Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils());
parseChildItem(errors, path, recognisedChildren, element, property);
} else if ("fhir_comments".equals(e.getKey()) && (VersionUtilities.isR2BVer(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion()))) {
if (!e.getValue().getValue().isJsonArray()) {
logError(errors, "2022-12-17", line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, e.getValue().getValue().type().toName()), IssueSeverity.ERROR);
parseChildItem(errors, path, children, element, property);
} else if ("fhir_comments".equals(e.getName()) && (VersionUtilities.isR2BVer(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion()))) {
if (!e.getValue().isJsonArray()) {
logError(errors, "2022-12-17", line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, e.getValue().type().toName()), IssueSeverity.ERROR);
} else {
for (JsonElement c : e.getValue().getValue().asJsonArray()) {
for (JsonElement c : e.getValue().asJsonArray()) {
if (!c.isJsonString()) {
logError(errors, "2022-12-17", line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, c.type().toName()), IssueSeverity.ERROR);
logError(errors, "2022-12-17", line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, c.type().toName()), IssueSeverity.ERROR);
} else {
element.getComments().add(c.asString());
}
}
}
} else if (hasResourceType && "resourceType".equals(e.getKey())) {
} else if (hasResourceType && "resourceType".equals(e.getName())) {
// nothing
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_, e.getKey()), IssueSeverity.ERROR);
JsonProperty p = getFoundJsonPropertyByName(e.getName(), children);
if (p != null) {
logError(errors, "2022-11-26", line(e.getValue()), col(e.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.DUPLICATE_JSON_PROPERTY, e.getName()), IssueSeverity.ERROR);
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_, e.getName()), IssueSeverity.ERROR);
}
}
}
}
}
}
}
private Map<String, JsonProperty> processChildren(List<ValidationMessage> errors, String path, JsonObject object) {
Map<String, JsonProperty> recognisedChildren = new HashMap<>();
Set<String> unique = new HashSet<>();
private JsonProperty getFoundJsonPropertyByName(String name, List<JsonProperty> children) {
int hash = name.hashCode();
for (JsonProperty p : children) {
if (p.getTag() == 1 && hash == p.getNameHash()) {
return p;
}
}
return null;
}
private JsonProperty getJsonPropertyByName(String name, List<JsonProperty> children) {
int hash = name.hashCode();
for (JsonProperty p : children) {
if (p.getTag() == 0 && hash == p.getNameHash()) {
return p;
}
}
return null;
}
private JsonProperty getJsonPropertyByBaseName(String name, List<JsonProperty> children) {
for (JsonProperty p : children) {
if (p.getTag() == 0 && p.getName().startsWith(name)) {
return p;
}
}
return null;
}
private void processChildren(List<ValidationMessage> errors, String path, JsonObject object) {
for (JsonProperty p : object.getProperties()) {
if (p.isUnquotedName()) {
logError(errors, "2022-11-26", line(p.getValue()), col(p.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_PROPERTY_NO_QUOTES, p.getName()), IssueSeverity.ERROR);
@ -269,20 +302,13 @@ public class JsonParser extends ParserBase {
if (p.isNoComma()) {
logError(errors, "2022-11-26", line(p.getValue()), col(p.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_MISSING), IssueSeverity.ERROR);
}
if (unique.contains(p.getName())) {
logError(errors, "2022-11-26", line(p.getValue()), col(p.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.DUPLICATE_JSON_PROPERTY, p.getName()), IssueSeverity.ERROR);
} else {
unique.add(p.getName());
recognisedChildren.put(p.getName(), p);
}
}
return recognisedChildren;
}
public void parseChildItem(List<ValidationMessage> errors, String path, Map<String, JsonProperty> children, Element context, Property property) {
public void parseChildItem(List<ValidationMessage> errors, String path, List<JsonProperty> children, Element context, Property property) {
JsonProperty jp = getJsonPropertyByName(property.getJsonName(), children);
if (property.isChoice() || property.getDefinition().getPath().endsWith("data[x]")) {
if (property.isJsonPrimitiveChoice()) {
JsonProperty jp = children.get(property.getJsonName());
if (jp != null) {
jp.setTag(1);
JsonElement je = jp.getValue();
@ -291,30 +317,38 @@ public class JsonParser extends ParserBase {
logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE, describeType(je), property.getName(), property.typeSummary()), IssueSeverity.ERROR);
} else if (property.hasType(type)) {
Property np = new Property(property.getContext(), property.getDefinition(), property.getStructure(), property.getUtils(), type);
parseChildPrimitive(errors, children, context, np, path, property.getName(), false);
parseChildPrimitive(errors, jp, getJsonPropertyByName("_"+property.getJsonName(), children), context, np, path, property.getName(), false);
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE_WRONG, describeType(je), property.getName(), type, property.typeSummary()), IssueSeverity.ERROR);
}
}
} else {
for (TypeRefComponent type : property.getDefinition().getType()) {
String eName = property.getJsonName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getWorkingCode());
if (!isPrimitive(type.getWorkingCode()) && children.containsKey(eName)) {
parseChildComplex(errors, path, children, context, property, eName, false);
break;
} else if (isPrimitive(type.getWorkingCode()) && (children.containsKey(eName) || children.containsKey("_"+eName))) {
parseChildPrimitive(errors, children, context, property, path, eName, false);
break;
} else {
String baseName = property.getJsonName().substring(0, property.getName().length()-3);
jp = getJsonPropertyByBaseName(baseName, children);
if (jp != null) {
for (TypeRefComponent type : property.getDefinition().getType()) {
String eName = baseName + Utilities.capitalize(type.getWorkingCode());
if (jp.getName().equals(eName)) {
JsonProperty jp1 = getJsonPropertyByName("_"+eName, children);
if (!isPrimitive(type.getWorkingCode()) && jp != null) {
parseChildComplex(errors, path, jp, context, property, eName, false);
break;
} else if (isPrimitive(type.getWorkingCode()) && (jp != null || jp1 != null)) {
parseChildPrimitive(errors, jp, jp1, context, property, path, eName, false);
break;
}
}
}
}
}
} else if (property.isPrimitive(property.getType(null))) {
parseChildPrimitive(errors, children, context, property, path, property.getJsonName(), property.hasJsonName());
} else if (children.containsKey(property.getJsonName())) {
parseChildComplex(errors, path, children, context, property, property.getJsonName(), property.hasJsonName());
parseChildPrimitive(errors, jp, getJsonPropertyByName("_"+property.getJsonName(), children), context, property, path, property.getJsonName(), property.hasJsonName());
} else if (jp != null) {
parseChildComplex(errors, path, jp, context, property, property.getJsonName(), property.hasJsonName());
}
}
private String getTypeFromJsonType(JsonElement je) {
if (je.isJsonPrimitive()) {
JsonPrimitive p = je.asJsonPrimitive();
@ -335,10 +369,9 @@ public class JsonParser extends ParserBase {
}
}
private void parseChildComplex(List<ValidationMessage> errors, String path, Map<String, JsonProperty> children, Element element, Property property, String name, boolean isJsonName) throws FHIRException {
private void parseChildComplex(List<ValidationMessage> errors, String path, JsonProperty p, Element element, Property property, String name, boolean isJsonName) throws FHIRException {
String npath = path+"."+property.getName();
String fpath = element.getPath()+"."+property.getName();
JsonProperty p = children.get(name);
if (p != null) { p.setTag(1); }
JsonElement e = p == null ? null : p.getValue();
if (property.isList() && !property.isJsonKeyArray() && (e instanceof JsonArray)) {
@ -533,11 +566,11 @@ public class JsonParser extends ParserBase {
return e.type().toName();
}
private void parseChildPrimitive(List<ValidationMessage> errors, Map<String, JsonProperty> children, Element element, Property property, String path, String name, boolean isJsonName) throws FHIRException {
// JsonProperty main = children.containsKey(name) ? children.get(name) : null;
// JsonProperty fork = children.containsKey("_"+name) ? children.get("_"+name) : null;
private void parseChildPrimitive(List<ValidationMessage> errors, JsonProperty main, JsonProperty fork, Element element, Property property, String path, String name, boolean isJsonName) throws FHIRException {
String npath = path+"."+property.getName();
String fpath = element.getPath()+"."+property.getName();
JsonProperty main = children.containsKey(name) ? children.get(name) : null;
JsonProperty fork = children.containsKey("_"+name) ? children.get("_"+name) : null;
if (main != null) { main.setTag(1); }
if (fork != null) { fork.setTag(1); }

View File

@ -101,6 +101,7 @@ public class NPMPackageGenerator {
private JsonObject packageManifest;
private NpmPackageIndexBuilder indexer;
private String igVersion;
private String indexdb;
public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, boolean notForPublication) throws FHIRException, IOException {
@ -315,8 +316,9 @@ public class NPMPackageGenerator {
bufferedOutputStream = new BufferedOutputStream(OutputStream);
gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream);
tar = new TarArchiveOutputStream(gzipOutputStream);
indexdb = Utilities.path("[tmp]", "tmp-"+UUID.randomUUID().toString()+".db");
indexer = new NpmPackageIndexBuilder();
indexer.start();
indexer.start(indexdb);
}
@ -383,6 +385,9 @@ public class NPMPackageGenerator {
private void buildIndexJson() throws IOException {
byte[] content = TextFile.stringToBytes(indexer.build(), false);
addFile(Category.RESOURCE, ".index.json", content);
content = TextFile.fileToBytes(indexdb);
new File(indexdb).delete();
addFile(Category.RESOURCE, ".index.db", content);
}
public String filename() {

View File

@ -992,6 +992,11 @@ public class I18nConstants {
public static final String BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH = "BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH";
public static final String BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_MULTIPLE_MATCHES = "BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_MULTIPLE_MATCHES";
public static final String BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON = "BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON";
public static final String VALIDATION_HL7_WG_NEEDED = "VALIDATION_HL7_WG_NEEDED";
public static final String VALIDATION_HL7_WG_UNKNOWN = "VALIDATION_HL7_WG_UNKNOWN";
public static final String VALIDATION_HL7_PUBLISHER_MISMATCH = "VALIDATION_HL7_PUBLISHER_MISMATCH";
public static final String VALIDATION_HL7_WG_URL = "VALIDATION_HL7_WG_URL";
public static final String VALIDATION_HL7_PUBLISHER_MISSING = "VALIDATION_HL7_PUBLISHER_MISSING";
}

View File

@ -3,16 +3,18 @@ package org.hl7.fhir.utilities.json.model;
public class JsonProperty {
private String name;
private JsonElement value;
private int nameHash; // for faster name comparison
boolean noComma; // parse in Json5 mode, but records this so the validator can complain
boolean unquotedName;
boolean unquotedValue;
private int tag;
private int tag; // used for status in validator - faster parsing
public JsonProperty(String name, JsonElement value) {
super();
this.name = name;
this.nameHash = name.hashCode();
this.value = value;
}
@ -64,5 +66,9 @@ public class JsonProperty {
public void setTag(int tag) {
this.tag = tag;
}
public int getNameHash() {
return nameHash;
}
}

View File

@ -52,6 +52,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@ -598,7 +599,7 @@ public class NpmPackage {
public void indexFolder(String desc, NpmPackageFolder folder) throws FileNotFoundException, IOException {
List<String> remove = new ArrayList<>();
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
indexer.start(folder.folder != null ? Utilities.path(folder.folder.getAbsolutePath(), ".index.db") : null);
for (String n : folder.listFiles()) {
if (!indexer.seeFile(n, folder.fetchFile(n))) {
remove.add(n);
@ -1076,7 +1077,7 @@ public class NpmPackage {
Utilities.createDirectory(pd.getAbsolutePath());
}
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
indexer.start(Utilities.path(dir.getAbsolutePath(), n, ".index.db"));
for (String s : folder.content.keySet()) {
byte[] b = folder.content.get(s);
indexer.seeFile(s, b);
@ -1112,7 +1113,8 @@ public class NpmPackage {
n = "package/"+n;
}
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
String filename = Utilities.path("[tmp]", "tmp-"+UUID.randomUUID().toString()+".db");
indexer.start(filename);
for (String s : folder.content.keySet()) {
byte[] b = folder.content.get(s);
String name = n+"/"+s;
@ -1135,6 +1137,16 @@ public class NpmPackage {
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
var file = new File(filename);
if (file.exists()) {
cnt = TextFile.fileToBytes(file);
file.delete();
entry = new TarArchiveEntry(n+"/.index.db");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
}
}
byte[] cnt = TextFile.stringToBytes(JsonParser.compose(npm, true), false);
TarArchiveEntry entry = new TarArchiveEntry("package/package.json");

View File

@ -2,6 +2,11 @@ package org.hl7.fhir.utilities.npm;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.TextFile;
@ -13,6 +18,8 @@ import org.hl7.fhir.utilities.json.parser.JsonParser;
/**
* This class builds the .index.json for a package
*
* it also builds a .index.db since that may provide faster access
*
* @author grahame
*
*/
@ -21,14 +28,42 @@ public class NpmPackageIndexBuilder {
public static final Integer CURRENT_INDEX_VERSION = 2;
private JsonObject index;
private JsonArray files;
private Connection conn;
private PreparedStatement psql;
private String dbFilename;
public void start() {
public void start(String filename) {
index = new JsonObject();
index.add("index-version", CURRENT_INDEX_VERSION);
files = new JsonArray();
index.add("files", files);
dbFilename = filename;
if (filename != null) {
try {
new File(filename).delete();
conn = DriverManager.getConnection("jdbc:sqlite:"+filename);
Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE index (\r\n"+
"FileName nvarchar NOT NULL,\r\n"+
"ResourceType nvarchar NOT NULL,\r\n"+
"Id nvarchar NULL,\r\n"+
"Url nvarchar NULL,\r\n"+
"Version nvarchar NULL,\r\n"+
"Kind nvarchar NULL,\r\n"+
"Type nvarchar NULL,\r\n"+
"Supplements nvarchar NULL,\r\n"+
"Content nvarchar NULL,\r\n"+
"PRIMARY KEY (FileName))\r\n");
psql = conn.prepareStatement("Insert into index (FileName, ResourceType, Id, Url, Version, Kind, Type, Supplements, Content) values (?, ?, ?, ?, ?, ?, ?, ?, ?)");
} catch (Exception e) {
conn = null;
}
}
}
public boolean seeFile(String name, byte[] content) {
if (name.endsWith(".json")) {
try {
@ -60,6 +95,18 @@ public class NpmPackageIndexBuilder {
if (json.hasPrimitive("content")) {
fi.add("content", json.asString("content"));
}
if (psql != null) {
psql.setString(1, name); // FileName);
psql.setString(2, json.asString("resourceType")); // ResourceType");
psql.setString(3, json.asString("id")); // Id");
psql.setString(4, json.asString("url")); // Url");
psql.setString(5, json.asString("version")); // Version");
psql.setString(6, json.asString("kind")); // Kind");
psql.setString(7, json.asString("type")); // Type");
psql.setString(8, json.asString("supplements")); // Supplements");
psql.setString(9, json.asString("content")); // Content");
psql.execute();
}
}
} catch (Exception e) {
System.out.println("Error parsing "+name+": "+e.getMessage());
@ -72,6 +119,13 @@ public class NpmPackageIndexBuilder {
}
public String build() {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
// nothing
}
String res = JsonParser.compose(index, true);
index = null;
files = null;
@ -95,7 +149,7 @@ public class NpmPackageIndexBuilder {
if (!existsFile(folder, "package.json")) {
throw new FHIRException("Not a proper package? (can't find package.json)");
}
start();
start(Utilities.path(folder, ".index.db"));
File dir = new File(folder);
for (File f : dir.listFiles()) {
seeFile(f.getName(), TextFile.fileToBytes(f));
@ -103,7 +157,6 @@ public class NpmPackageIndexBuilder {
TextFile.stringToFile(build(), Utilities.path(folder, ".index.json"));
}
private boolean existsFolder(String... args) throws IOException {
File f = new File(Utilities.path(args));
return f.exists() && f.isDirectory();
@ -114,7 +167,6 @@ public class NpmPackageIndexBuilder {
return f.exists() && !f.isDirectory();
}
public static void main(String[] args) throws IOException {
new NpmPackageIndexBuilder().executeWithStatus("C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r4.core");
new NpmPackageIndexBuilder().executeWithStatus("C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r4.examples");
@ -143,4 +195,8 @@ public class NpmPackageIndexBuilder {
new NpmPackageIndexBuilder().executeWithStatus("C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.core#4.0.1");
}
public String getDbFilename() {
return dbFilename;
}
}

View File

@ -1050,5 +1050,8 @@ VALUESET_INC_TOO_MANY_CODES = The value set include has too many codes to valida
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH = The {1} resource did not match any of the allowed profiles (Type {2}: {3})
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_MULTIPLE_MATCHES = The {1} resource matched more than one of the allowed profiles ({3})
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON = The {1} resource did not math the profile {2} because: {3}
VALIDATION_HL7_WG_NEEDED = When HL7 is publishing a resource, the owning committee must be stated using the {0} extension
VALIDATION_HL7_WG_UNKNOWN = The nominated WG ''{0}'' is unknown
VALIDATION_HL7_PUBLISHER_MISMATCH = The nominated WG ''{0}'' means that the publisher should be ''{1}'' but ''{2}'' was found
VALIDATION_HL7_WG_URL = The nominated WG ''{0}'' means that the contact url should be ''{1}'' but it was not found
VALIDATION_HL7_PUBLISHER_MISSING = When HL7 is publishing a resource, the publisher must be provided, and for WG ''{0}'' it should be ''{1}''

View File

@ -1330,6 +1330,11 @@ public class BaseValidator implements IValidationContextResourceLoader {
return url != null && url.contains("hl7");
}
protected boolean isHL7Core(Element cr) {
String url = cr.getChildValue("url");
return url != null && url.startsWith("http://hl7.org/fhir/") && !url.startsWith("http://hl7.org/fhir/test");
}
public boolean isAllowExamples() {
return this.allowExamples;
}

View File

@ -5338,7 +5338,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
public boolean checkSpecials(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) {
boolean ok = true;
long t = System.nanoTime();
try {
if (VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(element.getType())) {
@ -5350,41 +5351,137 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
hint(errors, "2023-08-14", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), statusCodesDeeplyConsistent(status, standardsStatus), I18nConstants.VALIDATION_VAL_STATUS_INCONSISTENT_HINT, status, standardsStatus);
}
}
if (isHL7Core(element)) {
ok = checkPublisherConsistency(errors, element, stack) && ok;
}
}
if (element.getType().equals(BUNDLE)) {
return new BundleValidator(this, serverBase).validateBundle(errors, element, stack, checkSpecials, hostContext, pct, mode);
return new BundleValidator(this, serverBase).validateBundle(errors, element, stack, checkSpecials, hostContext, pct, mode) && ok;
} else if (element.getType().equals("Observation")) {
return validateObservation(errors, element, stack);
return validateObservation(errors, element, stack) && ok;
} else if (element.getType().equals("Questionnaire")) {
return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaire(errors, element, element, stack);
return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaire(errors, element, element, stack) && ok;
} else if (element.getType().equals("QuestionnaireResponse")) {
return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaireResponse(hostContext, errors, element, stack);
return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaireResponse(hostContext, errors, element, stack) && ok;
} else if (element.getType().equals("Measure")) {
return new MeasureValidator(this).validateMeasure(hostContext, errors, element, stack);
return new MeasureValidator(this).validateMeasure(hostContext, errors, element, stack) && ok;
} else if (element.getType().equals("MeasureReport")) {
return new MeasureValidator(this).validateMeasureReport(hostContext, errors, element, stack);
return new MeasureValidator(this).validateMeasureReport(hostContext, errors, element, stack) && ok;
} else if (element.getType().equals("CapabilityStatement")) {
return validateCapabilityStatement(errors, element, stack);
return validateCapabilityStatement(errors, element, stack) && ok;
} else if (element.getType().equals("CodeSystem")) {
return new CodeSystemValidator(this).validateCodeSystem(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang()));
return new CodeSystemValidator(this).validateCodeSystem(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang())) && ok;
} else if (element.getType().equals("ConceptMap")) {
return new ConceptMapValidator(this).validateConceptMap(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang()));
return new ConceptMapValidator(this).validateConceptMap(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang())) && ok;
} else if (element.getType().equals("SearchParameter")) {
return new SearchParameterValidator(this, fpe).validateSearchParameter(errors, element, stack);
return new SearchParameterValidator(this, fpe).validateSearchParameter(errors, element, stack) && ok;
} else if (element.getType().equals("StructureDefinition")) {
return new StructureDefinitionValidator(this, fpe, wantCheckSnapshotUnchanged).validateStructureDefinition(errors, element, stack);
return new StructureDefinitionValidator(this, fpe, wantCheckSnapshotUnchanged).validateStructureDefinition(errors, element, stack) && ok;
} else if (element.getType().equals("StructureMap")) {
return new StructureMapValidator(this, fpe, profileUtilities).validateStructureMap(errors, element, stack);
return new StructureMapValidator(this, fpe, profileUtilities).validateStructureMap(errors, element, stack) && ok;
} else if (element.getType().equals("ValueSet")) {
return new ValueSetValidator(this).validateValueSet(errors, element, stack);
return new ValueSetValidator(this).validateValueSet(errors, element, stack) && ok;
} else {
return true;
return ok;
}
} finally {
timeTracker.spec(t);
}
}
private boolean checkPublisherConsistency(List<ValidationMessage> errors, Element element, NodeStack stack) {
String pub = element.getNamedChildValue("publisher");
Base wgT = element.getExtensionValue(ToolingExtensions.EXT_WORKGROUP);
String wg = wgT == null ? null : wgT.primitiveValue();
List<String> urls = new ArrayList<>();
for (Element c : element.getChildren("contact")) {
for (Element t : c.getChildren("telecom")) {
if ("url".equals(t.getNamedChildValue("system")) && t.getNamedChildValue("value") != null) {
urls.add(t.getNamedChildValue("value"));
}
}
}
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wg != null, I18nConstants.VALIDATION_HL7_WG_NEEDED, ToolingExtensions.EXT_WORKGROUP)) {
String name = nameForWG(wg);
String url = urlForWG(wg);
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), name != null, I18nConstants.VALIDATION_HL7_WG_UNKNOWN, wg)) {
String rpub = "HL7 International / "+name;
if (warning(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), pub != null, I18nConstants.VALIDATION_HL7_PUBLISHER_MISSING, wg, rpub)) {
warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), rpub.equals(pub), I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH, wg, rpub, pub);
}
warning(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(),
Utilities.startsWithInList(url, urls), I18nConstants.VALIDATION_HL7_WG_URL, wg, url);
return true;
}
}
return false;
}
private String urlForWG(String wg) {
switch (wg) {
case "cbcc": return "http://www.hl7.org/Special/committees/homehealth";
case "cds": return "http://www.hl7.org/Special/committees/dss";
case "cqi": return "http://www.hl7.org/Special/committees/cqi";
case "cg": return "http://www.hl7.org/Special/committees/clingenomics";
case "dev": return "http://www.hl7.org/Special/committees/healthcaredevices";
case "ehr": return "http://www.hl7.org/special/committees/ehr";
case "fhir": return "http://www.hl7.org/Special/committees/fiwg";
case "fm": return "http://www.hl7.org/Special/committees/fm";
case "hsi": return "http://www.hl7.org/Special/committees/hsi";
case "ii": return "http://www.hl7.org/Special/committees/imagemgt";
case "inm": return "http://www.hl7.org/special/committees/inm";
case "mnm": return "http://www.hl7.org/special/committees/mnm";
case "its": return "http://www.hl7.org/special/committees/xml";
case "oo": return "http://www.hl7.org/Special/committees/orders";
case "pa": return "http://www.hl7.org/Special/committees/pafm";
case "pc": return "http://www.hl7.org/Special/committees/patientcare";
case "pe": return "http://www.hl7.org/Special/committees/patientempowerment";
case "pher": return "http://www.hl7.org/Special/committees/pher";
case "phx": return "http://www.hl7.org/Special/committees/medication";
case "brr": return "http://www.hl7.org/Special/committees/rcrim";
case "sd": return "http://www.hl7.org/Special/committees/structure";
case "sec": return "http://www.hl7.org/Special/committees/secure";
case "us": return "http://www.hl7.org/Special/Committees/usrealm";
case "vocab": return "http://www.hl7.org/Special/committees/Vocab";
case "aid": return "http://www.hl7.org/Special/committees/java";
}
return null;
}
private String nameForWG(String wg) {
switch (wg) {
case "cbcc": return "Community Based Collaborative Care";
case "cds": return "Clinical Decision Support";
case "cqi": return "Clinical Quality Information";
case "cg": return "Clinical Genomics";
case "dev": return "Health Care Devices";
case "ehr": return "Electronic Health Records";
case "fhir": return "FHIR Infrastructure";
case "fm": return "Financial Management";
case "hsi": return "Health Standards Integration";
case "ii": return "Imaging Integration";
case "inm": return "Infrastructure And Messaging";
case "mnm": return "Modeling and Methodology";
case "its": return "Implementable Technology Specifications";
case "oo": return "Orders and Observations";
case "pa": return "Patient Administration";
case "pc": return "Patient Care";
case "pe": return "Patient Empowerment";
case "pher": return "Public Health and Emergency Response";
case "phx": return "Pharmacy";
case "brr": return "Biomedical Research and Regulation";
case "sd": return "Structured Documents";
case "sec": return "Security";
case "us": return "US Realm Taskforce";
case "vocab": return "Terminology Infrastructure";
case "aid": return "Application Implementation and Design";
}
return null;
}
private boolean statusCodesConsistent(String status, String standardsStatus) {
switch (standardsStatus) {
case "draft": return Utilities.existsInList(status, "draft");

View File

@ -20,7 +20,7 @@
<properties>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.4.4</validator_test_case_version>
<validator_test_case_version>1.4.5-SNAPSHOT</validator_test_case_version>
<jackson_version>2.15.2</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>