load dependency Packages, and load content from URL correctly

This commit is contained in:
Grahame Grieve 2019-11-11 14:20:52 +11:00
parent 13da3db00d
commit 52b80cf71c
4 changed files with 531 additions and 496 deletions

View File

@ -118,4 +118,11 @@ public class VersionUtilities {
return v1.substring(0, 3).equals(v2.substring(0, 3));
}
public static boolean isCorePackage(String s) {
if (s.contains("#")) {
s = s.substring(0, s.indexOf("#"));
}
return Utilities.existsInList(s, "hl7.fhir.core","hl7.fhir.r2.core", "hl7.fhir.r2b.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core");
}
}

View File

@ -62,486 +62,497 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* info and loader for a package
*
* Packages may exist on disk in the cache, or purely in memory when they are loaded on the fly
*
* Packages are contained in subfolders (see the package spec). The FHIR resources will be in "package"
*
* @author Grahame Grieve
*
*/
public class NpmPackage {
* info and loader for a package
*
* Packages may exist on disk in the cache, or purely in memory when they are loaded on the fly
*
* Packages are contained in subfolders (see the package spec). The FHIR resources will be in "package"
*
* @author Grahame Grieve
*
*/
public class NpmPackage {
private String path;
private List<String> folders = new ArrayList<String>();
private Map<String, byte[]> content = new HashMap<String, byte[]>();
private JsonObject npm;
private IniFile cache;
private Map<String, List<String>> types = new HashMap<>();
private Map<String, String> urls = new HashMap<>();
private String path;
private List<String> folders = new ArrayList<String>();
private Map<String, byte[]> content = new HashMap<String, byte[]>();
private JsonObject npm;
private IniFile cache;
private Map<String, List<String>> types = new HashMap<>();
private Map<String, String> urls = new HashMap<>();
public NpmPackage(JsonObject npm, Map<String, byte[]> content, List<String> folders) {
this.path = null;
this.content = content;
this.npm = npm;
this.folders = folders;
}
public NpmPackage(String path) throws IOException {
this.path = path;
if (path != null) {
for (String f : sorted(new File(path).list())) {
if (new File(Utilities.path(path, f)).isDirectory()) {
folders.add(f);
}
}
npm = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(Utilities.path(path, "package", "package.json")));
File ji = new File(Utilities.path(path, "package", ".index.json"));
if (ji.exists()) {
readIndexFile((JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(ji)));
}
cache = new IniFile(Utilities.path(path, "cache.ini"));
}
}
private void readIndexFile(JsonObject index) throws IOException {
for (JsonElement e : index.getAsJsonArray("files")) {
JsonObject file = (JsonObject) e;
String type = file.get("resourceType").getAsString();
String name = file.get("filename").getAsString();
String url = file.has("url") ? file.get("url").getAsString() : null;
String version = file.has("version") ? file.get("version").getAsString() : null;
if (!types.containsKey(type))
types.put(type, new ArrayList<>());
types.get(type).add(name);
if (version != null)
url = url + "|" + version;
urls.put(url, name);
}
}
public NpmPackage(JsonObject npm, Map<String, byte[]> content, List<String> folders) {
this.path = null;
this.content = content;
this.npm = npm;
this.folders = folders;
}
private List<String> sorted(String[] keys) {
List<String> names = new ArrayList<String>();
for (String s : keys)
names.add(s);
Collections.sort(names);
return names;
}
private static final int BUFFER_SIZE = 1024;
public static NpmPackage fromPackage(InputStream tgz) throws IOException {
return fromPackage(tgz, false);
}
public static NpmPackage fromPackage(InputStream tgz, boolean progress) throws IOException {
NpmPackage res = new NpmPackage(null);
boolean hasIndex = false;
GzipCompressorInputStream gzipIn = new GzipCompressorInputStream(tgz);
try (TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn)) {
TarArchiveEntry entry;
int i = 0;
int c = 12;
while ((entry = (TarArchiveEntry) tarIn.getNextEntry()) != null) {
i++;
if (entry.isDirectory()) {
res.folders.add(entry.getName());
} else {
hasIndex = hasIndex || "package/.index.json".equals(entry.getName());
int count;
byte data[] = new byte[BUFFER_SIZE];
ByteArrayOutputStream fos = new ByteArrayOutputStream();
try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) {
while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) {
dest.write(data, 0, count);
}
}
fos.close();
res.content.put(entry.getName(), fos.toByteArray());
}
i++;
if (progress && i % 50 == 0) {
c++;
System.out.print(".");
if (c == 120) {
System.out.println("");
System.out.print(" ");
c = 2;
}
}
public NpmPackage(String path) throws IOException {
this.path = path;
if (path != null) {
for (String f : sorted(new File(path).list())) {
if (new File(Utilities.path(path, f)).isDirectory()) {
folders.add(f);
}
}
if (!hasIndex) {
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
for (String n : res.getContent().keySet()) {
if (n.startsWith("package/")) {
indexer.seeFile(n.substring(8), res.content.get(n));
}
}
res.content.put("package/.index.json", indexer.build().getBytes(Charsets.UTF_8));
npm = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(Utilities.path(path, "package", "package.json")));
File ji = new File(Utilities.path(path, "package", ".index.json"));
if (ji.exists()) {
readIndexFile((JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(ji)));
}
res.npm = JsonTrackingParser.parseJson(res.content.get("package/package.json"));
res.readIndexFile((JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/.index.json"))));
return res;
}
public static NpmPackage fromZip(InputStream stream, boolean dropRootFolder) throws IOException {
NpmPackage res = new NpmPackage(null);
ZipInputStream zip = new ZipInputStream(stream);
ZipEntry ze;
while ((ze = zip.getNextEntry()) != null) {
int size;
byte[] buffer = new byte[2048];
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(bytes, buffer.length);
while ((size = zip.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, size);
}
bos.flush();
bos.close();
if (bytes.size() > 0)
if (dropRootFolder)
res.content.put(ze.getName().substring(ze.getName().indexOf("/")+1), bytes.toByteArray());
else
res.content.put(ze.getName(), bytes.toByteArray());
zip.closeEntry();
}
zip.close();
res.npm = (JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/package.json")));
if (res.content.containsKey("package/.index.json"))
res.readIndexFile((JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/.index.json"))));
return res;
}
/**
* Accessing the contents of the package - get a list of files in a subfolder of the package
*
* @param folder
* @return
* @throws IOException
*/
public List<String> list(String folder) throws IOException {
List<String> res = new ArrayList<String>();
if (path != null) {
File f = new File(Utilities.path(path, folder));
if (f.exists() && f.isDirectory())
for (String s : f.list())
res.add(s);
} else {
for (String s : content.keySet()) {
if (s.startsWith(folder+"/") && !s.substring(folder.length()+2).contains("/"))
res.add(s.substring(folder.length()+1));
}
}
return res;
}
public List<String> listResources(String... types) throws IOException {
List<String> res = new ArrayList<String>();
if (this.types.size() > 0) {
for (String s : types) {
if (this.types.containsKey(s))
res.addAll(this.types.get(s));
}
} else {
List<String> files = list("package");
for (String s : files) {
String[] n = s.split("\\-");
if (Utilities.existsInList(n[0], types))
res.add(s);
}
}
return res;
}
/**
* Copies all the files in the package folder [folder] to the nominated dest,
* and returns a list of all the file names copied
*
* @param folder
* @return
* @throws IOException
*/
public List<String> copyTo(String folder, String dest) throws IOException {
List<String> res = new ArrayList<String>();
if (path != null) {
copyToDest(Utilities.path(path, folder), Utilities.path(path, folder), dest, res);
} else {
for (Entry<String, byte[]> e : content.entrySet()) {
if (e.getKey().startsWith(folder+"/")) {
String s = e.getKey().substring(folder.length()+1);
res.add(s);
String dst = Utilities.path(dest, s);
String dstDir = Utilities.getDirectoryForFile(dst);
Utilities.createDirectory(dstDir);
TextFile.bytesToFile(e.getValue(), dst);
}
}
}
return res;
}
private void copyToDest(String base, String folder, String dest, List<String> res) throws IOException {
for (File f : new File(folder).listFiles()) {
if (f.isDirectory()) {
copyToDest(base, f.getAbsolutePath(), Utilities.path(dest, f.getName()), res);
} else {
String dst = Utilities.path(dest, f.getName());
FileUtils.copyFile(f, new File(dst), true);
res.add(f.getAbsolutePath().substring(base.length()+1));
}
}
}
/**
* get a stream that contains the contents of one of the files in a folder
*
* @param folder
* @param file
* @return
* @throws IOException
*/
public InputStream load(String folder, String file) throws IOException {
if (content.containsKey(folder+"/"+file))
return new ByteArrayInputStream(content.get(folder+"/"+file));
else {
File f = new File(Utilities.path(path, folder, file));
if (f.exists())
return new FileInputStream(f);
throw new IOException("Unable to find the file "+folder+"/"+file+" in the package "+name());
}
}
/**
* Handle to the package json file
*
* @return
*/
public JsonObject getNpm() {
return npm;
}
/**
* convenience method for getting the package name
* @return
*/
public String name() {
return npm.get("name").getAsString();
}
public String date() {
return npm.has("date") ? npm.get("date").getAsString() : null;
}
public String canonical() {
return npm.has("canonical") ? npm.get("canonical").getAsString() : null;
}
/**
* convenience method for getting the package version
* @return
*/
public String version() {
return npm.get("version").getAsString();
}
/**
* convenience method for getting the package fhir version
* @return
*/
public String fhirVersion() {
if ("hl7.fhir.core".equals(npm.get("name").getAsString()))
return npm.get("version").getAsString();
else if (npm.get("name").getAsString().startsWith("hl7.fhir.r2.") || npm.get("name").getAsString().startsWith("hl7.fhir.r2b.") || npm.get("name").getAsString().startsWith("hl7.fhir.r3.") || npm.get("name").getAsString().startsWith("hl7.fhir.r4.") || npm.get("name").getAsString().startsWith("hl7.fhir.r5."))
return npm.get("version").getAsString();
else {
JsonObject dep = npm.getAsJsonObject("dependencies");
if (dep == null)
throw new FHIRException("no dependencies found in the Package definition");
for (Entry<String, JsonElement> e : dep.entrySet()) {
if (Utilities.existsInList(e.getKey(), "hl7.fhir.r2.core", "hl7.fhir.r2b.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core"))
return e.getValue().getAsString();
if (Utilities.existsInList(e.getKey(), "hl7.fhir.core")) // while all packages are updated
return e.getValue().getAsString();
}
if (npm.has("fhirVersions")) {
return npm.getAsJsonArray("fhirVersions").get(0).getAsString();
}
// legacy simplifier support:
if (dep.has("simplifier.core.r4"))
return "4.0";
if (dep.has("simplifier.core.r3"))
return "3.0";
if (dep.has("simplifier.core.r2"))
return "2.0";
throw new FHIRException("no core dependency or FHIR Version found in the Package definition");
}
}
public String summary() {
if (path != null)
return path;
else
return "memory";
}
public boolean isType(PackageType template) {
return template.getCode().equals(type());
}
public String type() {
return npm.get("type").getAsString();
}
public String description() {
return npm.has("description") ? npm.get("description").getAsString() : null;
}
public String getPath() {
return path;
}
public IniFile getCache() {
return cache;
}
/**
* only for use by the package manager itself
*
* @param path
*/
public void setPath(String path) {
this.path = path;
}
public String getWebLocation() {
if (npm.has("url"))
return npm.get("url").getAsString();
else
return npm.get("canonical").getAsString();
}
public boolean hasFile(String folder, String name) throws IOException {
String file = folder+"/"+name;
return content.containsKey(file);
}
public InputStream loadResource(String type, String id) throws IOException {
String file = type+"-"+id+".json";
if (content.containsKey("package/"+file))
return new ByteArrayInputStream(content.get("package/"+file));
else {
File f = new File(Utilities.path(path, "package", file));
if (f.exists())
return new FileInputStream(f);
else
return null;
}
}
/** special case when playing around inside the package **/
public Map<String, byte[]> getContent() {
return content;
}
public static NpmPackage fromFolder(String folder, String... exemptions) throws IOException {
return fromFolder(folder, null, exemptions);
}
public static NpmPackage fromFolder(String folder, PackageType defType, String... exemptions) throws IOException {
NpmPackage res = new NpmPackage(null);
loadFiles(res, folder, new File(folder), exemptions);
if (!res.content.containsKey("package/package.json") && defType != null)
res.content.put("package/package.json", TextFile.stringToBytes("{ \"type\" : \""+defType.getCode()+"\"}", false));
res.npm = (JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/package.json")));
return res;
}
private static void loadFiles(NpmPackage res, String base, File folder, String... exemptions) throws FileNotFoundException, IOException {
for (File f : folder.listFiles()) {
if (!f.getName().equals(".git") || !Utilities.existsInList(f.getName(), exemptions)) {
if (f.isDirectory())
loadFiles(res, base, f);
else {
String name = f.getAbsolutePath().substring(base.length()+1).replace("\\", "/");
byte[] cnt = TextFile.fileToBytes(f);
res.content.put(name, cnt);
}
}
}
}
public void unPack(String dir) throws IOException {
for (String s : content.keySet()) {
String fn = Utilities.path(dir, s);
String dn = Utilities.getDirectoryForFile(fn);
Utilities.createDirectory(dn);
TextFile.bytesToFile(content.get(s), fn);
}
if (path != null)
FileUtils.copyDirectory(new File(path), new File(dir));
}
public void save(OutputStream stream) throws IOException {
TarArchiveOutputStream tar;
ByteArrayOutputStream OutputStream;
BufferedOutputStream bufferedOutputStream;
GzipCompressorOutputStream gzipOutputStream;
OutputStream = new ByteArrayOutputStream();
bufferedOutputStream = new BufferedOutputStream(OutputStream);
gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream);
tar = new TarArchiveOutputStream(gzipOutputStream);
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
for (String s : content.keySet()) {
byte[] b = content.get(s);
if (s.startsWith("package/")) {
indexer.seeFile(tail(s), b);
}
if (!s.endsWith(".index.json") && !s.equals("package/package.json")) {
TarArchiveEntry entry = new TarArchiveEntry(s);
entry.setSize(b.length);
tar.putArchiveEntry(entry);
tar.write(b);
tar.closeArchiveEntry();
}
}
byte[] cnt = new GsonBuilder().setPrettyPrinting().create().toJson(npm).getBytes(Charset.forName("UTF-8"));
TarArchiveEntry entry = new TarArchiveEntry("package/package.json");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
cnt = indexer.build().getBytes(Charset.forName("UTF-8"));
entry = new TarArchiveEntry("package/.index.json");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
tar.finish();
tar.close();
gzipOutputStream.close();
bufferedOutputStream.close();
OutputStream.close();
byte[] b = OutputStream.toByteArray();
stream.write(b);
}
private String tail(String s) {
return s.substring(s.lastIndexOf("/")+1);
cache = new IniFile(Utilities.path(path, "cache.ini"));
}
}
private void readIndexFile(JsonObject index) throws IOException {
for (JsonElement e : index.getAsJsonArray("files")) {
JsonObject file = (JsonObject) e;
String type = file.get("resourceType").getAsString();
String name = file.get("filename").getAsString();
String url = file.has("url") ? file.get("url").getAsString() : null;
String version = file.has("version") ? file.get("version").getAsString() : null;
if (!types.containsKey(type))
types.put(type, new ArrayList<>());
types.get(type).add(name);
if (version != null)
url = url + "|" + version;
urls.put(url, name);
}
}
private List<String> sorted(String[] keys) {
List<String> names = new ArrayList<String>();
for (String s : keys)
names.add(s);
Collections.sort(names);
return names;
}
private static final int BUFFER_SIZE = 1024;
public static NpmPackage fromPackage(InputStream tgz) throws IOException {
return fromPackage(tgz, false);
}
public static NpmPackage fromPackage(InputStream tgz, boolean progress) throws IOException {
NpmPackage res = new NpmPackage(null);
boolean hasIndex = false;
GzipCompressorInputStream gzipIn = new GzipCompressorInputStream(tgz);
try (TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn)) {
TarArchiveEntry entry;
int i = 0;
int c = 12;
while ((entry = (TarArchiveEntry) tarIn.getNextEntry()) != null) {
i++;
if (entry.isDirectory()) {
res.folders.add(entry.getName());
} else {
hasIndex = hasIndex || "package/.index.json".equals(entry.getName());
int count;
byte data[] = new byte[BUFFER_SIZE];
ByteArrayOutputStream fos = new ByteArrayOutputStream();
try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) {
while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) {
dest.write(data, 0, count);
}
}
fos.close();
res.content.put(entry.getName(), fos.toByteArray());
}
i++;
if (progress && i % 50 == 0) {
c++;
System.out.print(".");
if (c == 120) {
System.out.println("");
System.out.print(" ");
c = 2;
}
}
}
}
if (!hasIndex) {
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
for (String n : res.getContent().keySet()) {
if (n.startsWith("package/")) {
indexer.seeFile(n.substring(8), res.content.get(n));
}
}
res.content.put("package/.index.json", indexer.build().getBytes(Charsets.UTF_8));
}
res.npm = JsonTrackingParser.parseJson(res.content.get("package/package.json"));
res.readIndexFile((JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/.index.json"))));
return res;
}
public static NpmPackage fromZip(InputStream stream, boolean dropRootFolder) throws IOException {
NpmPackage res = new NpmPackage(null);
ZipInputStream zip = new ZipInputStream(stream);
ZipEntry ze;
while ((ze = zip.getNextEntry()) != null) {
int size;
byte[] buffer = new byte[2048];
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(bytes, buffer.length);
while ((size = zip.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, size);
}
bos.flush();
bos.close();
if (bytes.size() > 0)
if (dropRootFolder)
res.content.put(ze.getName().substring(ze.getName().indexOf("/")+1), bytes.toByteArray());
else
res.content.put(ze.getName(), bytes.toByteArray());
zip.closeEntry();
}
zip.close();
res.npm = (JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/package.json")));
if (res.content.containsKey("package/.index.json"))
res.readIndexFile((JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/.index.json"))));
return res;
}
/**
* Accessing the contents of the package - get a list of files in a subfolder of the package
*
* @param folder
* @return
* @throws IOException
*/
public List<String> list(String folder) throws IOException {
List<String> res = new ArrayList<String>();
if (path != null) {
File f = new File(Utilities.path(path, folder));
if (f.exists() && f.isDirectory())
for (String s : f.list())
res.add(s);
} else {
for (String s : content.keySet()) {
if (s.startsWith(folder+"/") && !s.substring(folder.length()+2).contains("/"))
res.add(s.substring(folder.length()+1));
}
}
return res;
}
public List<String> listResources(String... types) throws IOException {
List<String> res = new ArrayList<String>();
if (this.types.size() > 0) {
for (String s : types) {
if (this.types.containsKey(s))
res.addAll(this.types.get(s));
}
} else {
List<String> files = list("package");
for (String s : files) {
String[] n = s.split("\\-");
if (Utilities.existsInList(n[0], types))
res.add(s);
}
}
return res;
}
/**
* Copies all the files in the package folder [folder] to the nominated dest,
* and returns a list of all the file names copied
*
* @param folder
* @return
* @throws IOException
*/
public List<String> copyTo(String folder, String dest) throws IOException {
List<String> res = new ArrayList<String>();
if (path != null) {
copyToDest(Utilities.path(path, folder), Utilities.path(path, folder), dest, res);
} else {
for (Entry<String, byte[]> e : content.entrySet()) {
if (e.getKey().startsWith(folder+"/")) {
String s = e.getKey().substring(folder.length()+1);
res.add(s);
String dst = Utilities.path(dest, s);
String dstDir = Utilities.getDirectoryForFile(dst);
Utilities.createDirectory(dstDir);
TextFile.bytesToFile(e.getValue(), dst);
}
}
}
return res;
}
private void copyToDest(String base, String folder, String dest, List<String> res) throws IOException {
for (File f : new File(folder).listFiles()) {
if (f.isDirectory()) {
copyToDest(base, f.getAbsolutePath(), Utilities.path(dest, f.getName()), res);
} else {
String dst = Utilities.path(dest, f.getName());
FileUtils.copyFile(f, new File(dst), true);
res.add(f.getAbsolutePath().substring(base.length()+1));
}
}
}
/**
* get a stream that contains the contents of one of the files in a folder
*
* @param folder
* @param file
* @return
* @throws IOException
*/
public InputStream load(String folder, String file) throws IOException {
if (content.containsKey(folder+"/"+file))
return new ByteArrayInputStream(content.get(folder+"/"+file));
else {
File f = new File(Utilities.path(path, folder, file));
if (f.exists())
return new FileInputStream(f);
throw new IOException("Unable to find the file "+folder+"/"+file+" in the package "+name());
}
}
/**
* Handle to the package json file
*
* @return
*/
public JsonObject getNpm() {
return npm;
}
/**
* convenience method for getting the package name
* @return
*/
public String name() {
return npm.get("name").getAsString();
}
public String date() {
return npm.has("date") ? npm.get("date").getAsString() : null;
}
public String canonical() {
return npm.has("canonical") ? npm.get("canonical").getAsString() : null;
}
/**
* convenience method for getting the package version
* @return
*/
public String version() {
return npm.get("version").getAsString();
}
/**
* convenience method for getting the package fhir version
* @return
*/
public String fhirVersion() {
if ("hl7.fhir.core".equals(npm.get("name").getAsString()))
return npm.get("version").getAsString();
else if (npm.get("name").getAsString().startsWith("hl7.fhir.r2.") || npm.get("name").getAsString().startsWith("hl7.fhir.r2b.") || npm.get("name").getAsString().startsWith("hl7.fhir.r3.") || npm.get("name").getAsString().startsWith("hl7.fhir.r4.") || npm.get("name").getAsString().startsWith("hl7.fhir.r5."))
return npm.get("version").getAsString();
else {
JsonObject dep = npm.getAsJsonObject("dependencies");
if (dep == null)
throw new FHIRException("no dependencies found in the Package definition");
for (Entry<String, JsonElement> e : dep.entrySet()) {
if (Utilities.existsInList(e.getKey(), "hl7.fhir.r2.core", "hl7.fhir.r2b.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core"))
return e.getValue().getAsString();
if (Utilities.existsInList(e.getKey(), "hl7.fhir.core")) // while all packages are updated
return e.getValue().getAsString();
}
if (npm.has("fhirVersions")) {
return npm.getAsJsonArray("fhirVersions").get(0).getAsString();
}
// legacy simplifier support:
if (dep.has("simplifier.core.r4"))
return "4.0";
if (dep.has("simplifier.core.r3"))
return "3.0";
if (dep.has("simplifier.core.r2"))
return "2.0";
throw new FHIRException("no core dependency or FHIR Version found in the Package definition");
}
}
public String summary() {
if (path != null)
return path;
else
return "memory";
}
public boolean isType(PackageType template) {
return template.getCode().equals(type());
}
public String type() {
return npm.get("type").getAsString();
}
public String description() {
return npm.has("description") ? npm.get("description").getAsString() : null;
}
public String getPath() {
return path;
}
public IniFile getCache() {
return cache;
}
/**
* only for use by the package manager itself
*
* @param path
*/
public void setPath(String path) {
this.path = path;
}
public String getWebLocation() {
if (npm.has("url"))
return npm.get("url").getAsString();
else
return npm.get("canonical").getAsString();
}
public boolean hasFile(String folder, String name) throws IOException {
String file = folder+"/"+name;
return content.containsKey(file);
}
public InputStream loadResource(String type, String id) throws IOException {
String file = type+"-"+id+".json";
if (content.containsKey("package/"+file))
return new ByteArrayInputStream(content.get("package/"+file));
else {
File f = new File(Utilities.path(path, "package", file));
if (f.exists())
return new FileInputStream(f);
else
return null;
}
}
/** special case when playing around inside the package **/
public Map<String, byte[]> getContent() {
return content;
}
public static NpmPackage fromFolder(String folder, String... exemptions) throws IOException {
return fromFolder(folder, null, exemptions);
}
public static NpmPackage fromFolder(String folder, PackageType defType, String... exemptions) throws IOException {
NpmPackage res = new NpmPackage(null);
loadFiles(res, folder, new File(folder), exemptions);
if (!res.content.containsKey("package/package.json") && defType != null)
res.content.put("package/package.json", TextFile.stringToBytes("{ \"type\" : \""+defType.getCode()+"\"}", false));
res.npm = (JsonObject) new com.google.gson.JsonParser().parse(new String(res.content.get("package/package.json")));
return res;
}
private static void loadFiles(NpmPackage res, String base, File folder, String... exemptions) throws FileNotFoundException, IOException {
for (File f : folder.listFiles()) {
if (!f.getName().equals(".git") || !Utilities.existsInList(f.getName(), exemptions)) {
if (f.isDirectory())
loadFiles(res, base, f);
else {
String name = f.getAbsolutePath().substring(base.length()+1).replace("\\", "/");
byte[] cnt = TextFile.fileToBytes(f);
res.content.put(name, cnt);
}
}
}
}
public void unPack(String dir) throws IOException {
for (String s : content.keySet()) {
String fn = Utilities.path(dir, s);
String dn = Utilities.getDirectoryForFile(fn);
Utilities.createDirectory(dn);
TextFile.bytesToFile(content.get(s), fn);
}
if (path != null)
FileUtils.copyDirectory(new File(path), new File(dir));
}
public void save(OutputStream stream) throws IOException {
TarArchiveOutputStream tar;
ByteArrayOutputStream OutputStream;
BufferedOutputStream bufferedOutputStream;
GzipCompressorOutputStream gzipOutputStream;
OutputStream = new ByteArrayOutputStream();
bufferedOutputStream = new BufferedOutputStream(OutputStream);
gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream);
tar = new TarArchiveOutputStream(gzipOutputStream);
NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder();
indexer.start();
for (String s : content.keySet()) {
byte[] b = content.get(s);
if (s.startsWith("package/")) {
indexer.seeFile(tail(s), b);
}
if (!s.endsWith(".index.json") && !s.equals("package/package.json")) {
TarArchiveEntry entry = new TarArchiveEntry(s);
entry.setSize(b.length);
tar.putArchiveEntry(entry);
tar.write(b);
tar.closeArchiveEntry();
}
}
byte[] cnt = new GsonBuilder().setPrettyPrinting().create().toJson(npm).getBytes(Charset.forName("UTF-8"));
TarArchiveEntry entry = new TarArchiveEntry("package/package.json");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
cnt = indexer.build().getBytes(Charset.forName("UTF-8"));
entry = new TarArchiveEntry("package/.index.json");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);
tar.write(cnt);
tar.closeArchiveEntry();
tar.finish();
tar.close();
gzipOutputStream.close();
bufferedOutputStream.close();
OutputStream.close();
byte[] b = OutputStream.toByteArray();
stream.write(b);
}
private String tail(String s) {
return s.substring(s.lastIndexOf("/")+1);
}
public List<String> dependencies() {
List<String> res = new ArrayList<>();
if (npm.has("dependencies")) {
for (Entry<String, JsonElement> e : npm.getAsJsonObject("dependencies").entrySet()) {
res.add(e.getKey()+"#"+e.getValue().getAsString());
}
}
return res;
}
}

View File

@ -149,6 +149,7 @@ import org.hl7.fhir.r5.utils.ValidationProfileSet;
import org.hl7.fhir.utilities.IniFile;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.cache.NpmPackage;
import org.hl7.fhir.utilities.cache.PackageCacheManager;
import org.hl7.fhir.utilities.cache.ToolsVersion;
@ -310,6 +311,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
private PackageCacheManager pcm;
private PrintWriter mapLog;
private boolean debug;
private Set<String> loadedIgs = new HashSet<>();
private class AsteriskFilter implements FilenameFilter {
String dir;
@ -403,7 +405,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
private void loadDefinitions(String src, boolean recursive) throws Exception {
Map<String, byte[]> source = loadIgSource(src, recursive);
Map<String, byte[]> source = loadIgSource(src, recursive, true);
if (version == null)
version = getVersionFromPack(source);
context = SimpleWorkerContext.fromDefinitions(source, loaderForVersion());
@ -478,7 +480,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return TextFile.fileToBytes(src);
}
private Map<String, byte[]> loadIgSource(String src, boolean recursive) throws Exception {
/** explore should be true if we're trying to load an -ig parameter, and false if we're loading source **/
private Map<String, byte[]> loadIgSource(String src, boolean recursive, boolean explore) throws Exception {
// src can be one of the following:
// - a canonical url for an ig - this will be converted to a package id and loaded into the cache
// - a package id for an ig - this will be loaded into the cache
@ -494,7 +498,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
if (!Utilities.noString(pid))
return fetchByPackage(pid+(v == null ? "" : "#"+v));
else
return fetchFromUrl(src+(v == null ? "" : "|"+v));
return fetchFromUrl(src+(v == null ? "" : "|"+v), explore);
}
File f = new File(src);
@ -526,7 +530,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
private Map<String, byte[]> fetchFromUrl(String src) throws Exception {
private Map<String, byte[]> fetchFromUrl(String src, boolean explore) throws Exception {
if (src.endsWith(".tgz"))
return loadPackage(fetchFromUrlSpecific(src, false), src);
if (src.endsWith(".pack"))
@ -534,18 +538,21 @@ public class ValidationEngine implements IValidatorResourceFetcher {
if (src.endsWith("igpack.zip"))
return readZip(fetchFromUrlSpecific(src, false));
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(src, "package.tgz"), true);
if (stream != null)
return loadPackage(stream, Utilities.pathURL(src, "package.tgz"));
// todo: these options are deprecated - remove once all IGs have been rebuilt post R4 technical correction
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "igpack.zip"), true);
if (stream != null)
return readZip(stream);
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
if (stream != null)
return readZip(stream);
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
//// -----
InputStream stream = null;
if (explore) {
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "package.tgz"), true);
if (stream != null)
return loadPackage(stream, Utilities.pathURL(src, "package.tgz"));
// todo: these options are deprecated - remove once all IGs have been rebuilt post R4 technical correction
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "igpack.zip"), true);
if (stream != null)
return readZip(stream);
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
if (stream != null)
return readZip(stream);
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
//// -----
}
// ok, having tried all that... now we'll just try to access it directly
byte[] cnt;
@ -612,11 +619,21 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
private Map<String, byte[]> loadPackage(InputStream stream, String name) throws FileNotFoundException, IOException {
private Map<String, byte[]> loadPackage(InputStream stream, String name) throws Exception {
return loadPackage(NpmPackage.fromPackage(stream));
}
public Map<String, byte[]> loadPackage(NpmPackage pi) throws IOException {
public Map<String, byte[]> loadPackage(NpmPackage pi) throws Exception {
loadedIgs.add(pi.name()+"#"+pi.version());
for (String s : pi.dependencies()) {
if (!loadedIgs.contains(s)) {
if (!VersionUtilities.isCorePackage(s)) {
System.out.println("+ .. load IG from "+s);
fetchByPackage(s);
}
}
}
Map<String, byte[]> res = new HashMap<String, byte[]>();
for (String s : pi.listResources("CodeSystem", "ConceptMap", "ImplementationGuide", "CapabilityStatement", "Conformance", "StructureMap", "ValueSet", "StructureDefinition")) {
res.put(s, TextFile.streamToBytes(pi.load("package", s)));
@ -772,7 +789,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
public void loadIg(String src, boolean recursive) throws IOException, FHIRException, Exception {
String canonical = null;
Map<String, byte[]> source = loadIgSource(src, recursive);
Map<String, byte[]> source = loadIgSource(src, recursive, true);
String version = Constants.VERSION;
if (this.version != null)
version = this.version;
@ -893,7 +910,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
public Content loadContent(String source, String opName) throws Exception {
Map<String, byte[]> s = loadIgSource(source, false);
Map<String, byte[]> s = loadIgSource(source, false, false);
Content res = new Content();
if (s.size() != 1)
throw new Exception("Unable to find resource " + source + " to "+opName);

View File

@ -513,7 +513,7 @@ public class Validator {
validator.setVersion(sv);
for (String src : igs) {
System.out.println("+ .. load IG from "+src);
validator.loadIg(src, recursive);
validator.loadIg(src, recursive);
}
validator.setQuestionnaires(questionnaires);
validator.setNative(doNative);