Merge branch 'master' into ja_20200524_npm_rework

This commit is contained in:
jamesagnew 2020-05-30 12:07:17 -04:00
commit 17411a3659
81 changed files with 3886 additions and 2637 deletions

1
RELEASE_NOTES.md Normal file
View File

@ -0,0 +1 @@

View File

@ -1,16 +1,20 @@
trigger:
branches:
include:
- '*'
- master
- release
pr:
- master
- release
# Different users have different machine setups, we run the build three times, on ubuntu, osx, and windows
strategy:
matrix:
linux:
imageName: "ubuntu-16.04"
# mac:
# imageName: "macos-10.14"
# windows:
# imageName: "vs2017-win2016"
mac:
imageName: "macos-10.14"
windows:
imageName: "vs2017-win2016"
maxParallel: 3
pool:
@ -19,18 +23,47 @@ pool:
variables:
currentImage: $(imageName)
codecov: $(CODECOV_TOKEN)
VERSION:
steps:
- bash: ls -la
- bash: pwd
# This task pulls the <version> value from the org.hl7.fhir.r5 project pom.xml file. All modules are released as
# the same version, at the same time, as defined in the root level pom.xml.
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
[xml]$pomXml = Get-Content .\pom.xml
[xml]$pomXml = Get-Content -Path .\pom.xml
# version
Write-Host $pomXml.project.version
$version=$pomXml.project.version
Write-Host "##vso[task.setvariable variable=version]$version"
# Prints out the build version, for debugging purposes
- bash: echo Pulled version from pom.xml => $(version)
# Azure pipelines cannot pass variables between pipelines, but it can pass files, so we
# pass the build id (ex: 1.1.13-SNAPSHOT) as a string in a file.
# This is used in the release pipeline, so we create it here.
# This is only done for the release branch.
- bash: |
echo $(version)
VERSION=$(version)
echo "$VERSION" > $(System.DefaultWorkingDirectory)/VERSION
condition: and(eq(variables.currentImage, 'ubuntu-16.04'), eq(variables['Build.SourceBranch'], 'refs/heads/release'))
# Copies the VERSION file containing the build id (ex: 1.1.13-SNAPSHOT) to the staging directory
# This is done for release versions only.
- task: CopyFiles@2
condition: and(eq(variables.currentImage, 'ubuntu-16.04'), eq(variables['Build.SourceBranch'], 'refs/heads/release'))
displayName: 'Copy Files to: $(build.artifactstagingdirectory)'
inputs:
SourceFolder: '$(System.Defaultworkingdirectory)'
Contents: "$(System.DefaultWorkingDirectory)/VERSION"
TargetFolder: '$(build.artifactstagingdirectory)'
# Runs 'mvn clean package'
- task: Maven@3
inputs:
mavenPomFile: 'pom.xml'
@ -41,22 +74,34 @@ steps:
publishJUnitResults: true
testResultsFiles: '**/surefire-reports/TEST-*.xml'
goals: 'clean package'
- bash: echo Current version => $(version)
displayName: 'version'
# Upload test results to codecov
- script: bash <(curl https://codecov.io/bash) -t $(codecov)
displayName: 'codecov'
displayName: 'codecov Bash Uploader'
# Publishes the test results to build artifacts.
- task: PublishCodeCoverageResults@1
displayName: 'Publish JaCoCo test results '
inputs:
codeCoverageTool: 'JaCoCo'
summaryFileLocation: '$(System.DefaultWorkingDirectory)/org.hl7.fhir.report/target/site/jacoco-aggregate/jacoco.xml'
reportDirectory: '$(System.DefaultWorkingDirectory)/org.hl7.fhir.report/target/site/jacoco-aggregate/'
# Publishes the built Validator jar to build artifacts. Primarily for testing and debugging builds.
- task: PublishPipelineArtifact@1
condition: eq(variables.currentImage, 'ubuntu-16.04')
displayName: 'Publish Validator jar'
condition: and(eq(variables.currentImage, 'ubuntu-16.04'), eq(variables['Build.SourceBranch'], 'refs/heads/release'))
inputs:
targetPath: "$(System.DefaultWorkingDirectory)/org.hl7.fhir.validation/target/org.hl7.fhir.validation-$(version).jar"
artifactName: Validator
# Publishes the files we've moved into the staging directory, so they can be accessed by the
# release pipeline. You will notice that we only do this for the ubuntu build, as doing it
# for each of the three release pipelines will cause conflicts.
# This is done for release versions only.
- task: PublishBuildArtifacts@1
condition: and(eq(variables.currentImage, 'ubuntu-16.04'), eq(variables['Build.SourceBranch'], 'refs/heads/release'))
displayName: 'Publish Build Artifacts'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'

View File

@ -0,0 +1,106 @@
# This pipeline produces a SNAPSHOT build for each of the sub modules in
# the core library, and publishes them to ossrh.
trigger:
- master
strategy:
matrix:
dstu2:
module: "org.hl7.fhir.dstu2"
dstu3:
module: "org.hl7.fhir.dstu3"
dstu2016may:
module: "org.hl7.fhir.dstu2016may"
r4:
module: "org.hl7.fhir.r4"
r5:
module: "org.hl7.fhir.r5"
validator:
module: "org.hl7.fhir.validation"
maxParallel: 3
pool:
vmImage: "ubuntu-16.04"
variables:
currentModule: $(module)
steps:
# Debugging output to identify current module.
- bash: echo Publishing SNAPSHOT for $(module)
displayName: 'Print module name.'
# Signing, for now, occurs for all builds, SNAPSHOT or release. So we need a valid
# signing key. The next two steps download the public and private keys from the
# secure library files.
- task: DownloadSecureFile@1
displayName: 'Download public key.'
inputs:
secureFile: public.key
- task: DownloadSecureFile@1
displayName: 'Download private key.'
inputs:
secureFile: private.key
# Import both the private and public keys into gpg for signing.
- bash: |
gpg --import --no-tty --batch --yes $(Agent.TempDirectory)/public.key
gpg --import --no-tty --batch --yes $(Agent.TempDirectory)/private.key
gpg --list-keys --keyid-format LONG
gpg --list-secret-keys --keyid-format LONG
displayName: 'Import signing keys.'
# For creating the snapshot release with maven, we need to build a fake settings.xml
# for it to read from. This is done for the master branch merges only.
- bash: |
cat >$(System.DefaultWorkingDirectory)/settings.xml <<EOL
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>ossrh</id>
<username>$(SONATYPE_USER)</username>
<password>$(SONATYPE_PASS)</password>
</server>
<server>
<id>sonatype-nexus-snapshots</id>
<username>$(SONATYPE_USER)</username>
<password>$(SONATYPE_PASS)</password>
</server>
<server>
<id>sonatype-nexus-staging</id>
<username>$(SONATYPE_USER)</username>
<password>$(SONATYPE_PASS)</password>
</server>
<server>
<id>$(PGP_KEYNAME)</id>
<passphrase>$(PGP_PASSPHRASE)</passphrase>
</server>
</servers>
<profiles>
<profile>
<id>release</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.keyname>$(PGP_KEYNAME)</gpg.keyname>
</properties>
</profile>
</profiles>
</settings>
EOL
displayName: 'Create .mvn/settings.xml'
# Deploy the SNAPSHOT artifact to sonatype nexus.
# This is done for the master branch merges only.
- task: Maven@3
displayName: 'Deploy $(module) to Sonatype staging'
inputs:
mavenPomFile: '$(System.DefaultWorkingDirectory)/$(module)/pom.xml'
goals: deploy
options: '--settings $(System.DefaultWorkingDirectory)/settings.xml '
publishJUnitResults: false

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -1,7 +1,9 @@
package org.hl7.fhir.r4.formats;
import java.awt.im.InputContext;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/*
Copyright (c) 2011+, HL7, Inc.
@ -169,5 +171,12 @@ public abstract class FormatUtilities {
return parser.parse(src);
}
public static Resource loadFile(InputStream source) throws FileNotFoundException, IOException, FHIRException {
byte[] src = TextFile.streamToBytes(source);
FhirFormat fmt = determineFormat(src);
ParserBase parser = makeParser(fmt);
return parser.parse(src);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -1403,7 +1403,7 @@ public class OldProfileComparer implements ProfileKnowledgeProvider {
}
private void genValueSetFile(String filename, ValueSet vs) throws IOException, FHIRException, EOperationOutcome {
RenderingContext rc = new RenderingContext(context, null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
rc.setNoSlowLookup(true);
RendererFactory.factory(vs, rc).render(vs);
String s = new XhtmlComposer(XhtmlComposer.HTML).compose(vs.getText().getDiv());

View File

@ -1391,7 +1391,7 @@ public class ProfileComparer implements ProfileKnowledgeProvider {
}
private void genValueSetFile(String filename, ValueSet vs) throws IOException, FHIRException, EOperationOutcome {
RenderingContext rc = new RenderingContext(context, null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
rc.setNoSlowLookup(true);
RendererFactory.factory(vs, rc).render(vs);
String s = new XhtmlComposer(XhtmlComposer.HTML).compose(vs.getText().getDiv());

View File

@ -533,9 +533,6 @@ public class ProfileUtilities extends TranslatingUtilities {
}
baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType);
}
if (derived.getUrl().equals("http://sharedhealth.exchange/fhir/StructureDefinition/profile-operationoutcome")) {
debug = true;
}
processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1,
derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, new ArrayList<ElementRedirection>(), base);
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
@ -901,6 +898,9 @@ public class ProfileUtilities extends TranslatingUtilities {
baseCursor++;
} else if (diffMatches.size() == 1 && (slicingDone || (!isImplicitSlicing(diffMatches.get(0), cpath) && !(diffMatches.get(0).hasSlicing() || (isExtension(diffMatches.get(0)) && diffMatches.get(0).hasSliceName()))))) {// one matching element in the differential
ElementDefinition template = null;
if (diffMatches.get(0).hasType() && "Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode()) && !isValidType(diffMatches.get(0).getType().get(0), currentBase)) {
throw new DefinitionException(context.formatMessage(I18nConstants.VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT, url, diffMatches.get(0).getPath(), diffMatches.get(0).getType().get(0), currentBase.typeSummary()));
}
if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode())) {
CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
@ -915,8 +915,16 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
if (sd != null) {
checkNotGenerating(sd, "an extension definition");
if (!sd.hasSnapshot()) {
if (!isMatchingType(sd, diffMatches.get(0).getType())) {
throw new DefinitionException(context.formatMessage(I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE2, sd.getUrl(), diffMatches.get(0).getPath(), sd.getType(), p.getValue(), diffMatches.get(0).getType().get(0).getWorkingCode()));
}
if (isGenerating(sd)) {
// this is a special case, because we're only going to access the first element, and we can rely on the fact that it's already populated.
// but we check anyway
if (sd.getSnapshot().getElementFirstRep().isEmpty()) {
throw new FHIRException(context.formatMessage(I18nConstants.ATTEMPT_TO_USE_A_SNAPSHOT_ON_PROFILE__AS__BEFORE_IT_IS_GENERATED, sd.getUrl(), "Source for first element"));
}
} else if (!sd.hasSnapshot()) {
StructureDefinition sdb = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (sdb == null)
throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), sd.getUrl()));
@ -1445,7 +1453,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!diffMatches.get(0).hasSliceName()) { // it's not real content, just the slice
diffpos++;
}
if (hasInnerDiffMatches(differential, cpath, diffpos, diffLimit, base.getElement(), false)) {
if (hasInnerDiffMatches(differential, cpath, diffCursor, diffLimit, base.getElement(), false)) {
int nbl = findEndOfElement(base, baseCursor);
int ndx = differential.getElement().indexOf(diffMatches.get(0));
int ndc = ndx+(diffMatches.get(0).hasSlicing() ? 1 : 0);
@ -1603,6 +1611,35 @@ public class ProfileUtilities extends TranslatingUtilities {
return res;
}
private boolean isMatchingType(StructureDefinition sd, List<TypeRefComponent> types) {
while (sd != null) {
for (TypeRefComponent tr : types) {
if (sd.getType().equals(tr.getCode())) {
return true;
}
}
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
}
return false;
}
private boolean isValidType(TypeRefComponent t, ElementDefinition base) {
for (TypeRefComponent tr : base.getType()) {
if (tr.getCode().equals(t.getCode())) {
return true;
}
if (tr.getWorkingCode().equals(t.getCode())) {
System.out.println("Type error: use of a simple type \""+t.getCode()+"\" wrongly constraining "+base.getPath());
return true;
}
}
return false;
}
private boolean isGenerating(StructureDefinition sd) {
return sd.hasUserData("profileutils.snapshot.generating");
}
private void checkNotGenerating(StructureDefinition sd, String role) {
if (sd.hasUserData("profileutils.snapshot.generating")) {
@ -3111,10 +3148,16 @@ public class ProfileUtilities extends TranslatingUtilities {
private ElementDefinition getElementByName(List<ElementDefinition> elements, String contentReference) {
for (ElementDefinition ed : elements)
if (ed.hasSliceName() && ("#"+ed.getSliceName()).equals(contentReference))
for (ElementDefinition ed : elements) {
if (("#"+ed.getPath()).equals(contentReference)) {
return ed;
return null;
}
if (("#"+ed.getId()).equals(contentReference)) {
return ed;
}
}
throw new Error("getElementByName: can't find "+contentReference+"in "+elements.toString());
// return null;
}
private ElementDefinition getElementById(List<ElementDefinition> elements, String contentReference) {

View File

@ -260,7 +260,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
}
public void loadFromFile(InputStream stream, String name, IContextResourceLoader loader) throws IOException, FHIRException {
loadFromFile(stream, name, null);
loadFromFile(stream, name, loader, null);
}
public void loadFromFile(InputStream stream, String name, IContextResourceLoader loader, ILoadFilter filter) throws IOException, FHIRException {

View File

@ -68,6 +68,24 @@ public class Property {
return definition.getPath().substring(definition.getPath().lastIndexOf(".")+1);
}
public String getXmlName() {
if (definition.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name")) {
return ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name");
} else {
return getName();
}
}
public String getXmlNamespace() {
if (ToolingExtensions.hasExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) {
return ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
} else if (ToolingExtensions.hasExtension(structure, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) {
return ToolingExtensions.readStringExtension(structure, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
} else {
return FormatUtilities.FHIR_NS;
}
}
public ElementDefinition getDefinition() {
return definition;
}
@ -199,14 +217,6 @@ public class Property {
return definition.getBase().getPath();
}
public String getNamespace() {
if (ToolingExtensions.hasExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
return ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
if (ToolingExtensions.hasExtension(structure, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
return ToolingExtensions.readStringExtension(structure, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
return FormatUtilities.FHIR_NS;
}
private boolean isElementWithOnlyExtension(final ElementDefinition ed, final List<ElementDefinition> children) {
boolean result = false;
if (!ed.getType().isEmpty()) {

View File

@ -47,18 +47,18 @@ import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.SnomedExpressions;
import org.hl7.fhir.r5.utils.SnomedExpressions.Expression;
import org.hl7.fhir.r5.utils.formats.Turtle;
import org.hl7.fhir.r5.utils.formats.Turtle.Complex;
import org.hl7.fhir.r5.utils.formats.Turtle.Section;
import org.hl7.fhir.r5.utils.formats.Turtle.Subject;
import org.hl7.fhir.r5.utils.formats.Turtle.TTLComplex;
import org.hl7.fhir.r5.utils.formats.Turtle.TTLList;
import org.hl7.fhir.r5.utils.formats.Turtle.TTLLiteral;
import org.hl7.fhir.r5.utils.formats.Turtle.TTLObject;
import org.hl7.fhir.r5.utils.formats.Turtle.TTLURL;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.turtle.Turtle;
import org.hl7.fhir.utilities.turtle.Turtle.Complex;
import org.hl7.fhir.utilities.turtle.Turtle.Section;
import org.hl7.fhir.utilities.turtle.Turtle.Subject;
import org.hl7.fhir.utilities.turtle.Turtle.TTLComplex;
import org.hl7.fhir.utilities.turtle.Turtle.TTLList;
import org.hl7.fhir.utilities.turtle.Turtle.TTLLiteral;
import org.hl7.fhir.utilities.turtle.Turtle.TTLObject;
import org.hl7.fhir.utilities.turtle.Turtle.TTLURL;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;

View File

@ -235,11 +235,7 @@ public class XmlParser extends ParserBase {
if (policy == ValidationPolicy.EVERYTHING) {
if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR Content
logError(line(element), col(element), path, IssueType.INVALID, context.formatMessage(I18nConstants.ELEMENT_MUST_HAVE_SOME_CONTENT), IssueSeverity.ERROR);
String ns = FormatUtilities.FHIR_NS;
if (ToolingExtensions.hasExtension(prop.getDefinition(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
ns = ToolingExtensions.readStringExtension(prop.getDefinition(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
else if (ToolingExtensions.hasExtension(prop.getStructure(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
ns = ToolingExtensions.readStringExtension(prop.getStructure(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
String ns = prop.getXmlNamespace();
if (!element.getNamespaceURI().equals(ns))
logError(line(element), col(element), path, IssueType.INVALID, context.formatMessage(I18nConstants.WRONG_NAMESPACE__EXPECTED_, ns), IssueSeverity.ERROR);
}
@ -304,7 +300,7 @@ public class XmlParser extends ParserBase {
logError(line, col, path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.XML_ATTR_VALUE_INVALID, attr.getNodeName()), IssueSeverity.ERROR);
}
if (!(attr.getNodeName().equals("xmlns") || attr.getNodeName().startsWith("xmlns:"))) {
Property property = getAttrProp(properties, attr.getNodeName());
Property property = getAttrProp(properties, attr.getLocalName(), attr.getNamespaceURI());
if (property != null) {
String av = attr.getNodeValue();
if (ToolingExtensions.hasExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"))
@ -331,7 +327,7 @@ public class XmlParser extends ParserBase {
Node child = node.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
Property property = getElementProp(properties, child.getLocalName());
Property property = getElementProp(properties, child.getLocalName(), child.getNamespaceURI());
if (property != null) {
if (!property.isChoice() && "xhtml".equals(property.getType())) {
XhtmlNode xhtml;
@ -401,7 +397,7 @@ public class XmlParser extends ParserBase {
}
private Property getElementProp(List<Property> properties, String nodeName) {
private Property getElementProp(List<Property> properties, String nodeName, String namespace) {
List<Property> propsSortedByLongestFirst = new ArrayList<Property>(properties);
// sort properties according to their name longest first, so .requestOrganizationReference comes first before .request[x]
// and therefore the longer property names get evaluated first
@ -411,22 +407,37 @@ public class XmlParser extends ParserBase {
return o2.getName().length() - o1.getName().length();
}
});
for (Property p : propsSortedByLongestFirst)
if (!p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && !p.getDefinition().hasRepresentation(
PropertyRepresentation.XMLTEXT)) {
if (p.getName().equals(nodeName))
return p;
if (p.getName().endsWith("[x]") && nodeName.length() > p.getName().length()-3 && p.getName().substring(0, p.getName().length()-3).equals(nodeName.substring(0, p.getName().length()-3)))
return p;
}
// first scan, by namespace
for (Property p : propsSortedByLongestFirst) {
if (!p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && !p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) {
if (p.getXmlName().equals(nodeName) && p.getXmlNamespace().equals(namespace))
return p;
}
}
for (Property p : propsSortedByLongestFirst) {
if (!p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && !p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT)) {
if (p.getXmlName().equals(nodeName))
return p;
if (p.getName().endsWith("[x]") && nodeName.length() > p.getName().length()-3 && p.getName().substring(0, p.getName().length()-3).equals(nodeName.substring(0, p.getName().length()-3)))
return p;
}
}
return null;
}
private Property getAttrProp(List<Property> properties, String nodeName) {
for (Property p : properties)
if (p.getName().equals(nodeName) && p.getDefinition().hasRepresentation(
PropertyRepresentation.XMLATTR))
return p;
private Property getAttrProp(List<Property> properties, String nodeName, String namespace) {
for (Property p : properties) {
if (p.getXmlName().equals(nodeName) && p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR) && p.getXmlNamespace().equals(namespace)) {
return p;
}
}
if (namespace == null) {
for (Property p : properties) {
if (p.getXmlName().equals(nodeName) && p.getDefinition().hasRepresentation(PropertyRepresentation.XMLATTR)) {
return p;
}
}
}
return null;
}
@ -524,7 +535,7 @@ public class XmlParser extends ParserBase {
xml.setSortAttributes(false);
xml.setPretty(style == OutputStyle.PRETTY);
xml.start();
xml.setDefaultNamespace(e.getProperty().getNamespace());
xml.setDefaultNamespace(e.getProperty().getXmlNamespace());
if (hasTypeAttr(e))
xml.namespace("http://www.w3.org/2001/XMLSchema-instance", "xsi");
composeElement(xml, e, e.getType(), true);
@ -545,7 +556,7 @@ public class XmlParser extends ParserBase {
public void compose(Element e, IXMLWriter xml) throws Exception {
xml.start();
xml.setDefaultNamespace(e.getProperty().getNamespace());
xml.setDefaultNamespace(e.getProperty().getXmlNamespace());
composeElement(xml, e, e.getType(), true);
xml.end();
}

View File

@ -38,8 +38,8 @@ package org.hl7.fhir.r5.formats;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.utils.formats.Turtle.Complex;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.turtle.Turtle.Complex;
public class RdfParser extends RdfParserBase {

View File

@ -43,10 +43,10 @@ import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.utils.formats.Turtle;
import org.hl7.fhir.r5.utils.formats.Turtle.Complex;
import org.hl7.fhir.r5.utils.formats.Turtle.Section;
import org.hl7.fhir.r5.utils.formats.Turtle.Subject;
import org.hl7.fhir.utilities.turtle.Turtle;
import org.hl7.fhir.utilities.turtle.Turtle.Complex;
import org.hl7.fhir.utilities.turtle.Turtle.Section;
import org.hl7.fhir.utilities.turtle.Turtle.Subject;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public abstract class RdfParserBase extends ParserBase implements IParser {

View File

@ -1,33 +1,33 @@
package org.hl7.fhir.r5.formats;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
@ -181,7 +181,6 @@ public abstract class XmlParserBase extends ParserBase implements IParser {
writer.end();
}
/**
* Compose a type to a stream (used in the spec, for example, but not normally in production)
* @

View File

@ -15,6 +15,10 @@ import org.hl7.fhir.r5.model.Bundle.BundleEntrySearchComponent;
import org.hl7.fhir.r5.model.Bundle.BundleType;
import org.hl7.fhir.r5.model.Composition;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Provenance;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.utils.EOperationOutcome;
@ -37,17 +41,58 @@ public class BundleRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return null;
}
@Override
public boolean render(XhtmlNode x, ResourceWrapper b) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
List<BaseWrapper> entries = b.children("entry");
if ("document".equals(b.get("type").primitiveValue())) {
if (entries.isEmpty() || (entries.get(0).has("resource") && "Composition".equals(entries.get(0).get("resource").fhirType())))
throw new FHIRException("Invalid document - first entry is not a Composition");
ResourceWrapper r = (ResourceWrapper) entries.get(0).getChildByName("resource").getValues().get(0);
x.addChildren(r.getNarrative());
} else if ("collection".equals(b.get("type").primitiveValue()) && allEntriesAreHistoryProvenance(entries)) {
// nothing
} else {
XhtmlNode root = new XhtmlNode(NodeType.Element, "div");
root.para().addText("Bundle "+b.getId()+" of type "+b.get("type").primitiveValue());
int i = 0;
for (BaseWrapper be : entries) {
i++;
if (be.has("fullUrl")) {
root.an(makeInternalLink(be.get("fullUrl").primitiveValue()));
}
if (be.has("resource") && be.getChildByName("resource").getValues().get(0).has("id")) {
root.an(be.get("resource").fhirType().toLowerCase() + "_" + be.getChildByName("resource").getValues().get(0).get("id").primitiveValue());
}
root.hr();
root.para().addText("Entry "+Integer.toString(i)+(be.has("fullUrl") ? " - Full URL = " + be.get("fullUrl").primitiveValue() : ""));
// if (be.hasRequest())
// renderRequest(root, be.getRequest());
// if (be.hasSearch())
// renderSearch(root, be.getSearch());
// if (be.hasResponse())
// renderResponse(root, be.getResponse());
if (be.has("resource")) {
root.para().addText("Resource "+be.get("resource").fhirType()+":");
ResourceWrapper rw = be.getChildByName("resource").getAsResource();
root.blockquote().addChildren(rw.getNarrative());
}
}
}
return false;
}
public XhtmlNode render(Bundle b) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
if (b.getType() == BundleType.DOCUMENT) {
if (!b.hasEntry() || !(b.getEntryFirstRep().hasResource() && b.getEntryFirstRep().getResource() instanceof Composition))
throw new FHIRException("Invalid document - first entry is not a Composition");
Composition dr = (Composition) b.getEntryFirstRep().getResource();
return dr.getText().getDiv();
} else if ((b.getType() == BundleType.DOCUMENT && allEntresAreHistoryProvenance(b))) {
} else if ((b.getType() == BundleType.COLLECTION && allEntresAreHistoryProvenance(b))) {
return null;
} else {
XhtmlNode root = new XhtmlNode(NodeType.Element, "div");
@ -80,9 +125,22 @@ public class BundleRenderer extends ResourceRenderer {
}
}
private boolean allEntriesAreHistoryProvenance(List<BaseWrapper> entries) throws UnsupportedEncodingException, FHIRException, IOException {
for (BaseWrapper be : entries) {
if (!"Provenance".equals(be.get("resource").fhirType())) {
return false;
}
}
return !entries.isEmpty();
}
private boolean allEntresAreHistoryProvenance(Bundle b) {
return false;
for (BundleEntryComponent be : b.getEntry()) {
if (!(be.getResource() instanceof Provenance)) {
return false;
}
}
return !b.getEntry().isEmpty();
}
private List<XhtmlNode> checkInternalLinks(Bundle b, List<XhtmlNode> childNodes) {

View File

@ -6,6 +6,7 @@ import java.io.UnsupportedEncodingException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent;
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
@ -81,7 +82,7 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
tr = t.tr();
tr.td().addText(r.getType());
if (r.hasProfile()) {
tr.td().ah(context.getPrefix()+r.getProfile()).addText(r.getProfile());
tr.td().ah(r.getProfile()).addText(r.getProfile());
}
tr.td().addText(showOp(r, TypeRestfulInteraction.READ));
if (hasVRead)
@ -112,7 +113,7 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((CapabilityStatement) r).present();
}

View File

@ -414,7 +414,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
first = false;
XhtmlNode span = td.span(null, mapping.comp.hasRelationship() ? mapping.comp.getRelationship().toCode() : "");
span.addText(getCharForRelationship(mapping.comp));
a = td.ah(getContext().getPrefix()+m.getLink()+"#"+makeAnchor(mapping.group.getTarget(), mapping.comp.getCode()));
a = td.ah(getContext().getSpecificationLink()+m.getLink()+"#"+makeAnchor(mapping.group.getTarget(), mapping.comp.getCode()));
a.addText(mapping.comp.getCode());
if (!Utilities.noString(mapping.comp.getComment()))
td.i().tx("("+mapping.comp.getComment()+")");

View File

@ -6,6 +6,7 @@ import java.io.UnsupportedEncodingException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.CompartmentDefinition;
import org.hl7.fhir.r5.model.CompartmentDefinition.CompartmentDefinitionResourceComponent;
@ -67,7 +68,7 @@ public class CompartmentDefinitionRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((CompartmentDefinition) r).present();
}

View File

@ -375,7 +375,7 @@ public class ConceptMapRenderer extends TerminologyRenderer {
if (cs == null)
td.tx(url);
else
td.ah(cs.getUserString("path")).attribute("title", url).tx(cs.present());
td.ah(context.fixReference(cs.getUserString("path"))).attribute("title", url).tx(cs.present());
}
private void addUnmapped(XhtmlNode tbl, ConceptMapGroupComponent grp) {

View File

@ -61,7 +61,7 @@ public class DataRenderer {
public DataRenderer(IWorkerContext worker) {
super();
this.context = new RenderingContext(worker, new MarkDownProcessor(Dialect.COMMON_MARK), ValidationOptions.defaults(), "", null, ResourceRendererMode.RESOURCE);
this.context = new RenderingContext(worker, new MarkDownProcessor(Dialect.COMMON_MARK), ValidationOptions.defaults(), "http://hl7.org/fhir/R4", "", null, ResourceRendererMode.RESOURCE);
}
// -- 2. Markdown support -------------------------------------------------------
@ -218,7 +218,7 @@ public class DataRenderer {
// -- 5. Data type Rendering ----------------------------------------------
public static String display(IWorkerContext context, DataType type) {
return new DataRenderer(new RenderingContext(context, null, null, "", null, ResourceRendererMode.RESOURCE)).display(type);
return new DataRenderer(new RenderingContext(context, null, null, "http://hl7.org/fhir/R4", "", null, ResourceRendererMode.RESOURCE)).display(type);
}
public String displayBase(Base b) {

View File

@ -9,6 +9,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.DiagnosticReport;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper;
@ -126,7 +127,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return display((DiagnosticReport) r);
}

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -19,7 +20,7 @@ public class EncounterRenderer extends ResourceRenderer {
return false;
}
public String display(DomainResource dr) {
public String display(Resource dr) {
return "Not done yet";
}

View File

@ -7,6 +7,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -42,7 +43,7 @@ public class ImplementationGuideRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((ImplementationGuide) r).present();
}

View File

@ -7,6 +7,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
@ -51,7 +52,7 @@ public class LiquidRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return "not done yet";
}
@ -61,7 +62,7 @@ public class LiquidRenderer extends ResourceRenderer {
XhtmlNode xn;
try {
LiquidDocument doc = engine.parse(liquidTemplate, "template");
String html = "rto do"; // engine.evaluate(doc, r, rcontext);
String html = engine.evaluate(doc, r.getBase(), rcontext);
xn = new XhtmlParser().parseFragment(html);
if (!x.getName().equals("div"))
throw new FHIRException("Error in template: Root element is not 'div'");

View File

@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.ListResource;
import org.hl7.fhir.r5.model.ListResource.ListResourceEntryComponent;
import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
@ -183,7 +184,7 @@ public class ListRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((ListResource) r).getTitle();
}

View File

@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.NamingSystem;
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
@ -120,7 +121,7 @@ public class NamingSystemRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((NamingSystem) r).present();
}

View File

@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.OperationDefinition;
import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
@ -74,7 +75,7 @@ public class OperationDefinitionRenderer extends TerminologyRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((OperationDefinition) r).present();
}
@ -104,7 +105,7 @@ public class OperationDefinitionRenderer extends TerminologyRenderer {
if (p.hasSearchType()) {
td.br();
td.tx("(");
td.ah( context.getPrefix() == null ? "search.html#"+p.getSearchType().toCode() : Utilities.pathURL(context.getPrefix(), "search.html#"+p.getSearchType().toCode())).tx(p.getSearchType().toCode());
td.ah( context.getSpecificationLink() == null ? "search.html#"+p.getSearchType().toCode() : Utilities.pathURL(context.getSpecificationLink(), "search.html#"+p.getSearchType().toCode())).tx(p.getSearchType().toCode());
td.tx(")");
}
td = tr.td();

View File

@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.ExtensionHelper;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
@ -84,7 +85,7 @@ public class OperationOutcomeRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return display((OperationOutcome) r);
}

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -20,7 +21,7 @@ public class PatientRenderer extends ResourceRenderer {
return false;
}
public String display(DomainResource dr) {
public String display(Resource dr) {
return "Not done yet";
}

View File

@ -110,7 +110,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return "todo";
}
//
@ -213,20 +213,22 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
boolean firstElement = true;
boolean last = false;
for (PropertyWrapper p : res.children()) {
ElementDefinition child = getElementDefinition(profile.getSnapshot().getElement(), path+"."+p.getName(), p);
if (p.getValues().size() > 0 && p.getValues().get(0) != null && child != null && isPrimitive(child) && includeInSummary(child)) {
if (firstElement)
firstElement = false;
else if (last)
x.tx("; ");
boolean first = true;
last = false;
for (BaseWrapper v : p.getValues()) {
if (first)
first = false;
if (!ignoreProperty(p)) {
ElementDefinition child = getElementDefinition(profile.getSnapshot().getElement(), path+"."+p.getName(), p);
if (p.getValues().size() > 0 && p.getValues().get(0) != null && child != null && isPrimitive(child) && includeInSummary(child)) {
if (firstElement)
firstElement = false;
else if (last)
x.tx(", ");
last = displayLeaf(res, v, child, x, p.getName(), showCodeDetails) || last;
x.tx("; ");
boolean first = true;
last = false;
for (BaseWrapper v : p.getValues()) {
if (first)
first = false;
else if (last)
x.tx(", ");
last = displayLeaf(res, v, child, x, p.getName(), showCodeDetails) || last;
}
}
}
}
@ -234,6 +236,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
private boolean ignoreProperty(PropertyWrapper p) {
return Utilities.existsInList(p.getName(), "contained");
}
private boolean includeInSummary(ElementDefinition child) {
if (child.getIsModifier())
return true;

View File

@ -8,6 +8,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Provenance;
import org.hl7.fhir.r5.model.Provenance.ProvenanceAgentComponent;
import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -141,7 +142,7 @@ public class ProvenanceRenderer extends ResourceRenderer {
return hasExtensions;
}
public String display(DomainResource dr) throws UnsupportedEncodingException, IOException {
public String display(Resource dr) throws UnsupportedEncodingException, IOException {
return display((Provenance) dr);
}

View File

@ -2,18 +2,25 @@ package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Expression;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Questionnaire;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemInitialComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -27,30 +34,24 @@ import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class QuestionnaireRenderer extends TerminologyRenderer {
private boolean tree = false;
public QuestionnaireRenderer(RenderingContext context) {
super(context);
}
public boolean isTree() {
return tree;
}
public void setTree(boolean tree) {
this.tree = tree;
}
public boolean render(XhtmlNode x, DomainResource q) throws UnsupportedEncodingException, IOException {
return render(x, (Questionnaire) q);
}
public boolean render(XhtmlNode x, Questionnaire q) throws UnsupportedEncodingException, IOException {
if (tree) {
return renderTree(x, q);
} else {
return renderForm(x, q);
switch (context.getQuestionnaireMode()) {
case FORM: return renderForm(x, q);
case LINKS: return renderLinks(x, q);
case LOGIC: return renderLogic(x, q);
case DEFNS: return renderDefns(x, q);
case TREE: return renderTree(x, q);
default:
throw new Error("Unknown Questionnaire Renderer Mode");
}
}
@ -58,8 +59,8 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context.getDestDir(), context.isInlineGraphics(), true);
TableModel model = gen.new TableModel("qtree="+q.getId(), true);
model.setAlternating(true);
model.setDocoImg(context.getPrefix() +"help16.png");
model.setDocoRef(context.getPrefix()+"formats.html#table");
model.setDocoImg(context.getSpecificationLink() +"help16.png");
model.setDocoRef(context.getSpecificationLink()+"formats.html#table");
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "LinkId"), translate("sd.hint", "The linkId for the item"), null, 0));
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Text"), translate("sd.hint", "Text for the item"), null, 0));
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Cardinality"), translate("sd.hint", "Minimum and Maximum # of times the the itemcan appear in the instance"), null, 0));
@ -86,19 +87,19 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
String txt = (i.hasPrefix() ? i.getPrefix() + ". " : "") + i.getText();
r.getCells().add(gen.new Cell(null, null, txt, null, null));
r.getCells().add(gen.new Cell(null, null, (i.getRequired() ? "1" : "0")+".."+(i.getRepeats() ? "*" : "1"), null, null));
r.getCells().add(gen.new Cell(null, context.getPrefix()+"codesystem-item-type.html#"+i.getType().toCode(), i.getType().toCode(), null, null));
r.getCells().add(gen.new Cell(null, context.getSpecificationLink()+"codesystem-item-type.html#"+i.getType().toCode(), i.getType().toCode(), null, null));
// flags:
Cell flags = gen.new Cell();
r.getCells().add(flags);
if (i.getReadOnly()) {
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getPrefix(), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-readonly.png"))));
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-readonly.png"))));
}
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-subject.png"))));
}
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getPrefix(), "extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-hidden.png"))));
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-hidden.png"))));
}
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-optional.png"))));
@ -113,7 +114,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"))));
flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory", null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"))));
}
Cell defn = gen.new Cell();
@ -209,11 +210,9 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
for (QuestionnaireItemComponent c : i.getItem()) {
hasExt = renderTreeItem(gen, r.getSubRows(), q, c) || hasExt;
}
return hasExt;
return hasExt;
}
private void addExpression(Piece p, Expression exp, String label, String url) {
XhtmlNode x = new XhtmlNode(NodeType.Element, "li").style("font-size: 11px");
p.addHtml(x);
@ -222,6 +221,129 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
x.code(exp.getExpression());
}
private boolean renderLogic(XhtmlNode x, Questionnaire q) throws FHIRException, IOException {
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context.getDestDir(), context.isInlineGraphics(), true);
TableModel model = gen.new TableModel("qtree="+q.getId(), true);
model.setAlternating(true);
model.setDocoImg(context.getSpecificationLink() +"help16.png");
model.setDocoRef(context.getSpecificationLink()+"formats.html#table");
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "LinkId"), translate("sd.hint", "The linkId for the item"), null, 0));
model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Description & Constraints"), translate("sd.hint", "Additional information about the item"), null, 0));
boolean hasExt = false;
for (QuestionnaireItemComponent i : q.getItem()) {
hasExt = renderLogicItem(gen, model.getRows(), q, i) || hasExt;
}
XhtmlNode xn = gen.generate(model, context.getDestDir(), 1, null);
x.getChildNodes().add(xn);
return hasExt;
}
private boolean renderLogicItem(HierarchicalTableGenerator gen, List<Row> rows, Questionnaire q, QuestionnaireItemComponent i) throws IOException {
Row r = gen.new Row();
rows.add(r);
boolean hasExt = false;
r.setIcon("icon-q-"+i.getType().toCode()+".png", i.getType().getDisplay());
r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"-definitions.html#extension."+i.getLinkId(), i.getLinkId(), null, null));
Cell defn = gen.new Cell();
r.getCells().add(defn);
if (i.hasMaxLength()) {
defn.getPieces().add(gen.new Piece(null, "Max Length: ", null));
defn.getPieces().add(gen.new Piece(null, Integer.toString(i.getMaxLength()), null));
}
if (i.hasDefinition()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Definition: ", null));
defn.getPieces().add(gen.new Piece(null, i.getDefinition(), null));
}
if (i.hasEnableWhen()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Enable When: ", null));
defn.getPieces().add(gen.new Piece(null, "todo", null));
}
if (i.hasAnswerValueSet()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Value Set: ", null));
if (i.getAnswerValueSet().startsWith("#")) {
ValueSet vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
if (vs == null) {
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
} else {
defn.getPieces().add(gen.new Piece("todo", vs.present(), null));
}
} else {
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
if (vs == null || !vs.hasUserData("path")) {
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
} else {
defn.getPieces().add(gen.new Piece(vs.getUserString("path"), vs.present(), null));
}
}
}
if (i.hasAnswerOption()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Options: ", null));
defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#"+i.getLinkId(), Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), null));
}
if (i.hasInitial()) {
for (QuestionnaireItemInitialComponent v : i.getInitial()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Initial Value: ", null));
defn.getPieces().add(gen.new Piece(null, v.getValue().fhirType(), null));
defn.getPieces().add(gen.new Piece(null, " = ", null));
if (v.getValue().isPrimitive()) {
defn.getPieces().add(gen.new Piece(null, v.getValue().primitiveValue(), null));
} else {
defn.getPieces().add(gen.new Piece(null, "{todo}", null));
}
}
}
// still todo
//
//http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-choiceColumn
//
//http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width
//http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod
//http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl
//http://hl7.org/fhir/StructureDefinition/questionnaire-sliderStepValue
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemContext") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-contextExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-candidateExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression")) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Expressions: ", null));
Piece p = gen.new Piece("ul");
defn.getPieces().add(p);
for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression")) {
addExpression(p, e.getValueExpression(), "Initial Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression");
}
for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-contextExpression")) {
addExpression(p, e.getValueExpression(), "Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-contextExpression");
}
for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemContext")) {
addExpression(p, e.getValueExpression(), "Item Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemContext");
}
for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression")) {
addExpression(p, e.getValueExpression(), "Enable When", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression");
}
for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression")) {
addExpression(p, e.getValueExpression(), "Calculated Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression");
}
for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-candidateExpression")) {
addExpression(p, e.getValueExpression(), "Candidates", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-candidateExpression");
}
}
for (QuestionnaireItemComponent c : i.getItem()) {
hasExt = renderLogicItem(gen, r.getSubRows(), q, c) || hasExt;
}
return hasExt;
}
public boolean renderForm(XhtmlNode x, Questionnaire q) throws UnsupportedEncodingException, IOException {
boolean hasExt = false;
XhtmlNode d = x.div();
@ -231,7 +353,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
}
int i = 1;
for (QuestionnaireItemComponent c : q.getItem()) {
hasExt = renderFormItem(d, q, c, hasPrefix ? null : Integer.toString(i), true) || hasExt;
hasExt = renderFormItem(d, q, c, hasPrefix ? null : Integer.toString(i), 0) || hasExt;
i++;
}
return hasExt;
@ -249,20 +371,25 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
return false;
}
private boolean renderFormItem(XhtmlNode x, Questionnaire q, QuestionnaireItemComponent i, String pfx, boolean root) {
private boolean renderFormItem(XhtmlNode x, Questionnaire q, QuestionnaireItemComponent i, String pfx, int indent) throws IOException {
boolean hasExt = false;
XhtmlNode d = x.div();
if (!root) {
d.style("margin-left: 10px");
XhtmlNode d = x.div().style("width: "+Integer.toString(900-indent*10)+"px; border-top: 1px #eeeeee solid");
if (indent > 0) {
d.style("margin-left: "+Integer.toString(10*indent)+"px");
}
XhtmlNode display = d.div().style("display: inline-block; width: "+Integer.toString(500-indent*10)+"px");
XhtmlNode details = d.div().style("border: 1px #ccccff solid; padding: 2px; display: inline-block; background-color: #fefce7; width: 380px");
XhtmlNode p = display.para();
if (i.getType() == QuestionnaireItemType.GROUP) {
p = p.b();
}
XhtmlNode p = d.para();
if (i.hasPrefix()) {
p.tx(i.getPrefix());
p.tx(": ");
}
p.span(null, "linkId: "+i.getLinkId()).tx(i.getText());
if (i.getRequired()) {
p.span("color: red", "Mandatory").tx(" *");
p.span("color: red", "Mandatory").tx("*");
}
XhtmlNode input = null;
@ -296,6 +423,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
case DISPLAY:
break;
case GROUP:
break;
case INTEGER:
p.tx(" ");
@ -325,17 +453,142 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
if (input != null) {
if (i.getReadOnly()) {
input.attribute("readonly", "1");
input.style("background-color: #eeeeee");
}
}
// if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation")) {
// String code = ToolingExtensions.readStringExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation");
// flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"))));
//}
XhtmlNode ul = details.ul();
boolean hasFlag = false;
XhtmlNode flags = item(ul, "Flags");
item(ul, "linkId", i.getLinkId());
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
hasFlag = true;
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", "Can change the subject of the questionnaire").img(Utilities.path(context.getDestDir(), "icon-qi-subject.png"));
}
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
hasFlag = true;
flags.ah(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), "Is a hidden item").img(Utilities.path(context.getDestDir(), "icon-qi-hidden.png"));
d.style("background-color: #eeeeee");
}
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
hasFlag = true;
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", "Is optional to display").img(Utilities.path(context.getDestDir(), "icon-qi-optional.png"));
}
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
hasFlag = true;
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", "Is linked to an observation").img(Utilities.path(context.getDestDir(), "icon-qi-observation.png"));
}
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
hasFlag = true;
flags.ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory", "Category: "+code).img(Utilities.path(context.getDestDir(), "icon-qi-"+code+".png"));
}
if (i.hasMaxLength()) {
item(ul, "Max Length", Integer.toString(i.getMaxLength()));
}
if (i.hasDefinition()) {
item(ul, "Definition", i.getDefinition());
}
if (i.hasEnableWhen()) {
item(ul, "Enable When", "todo");
}
if (i.hasAnswerValueSet()) {
XhtmlNode ans = item(ul, "Answers");
if (i.getAnswerValueSet().startsWith("#")) {
ValueSet vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
if (vs == null) {
ans.tx(i.getAnswerValueSet());
} else {
ans.ah("todo").tx(vs.present());
}
} else {
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
if (vs == null || !vs.hasUserData("path")) {
ans.tx(i.getAnswerValueSet());
} else {
ans.ah(vs.getUserString("path")).tx(vs.present());
}
}
}
if (i.hasAnswerOption()) {
item(ul, "Answers", Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), context.getDefinitionsTarget()+"#"+i.getLinkId());
}
if (i.hasInitial()) {
XhtmlNode vi = item(ul, "Initial Values");
boolean first = true;
for (QuestionnaireItemInitialComponent v : i.getInitial()) {
if (first) first = false; else vi.tx(", ");
if (v.getValue().isPrimitive()) {
vi.tx(v.getValue().primitiveValue());
} else {
vi.tx("{todo}");
}
}
}
if (!hasFlag) {
ul.remove(flags);
}
// if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemContext") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-contextExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-candidateExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression")) {
// if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
// defn.getPieces().add(gen.new Piece(null, "Expressions: ", null));
// Piece p = gen.new Piece("ul");
// defn.getPieces().add(p);
// for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression")) {
// addExpression(p, e.getValueExpression(), "Initial Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression");
// }
// for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-contextExpression")) {
// addExpression(p, e.getValueExpression(), "Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-contextExpression");
// }
// for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemContext")) {
// addExpression(p, e.getValueExpression(), "Item Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemContext");
// }
// for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression")) {
// addExpression(p, e.getValueExpression(), "Enable When", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression");
// }
// for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression")) {
// addExpression(p, e.getValueExpression(), "Calculated Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression");
// }
// for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-candidateExpression")) {
// addExpression(p, e.getValueExpression(), "Candidates", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-candidateExpression");
// }
// }
//
int t = 1;
for (QuestionnaireItemComponent c : i.getItem()) {
hasExt = renderFormItem(d, q, c, pfx == null ? null : pfx+"."+Integer.toString(t), false) || hasExt;
hasExt = renderFormItem(x, q, c, pfx == null ? null : pfx+"."+Integer.toString(t), indent+1) || hasExt;
t++;
}
return hasExt;
}
private void item(XhtmlNode ul, String name, String value, String valueLink) {
if (!Utilities.noString(value)) {
ul.li().style("font-size: 10px").ah(valueLink).tx(name+": "+value);
}
}
private void item(XhtmlNode ul, String name, String value) {
if (!Utilities.noString(value)) {
ul.li().style("font-size: 10px").tx(name+": "+value);
}
}
private XhtmlNode item(XhtmlNode ul, String name) {
XhtmlNode li = ul.li();
li.style("font-size: 10px").tx(name+": ");
return li;
}
private void listOptions(Questionnaire q, QuestionnaireItemComponent i, XhtmlNode select) {
if (i.hasAnswerValueSet()) {
ValueSet vs = null;
@ -362,12 +615,207 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
select.option("a", "??", false);
}
public String display(DomainResource dr) throws UnsupportedEncodingException, IOException {
public String display(Resource dr) throws UnsupportedEncodingException, IOException {
return display((Questionnaire) dr);
}
public String display(Questionnaire q) throws UnsupportedEncodingException, IOException {
return "Questionnaire "+q.present();
}
private boolean renderLinks(XhtmlNode x, Questionnaire q) {
x.para().tx("Try this questionnaire out:");
XhtmlNode ul = x.ul();
ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getSelfLink(), "package.tgz")+"&q="+q.getId()+".json").tx("NLM Forms Library");
return false;
}
private boolean renderDefns(XhtmlNode x, Questionnaire q) throws IOException {
XhtmlNode tbl = x.table("dict");
boolean ext = false;
for (QuestionnaireItemComponent qi : q.getItem()) {
ext = renderDefinition(tbl, q, qi, new ArrayList<>()) || ext;
}
return ext;
}
private boolean renderDefinition(XhtmlNode tbl, Questionnaire q, QuestionnaireItemComponent qi, List<QuestionnaireItemComponent> parents) throws IOException {
boolean ext = false;
XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
td.an(qi.getLinkId());
for (QuestionnaireItemComponent p : parents) {
td.ah("#"+p.getLinkId()).img(Utilities.path(context.getDestDir(), "icon_q_item.png"));
td.tx(" > ");
}
td.img(Utilities.path(context.getDestDir(), "icon_q_item.png"));
td.tx(" Item ");
td.b().tx(qi.getLinkId());
// general information
defn(tbl, "Link Id", qi.getLinkId());
defn(tbl, "Prefix", qi.getPrefix());
defn(tbl, "Text", qi.getText());
defn(tbl, "Type", qi.getType().getDisplay());
defn(tbl, "Required", qi.getRequired(), true);
defn(tbl, "Repeats", qi.getRepeats(), true);
defn(tbl, "Read Only", qi.getReadOnly(), false);
if (ToolingExtensions.readBoolExtension(qi, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
defn(tbl, "Subject", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", "This element changes who the subject of the question is", null);
}
// content control
defn(tbl, "Max Length", qi.getMaxLength());
if (qi.hasAnswerValueSet()) {
defn(tbl, "Value Set", qi.getDefinition(), context.getWorker().fetchResource(ValueSet.class, qi.getAnswerValueSet()));
}
if (qi.hasAnswerOption()) {
XhtmlNode tr = tbl.tr();
tr.td().tx("Allowed Answers");
XhtmlNode ul = tr.td().ul();
for (QuestionnaireItemAnswerOptionComponent ans : qi.getAnswerOption()) {
XhtmlNode li = ul.li();
render(li, ans.getValue());
if (ans.getInitialSelected()) {
li.tx(" (initially selected)");
}
}
}
if (qi.hasInitial()) {
XhtmlNode tr = tbl.tr();
tr.td().tx(Utilities.pluralize("Initial Answer", qi.getInitial().size()));
if (qi.getInitial().size() == 1) {
render(tr.td(), qi.getInitialFirstRep().getValue());
} else {
XhtmlNode ul = tr.td().ul();
for (QuestionnaireItemInitialComponent ans : qi.getInitial()) {
XhtmlNode li = ul.li();
render(li, ans.getValue());
}
}
}
// appearance
if (qi.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
XhtmlNode tr = tbl.tr();
tr.td().ah("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").tx("Display Category");
render(tr.td(), qi.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValue());
}
if (ToolingExtensions.readBoolExtension(qi, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
defn(tbl, "Hidden Item", "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", "This item is a hidden question", null);
}
if (ToolingExtensions.readBoolExtension(qi, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
defn(tbl, "Hidden Item", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", "This item is optional to display", null);
}
// formal definitions
if (qi.hasDefinition()) {
defn(tbl, "Definition", qi.getDefinition(), context.getWorker().fetchResource(Resource.class, qi.getDefinition()));
}
if (qi.hasCode()) {
XhtmlNode tr = tbl.tr();
tr.td().tx(Utilities.pluralize("Code", qi.getCode().size()));
XhtmlNode ul = tr.td().ul();
for (Coding c : qi.getCode()) {
renderCoding(ul.li(), c);
}
}
if (qi.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
XhtmlNode tr = tbl.tr();
tr.td().ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod").tx("Observation Link Period");
render(tr.td(), qi.getExtensionByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod").getValue());
}
// dynamic management
if (qi.hasEnableWhen()) {
XhtmlNode tr = tbl.tr();
tr.td().tx("Enable When");
td = tr.td();
if (qi.getEnableWhen().size() == 1) {
renderEnableWhen(td, qi.getEnableWhen().get(0));
} else {
td.tx(qi.getEnableBehavior().getDisplay()+" are true:");
XhtmlNode ul = td.ul();
for (QuestionnaireItemEnableWhenComponent ew : qi.getEnableWhen()) {
renderEnableWhen(ul.li(), ew);
}
}
}
// other stuff
List<QuestionnaireItemComponent> curr = new ArrayList<>();
curr.addAll(parents);
curr.add(qi);
for (QuestionnaireItemComponent qic : qi.getItem()) {
ext = renderDefinition(tbl, q, qic, curr) || ext;
}
return ext;
}
private void defn(XhtmlNode tbl, String name, String url, Resource res) throws UnsupportedEncodingException, IOException {
if (res != null && res.hasUserData("path")) {
defn(tbl, "Definition", RendererFactory.factory(res, context).display(res), res.getUserString("path"));
} else if (Utilities.isAbsoluteUrl(url)) {
defn(tbl, "Definition", url, url);
} {
defn(tbl, "Definition", url);
}
}
private void renderEnableWhen(XhtmlNode x, QuestionnaireItemEnableWhenComponent ew) {
x.ah("#"+ew.getQuestion()).tx(ew.getQuestion());
x.tx(" ");
x.tx(ew.getOperator().toCode());
x.tx(" ");
x.tx(display(ew.getAnswer()));
}
private void defn(XhtmlNode tbl, String name, int value) {
if (value > 0) {
XhtmlNode tr = tbl.tr();
tr.td().tx(name);
tr.td().tx(value);
}
}
private void defn(XhtmlNode tbl, String name, String value) {
if (!Utilities.noString(value)) {
XhtmlNode tr = tbl.tr();
tr.td().tx(name);
tr.td().tx(value);
}
}
private void defn(XhtmlNode tbl, String name, String value, String url) {
if (!Utilities.noString(value)) {
XhtmlNode tr = tbl.tr();
tr.td().tx(name);
tr.td().ah(url).tx(value);
}
}
private void defn(XhtmlNode tbl, String name, String nurl, String value, String url) {
if (!Utilities.noString(value)) {
XhtmlNode tr = tbl.tr();
tr.td().ah(nurl).tx(name);
if (url != null) {
tr.td().ah(url).tx(value);
} else {
tr.td().tx(value);
}
}
}
private void defn(XhtmlNode tbl, String name, boolean value, boolean ifFalse) {
if (ifFalse || value) {
XhtmlNode tr = tbl.tr();
tr.td().tx(name);
tr.td().tx(Boolean.toString(value));
}
}
}

View File

@ -74,21 +74,23 @@ public abstract class ResourceRenderer extends DataRenderer {
assert r.getContext() == context;
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
boolean hasExtensions = render(x, r);
r.injectNarrative(x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED);
if (r.hasNarrative()) {
r.injectNarrative(x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED);
}
return x;
}
public abstract boolean render(XhtmlNode x, DomainResource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome;
public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
throw new NotImplementedException("This is not implemented yet for resources of type "+r.getName());
throw new NotImplementedException("Rendering using the wrapper is not implemented yet for resources of type "+r.getName());
}
public void describe(XhtmlNode x, DomainResource r) throws UnsupportedEncodingException, IOException {
x.tx(display(r));
}
public abstract String display(DomainResource r) throws UnsupportedEncodingException, IOException;
public abstract String display(Resource r) throws UnsupportedEncodingException, IOException;
public static void inject(DomainResource r, XhtmlNode x, NarrativeStatus status) {
if (!x.hasAttribute("xmlns"))
@ -104,7 +106,7 @@ public abstract class ResourceRenderer extends DataRenderer {
r.getText().setStatus(status);
} else {
XhtmlNode n = r.getText().getDiv();
n.hr();
n.clear();
n.getChildNodes().addAll(x.getChildNodes());
}
}

View File

@ -6,6 +6,7 @@ import java.io.UnsupportedEncodingException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
@ -26,7 +27,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
public boolean render(XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException {
x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getPrefix(), "", false, false, null, false));
x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getSpecificationLink(), "", false, false, null, false));
return true;
}
@ -39,7 +40,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
@Override
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((StructureDefinition) r).present();
}

View File

@ -40,7 +40,7 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
super(context, rcontext);
}
public String display(DomainResource r) throws UnsupportedEncodingException, IOException {
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((CanonicalResource) r).present();
}
@ -103,7 +103,7 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
for (UsedConceptMap m : maps) {
XhtmlNode td = tr.td();
XhtmlNode b = td.b();
XhtmlNode a = b.ah(getContext().getPrefix()+m.getLink());
XhtmlNode a = b.ah(getContext().getSpecificationLink()+m.getLink());
a.addText(m.getDetails().getName());
if (m.getDetails().isDoDescription() && m.getMap().hasDescription())
addMarkdown(td, m.getMap().getDescription());
@ -155,19 +155,22 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
ref = (String) cs.getUserData("filename");
else
addHtml = false;
if (Utilities.noString(ref))
if (Utilities.noString(ref)) {
ref = (String) cs.getUserData("path");
if (ref != null) {
addHtml = false;
}
}
}
String spec = getSpecialReference(inc.getSystem());
if (spec != null) {
XhtmlNode a = li.ah(spec);
a.code(inc.getSystem());
} else if (cs != null && ref != null) {
if (!Utilities.noString(getContext().getPrefix()) && ref.startsWith("http://hl7.org/fhir/"))
ref = ref.substring(20)+"/index.html";
else if (addHtml && !ref.contains(".html"))
if (addHtml && !ref.contains(".html"))
ref = ref + ".html";
XhtmlNode a = li.ah(getContext().getPrefix()+ref.replace("\\", "/"));
ref = context.fixReference(ref);
XhtmlNode a = li.ah(ref.replace("\\", "/"));
a.code(inc.getSystem());
} else {
li.code(inc.getSystem());
@ -260,14 +263,14 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
if (vs != null) {
String ref = (String) vs.getUserData("path");
ref = adjustForPath(ref);
ref = context.fixReference(ref);
XhtmlNode a = li.ah(ref == null ? "?ngen-11?" : ref.replace("\\", "/"));
a.addText(value);
} else {
CodeSystem cs = getContext().getWorker().fetchCodeSystem(value);
if (cs != null) {
String ref = (String) cs.getUserData("path");
ref = adjustForPath(ref);
ref = context.fixReference(ref);
XhtmlNode a = li.ah(ref == null ? "?ngen-12?" : ref.replace("\\", "/"));
a.addText(value);
} else if (value.equals("http://snomed.info/sct") || value.equals("http://snomed.info/id")) {
@ -282,13 +285,6 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
}
}
private String adjustForPath(String ref) {
if (getContext().getPrefix() == null)
return ref;
else
return getContext().getPrefix()+ref;
}
protected String getDisplayForConcept(String system, String value) {

View File

@ -207,7 +207,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
if (ref == null)
p.code(vs.getExpansion().getContains().get(0).getSystem());
else
p.ah(getContext().getPrefix()+ref).code(vs.getExpansion().getContains().get(0).getSystem());
p.ah(context.fixReference(ref)).code(vs.getExpansion().getContains().get(0).getSystem());
}
XhtmlNode t = x.table( "codes");
XhtmlNode tr = t.tr();
@ -638,7 +638,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
} else
td.addText(code);
} else {
String href = getContext().getPrefix()+getCsRef(e);
String href = context.fixReference(getCsRef(e));
if (href.contains("#"))
href = href + "-"+Utilities.nmtokenize(code);
else
@ -662,9 +662,9 @@ public class ValueSetRenderer extends TerminologyRenderer {
String cslink = getCsRef(cs);
XhtmlNode a = null;
if (cslink != null)
a = td.ah(getContext().getPrefix()+cslink+"#"+cs.getId()+"-"+code);
a = td.ah(getContext().getSpecificationLink()+cslink+"#"+cs.getId()+"-"+code);
else
a = td.ah(getContext().getPrefix()+vslink+"#"+code);
a = td.ah(getContext().getSpecificationLink()+vslink+"#"+code);
a.addText(code);
}
@ -796,7 +796,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
} else {
li.tx(f.getProperty()+" "+describe(f.getOp())+" ");
if (e != null && codeExistsInValueSet(e, f.getValue())) {
String href = getContext().getPrefix()+getCsRef(e);
String href = getContext().fixReference(getCsRef(e));
if (href.contains("#"))
href = href + "-"+Utilities.nmtokenize(f.getValue());
else

View File

@ -29,6 +29,7 @@ public class BaseWrappers {
public int getMaxCardinality();
public StructureDefinition getStructure();
public BaseWrapper value();
public ResourceWrapper getAsResource();
}
public interface WrapperBase extends RendererWrapper {
@ -42,11 +43,13 @@ public class BaseWrappers {
public List<ResourceWrapper> getContained();
public String getId();
public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException;
public Base getBase();
public String getName();
public void describe(XhtmlNode x) throws UnsupportedEncodingException, IOException;
public void injectNarrative(XhtmlNode x, NarrativeStatus status) throws IOException;
public BaseWrapper root();
public StructureDefinition getDefinition();
public boolean hasNarrative();
}
public interface BaseWrapper extends WrapperBase {

View File

@ -178,6 +178,11 @@ public class DOMWrappers {
return getValues().get(0);
}
@Override
public ResourceWrapper getAsResource() {
throw new Error("Not implemented yet");
}
}
public static class ResourceWrapperElement extends WrapperBaseImpl implements ResourceWrapper {
@ -307,6 +312,23 @@ public class DOMWrappers {
public StructureDefinition getDefinition() {
return definition;
}
@Override
public Base getBase() {
throw new Error("Not Implemented yet");
}
@Override
public boolean hasNarrative() {
StructureDefinition sd = definition;
while (sd != null) {
if ("DomainResource".equals(sd.getType())) {
return true;
}
sd = context.getWorker().fetchResource(StructureDefinition.class, sd.getBaseDefinition());
}
return false;
}
}
}

View File

@ -96,6 +96,11 @@ public class DirectWrappers {
public String toString() {
return "#."+wrapped.toString();
}
@Override
public ResourceWrapper getAsResource() {
throw new Error("Not implemented yet");
}
}
public static class BaseWrapperDirect extends WrapperBaseImpl implements BaseWrapper {
@ -214,6 +219,24 @@ public class DirectWrappers {
public StructureDefinition getDefinition() {
return context.getWorker().fetchTypeDefinition(wrapped.fhirType());
}
@Override
public Base getBase() {
return wrapped;
}
@Override
public boolean hasNarrative() {
StructureDefinition sd = context.getWorker().fetchTypeDefinition(wrapped.fhirType());
while (sd != null) {
if ("DomainResource".equals(sd.getType())) {
return true;
}
sd = context.getWorker().fetchResource(StructureDefinition.class, sd.getBaseDefinition());
}
return false;
}
}
}

View File

@ -48,8 +48,9 @@ public class ElementWrappers {
if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
return null;
if (element.hasElementProperty())
return null;
if (element.hasElementProperty()) {
return element;
}
ByteArrayOutputStream xml = new ByteArrayOutputStream();
try {
new XmlParser(context.getWorker()).compose(element, xml, OutputStyle.PRETTY, null);
@ -57,7 +58,7 @@ public class ElementWrappers {
throw new FHIRException(e.getMessage(), e);
}
if (context.getParser() == null) {
System.out.println("huh?");
System.out.println("Noe version specific parser provided");
}
return context.getParser().parseType(xml.toString(), type);
}
@ -214,6 +215,23 @@ public class ElementWrappers {
public StructureDefinition getDefinition() {
return definition;
}
@Override
public Base getBase() {
return wrapped;
}
@Override
public boolean hasNarrative() {
StructureDefinition sd = definition;
while (sd != null) {
if ("DomainResource".equals(sd.getType())) {
return true;
}
sd = context.getWorker().fetchResource(StructureDefinition.class, sd.getBaseDefinition());
}
return false;
}
}
public static class PropertyWrapperMetaElement extends RendererWrapperImpl implements PropertyWrapper {
@ -282,6 +300,11 @@ public class ElementWrappers {
return getValues().get(0);
}
@Override
public ResourceWrapper getAsResource() {
return new ElementWrappers.ResourceWrapperMetaElement(context, values.get(0));
}
}
}

View File

@ -16,6 +16,7 @@ import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.utilities.MarkDownProcessor;
import org.hl7.fhir.utilities.MarkDownProcessor.Dialect;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationOptions;
public class RenderingContext {
@ -33,22 +34,53 @@ public class RenderingContext {
RESOURCE, IG
}
protected IWorkerContext worker;
protected MarkDownProcessor markdown;
protected ResourceRendererMode mode;
public enum QuestionnaireRendererMode {
/**
* A visual presentation of the questionnaire, with a set of property panes that can be toggled on and off.
* Note that this is not the same as how the questionnaire would like on a form filler, since all dynamic behavior is ignored
*/
FORM,
/**
* a structured tree that presents the content of the questionnaire in a logical fashion
*/
TREE,
/**
* A structured tree that presents the enableWhen, terminology and expression bindings for the questionnaire
*/
LOGIC,
/**
* A presentation that lists all the items, with full details about them
*/
DEFNS,
/**
* Rendered links to various openly available Form Filler applications that know how to render a questionnaire published in a package
*/
LINKS
}
private IWorkerContext worker;
private MarkDownProcessor markdown;
private ResourceRendererMode mode;
private IReferenceResolver resolver;
private ILiquidTemplateProvider templateProvider;
private IEvaluationContext services;
private ITypeParser parser;
protected String lang;
protected String prefix;
private String lang;
private String localPrefix; // relative link within local context
private String specificationLink;
private String selfLink; // absolute link to where the content is to be found (only used in a few circumstances when making external references to tools)
private int headerLevelContext;
private boolean canonicalUrlsAsLinks;
private boolean pretty;
private boolean header;
protected ValidationOptions terminologyServiceOptions;
private ValidationOptions terminologyServiceOptions;
private boolean noSlowLookup;
private String tooCostlyNoteEmpty;
private String tooCostlyNoteNotEmpty;
@ -56,26 +88,28 @@ public class RenderingContext {
private String tooCostlyNoteNotEmptyDependent;
private List<String> codeSystemPropList = new ArrayList<>();
protected ProfileUtilities profileUtilities;
private ProfileUtilities profileUtilities;
private String definitionsTarget;
private String destDir;
private boolean inlineGraphics;
private QuestionnaireRendererMode questionnaireMode = QuestionnaireRendererMode.FORM;
/**
*
* @param context - access to all related resources that might be needed
* @param markdown - appropriate markdown processing engine
* @param terminologyServiceOptions - options to use when looking up codes
* @param prefix - path to FHIR specification
* @param specLink - path to FHIR specification
* @param lang - langauage to render in
*/
public RenderingContext(IWorkerContext worker, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String prefix, String lang, ResourceRendererMode mode) {
public RenderingContext(IWorkerContext worker, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String specLink, String localPrefix, String lang, ResourceRendererMode mode) {
super();
this.worker = worker;
this.markdown = markdown;
this.lang = lang;
this.prefix = prefix;
this.specificationLink = specLink;
this.localPrefix = localPrefix;
this.mode = mode;
this.terminologyServiceOptions = terminologyServiceOptions;
profileUtilities = new ProfileUtilities(worker, null, null);
@ -115,8 +149,12 @@ public class RenderingContext {
return lang;
}
public String getPrefix() {
return prefix;
public String getSpecificationLink() {
return specificationLink;
}
public String getLocalPrefix() {
return localPrefix;
}
public ValidationOptions getTerminologyServiceOptions() {
@ -262,7 +300,7 @@ public class RenderingContext {
}
public RenderingContext copy() {
RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, prefix, lang, mode);
RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, specificationLink, localPrefix, lang, mode);
res.resolver = resolver;
res.templateProvider = templateProvider;
@ -302,6 +340,32 @@ public class RenderingContext {
public void setHeader(boolean header) {
this.header = header;
}
public QuestionnaireRendererMode getQuestionnaireMode() {
return questionnaireMode;
}
public void setQuestionnaireMode(QuestionnaireRendererMode questionnaireMode) {
this.questionnaireMode = questionnaireMode;
}
public String getSelfLink() {
return selfLink;
}
public void setSelfLink(String selfLink) {
this.selfLink = selfLink;
}
public String fixReference(String ref) {
if (!Utilities.isAbsoluteUrl(ref)) {
return (localPrefix == null ? "" : localPrefix)+ref;
}
if (ref.startsWith("http://hl7.org/fhir") && !ref.substring(20).contains("/")) {
return specificationLink+ref.substring(20);
}
return ref;
}

View File

@ -14,7 +14,6 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.fhir.ucum.UcumEssenceService;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.SimpleWorkerContext;
@ -25,6 +24,7 @@ import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.cache.ToolsVersion;
import org.hl7.fhir.utilities.tests.BaseTestingUtilities;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
@ -67,7 +67,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
public class TestingUtilities {
public class TestingUtilities extends BaseTestingUtilities {
private static final boolean SHOW_DIFF = true;
static public Map<String, IWorkerContext> fcontexts;
@ -97,8 +97,6 @@ public class TestingUtilities {
return fcontexts.get(v);
}
static public boolean silent;
static public String fixedpath;
static public String contentpath;
@ -457,74 +455,6 @@ public class TestingUtilities {
return null;
}
public static boolean findTestResource(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
return new File(n).exists();
} else {
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
try {
InputStream inputStream = TestingUtilities.class.getResourceAsStream(classpath);
return inputStream != null;
} catch (Throwable t) {
return false;
}
}
}
// TODO: JA need to figure out how to detect that we're running in maven
private static boolean isTryToLoadFromFileSystem() {
return !"true".equals(System.getProperty("dont_load_from_filesystem"));
}
public static String loadTestResource(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
// ok, we'll resolve this locally
return TextFile.fileToString(new File(n));
} else {
// resolve from the package
String contents;
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
try (InputStream inputStream = TestingUtilities.class.getResourceAsStream(classpath)) {
if (inputStream == null) {
throw new IOException("Can't find file on classpath: " + classpath);
}
contents = IOUtils.toString(inputStream, java.nio.charset.StandardCharsets.UTF_8);
}
return contents;
}
}
public static InputStream loadTestResourceStream(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
return new FileInputStream(n);
} else {
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
InputStream s = TestingUtilities.class.getResourceAsStream(classpath);
if (s == null) {
throw new Error("unable to find resource " + classpath);
}
return s;
}
}
public static byte[] loadTestResourceBytes(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
return TextFile.fileToBytes(n);
} else {
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
InputStream s = TestingUtilities.class.getResourceAsStream(classpath);
if (s == null) {
throw new Error("unable to find resource " + classpath);
}
return TextFile.streamToBytes(s);
}
}
public static String tempFile(String folder, String name) throws IOException {
String tmp = tempFolder(folder);
return Utilities.path(tmp, name);

View File

@ -1,39 +1,39 @@
package org.hl7.fhir.r5.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
@ -55,10 +55,10 @@ public class LiquidEngine implements IEvaluationContext {
public interface ILiquidEngineIcludeResolver {
public String fetchInclude(LiquidEngine engine, String name);
}
private IEvaluationContext externalHostServices;
private FHIRPathEngine engine;
private ILiquidEngineIcludeResolver includeResolver;
private ILiquidEngineIcludeResolver includeResolver;
private class LiquidEngineContext {
private Object externalContext;
@ -82,7 +82,7 @@ public class LiquidEngine implements IEvaluationContext {
engine = new FHIRPathEngine(context);
engine.setHostServices(this);
}
public ILiquidEngineIcludeResolver getIncludeResolver() {
return includeResolver;
}
@ -95,7 +95,7 @@ public class LiquidEngine implements IEvaluationContext {
return new LiquidParser(source).parse(sourceName);
}
public String evaluate(LiquidDocument document, Resource resource, Object appContext) throws FHIRException {
public String evaluate(LiquidDocument document, Base resource, Object appContext) throws FHIRException {
StringBuilder b = new StringBuilder();
LiquidEngineContext ctxt = new LiquidEngineContext(appContext);
for (LiquidNode n : document.body) {
@ -105,9 +105,10 @@ public class LiquidEngine implements IEvaluationContext {
}
private abstract class LiquidNode {
protected void closeUp() {}
protected void closeUp() {
}
public abstract void evaluate(StringBuilder b, Resource resource, LiquidEngineContext ctxt) throws FHIRException;
public abstract void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException;
}
private class LiquidConstant extends LiquidNode {
@ -125,7 +126,7 @@ public class LiquidEngine implements IEvaluationContext {
}
@Override
public void evaluate(StringBuilder b, Resource resource, LiquidEngineContext ctxt) {
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) {
b.append(constant);
}
}
@ -135,48 +136,191 @@ public class LiquidEngine implements IEvaluationContext {
private ExpressionNode compiled;
@Override
public void evaluate(StringBuilder b, Resource resource, LiquidEngineContext ctxt) throws FHIRException {
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
if (compiled == null)
compiled = engine.parse(statement);
b.append(engine.evaluateToString(ctxt, resource, resource, resource, compiled));
}
}
private class LiquidElsIf extends LiquidNode {
private String condition;
private ExpressionNode compiled;
private List<LiquidNode> body = new ArrayList<>();
@Override
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
for (LiquidNode n : body) {
n.evaluate(b, resource, ctxt);
}
}
}
private class LiquidIf extends LiquidNode {
private String condition;
private ExpressionNode compiled;
private List<LiquidNode> thenBody = new ArrayList<>();
private List<LiquidElsIf> elseIf = new ArrayList<>();
private List<LiquidNode> elseBody = new ArrayList<>();
@Override
public void evaluate(StringBuilder b, Resource resource, LiquidEngineContext ctxt) throws FHIRException {
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
if (compiled == null)
compiled = engine.parse(condition);
boolean ok = engine.evaluateToBoolean(ctxt, resource, resource, resource, compiled);
List<LiquidNode> list = ok ? thenBody : elseBody;
boolean ok = engine.evaluateToBoolean(ctxt, resource, resource, resource, compiled);
List<LiquidNode> list = null;
if (ok) {
list = thenBody;
} else {
list = elseBody;
for (LiquidElsIf i : elseIf) {
if (i.compiled == null)
i.compiled = engine.parse(i.condition);
ok = engine.evaluateToBoolean(ctxt, resource, resource, resource, i.compiled);
if (ok) {
list = i.body;
break;
}
}
}
for (LiquidNode n : list) {
n.evaluate(b, resource, ctxt);
}
}
}
private class LiquidLoop extends LiquidNode {
private class LiquidContinueExecuted extends FHIRException {
private static final long serialVersionUID = 4748737094188943721L;
}
private class LiquidContinue extends LiquidNode {
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
throw new LiquidContinueExecuted();
}
}
private class LiquidBreakExecuted extends FHIRException {
private static final long serialVersionUID = 6328496371172871082L;
}
private class LiquidBreak extends LiquidNode {
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
throw new LiquidBreakExecuted();
}
}
private class LiquidCycle extends LiquidNode {
private List<String> list = new ArrayList<>();
private int cursor = 0;
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
b.append(list.get(cursor));
cursor++;
if (cursor == list.size()) {
cursor = 0;
}
}
}
private class LiquidFor extends LiquidNode {
private String varName;
private String condition;
private ExpressionNode compiled;
private boolean reversed = false;
private int limit = -1;
private int offset = -1;
private List<LiquidNode> body = new ArrayList<>();
private List<LiquidNode> elseBody = new ArrayList<>();
@Override
public void evaluate(StringBuilder b, Resource resource, LiquidEngineContext ctxt) throws FHIRException {
if (compiled == null)
compiled = engine.parse(condition);
List<Base> list = engine.evaluate(ctxt, resource, resource, resource, compiled);
LiquidEngineContext lctxt = new LiquidEngineContext(ctxt);
for (Base o : list) {
lctxt.vars.put(varName, o);
for (LiquidNode n : body) {
n.evaluate(b, resource, lctxt);
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
if (compiled == null) {
ExpressionNodeWithOffset po = engine.parsePartial(condition, 0);
compiled = po.getNode();
if (po.getOffset() < condition.length()) {
parseModifiers(condition.substring(po.getOffset()));
}
}
List<Base> list = engine.evaluate(ctxt, resource, resource, resource, compiled);
LiquidEngineContext lctxt = new LiquidEngineContext(ctxt);
if (list.isEmpty()) {
for (LiquidNode n : elseBody) {
n.evaluate(b, resource, lctxt);
}
} else {
if (reversed) {
Collections.reverse(list);
}
int i = 0;
for (Base o : list) {
if (offset >= 0 && i < offset) {
i++;
continue;
}
if (limit >= 0 && i == limit) {
break;
}
lctxt.vars.put(varName, o);
boolean wantBreak = false;
for (LiquidNode n : body) {
try {
n.evaluate(b, resource, lctxt);
} catch (LiquidContinueExecuted e) {
break;
} catch (LiquidBreakExecuted e) {
wantBreak = true;
break;
}
}
if (wantBreak) {
break;
}
i++;
}
}
}
private void parseModifiers(String cnt) {
String src = cnt;
while (!Utilities.noString(cnt)) {
if (cnt.startsWith("reversed")) {
reversed = true;
cnt = cnt.substring(8);
} else if (cnt.startsWith("limit")) {
cnt = cnt.substring(5).trim();
if (!cnt.startsWith(":")) {
throw new FHIRException("Exception evaluating "+src+": limit is not followed by ':'");
}
cnt = cnt.substring(1).trim();
int i = 0;
while (i < cnt.length() && Character.isDigit(cnt.charAt(i))) {
i++;
}
if (i == 0) {
throw new FHIRException("Exception evaluating "+src+": limit is not followed by a number");
}
limit = Integer.parseInt(cnt.substring(0, i));
cnt = cnt.substring(i);
} else if (cnt.startsWith("offset")) {
cnt = cnt.substring(6).trim();
if (!cnt.startsWith(":")) {
throw new FHIRException("Exception evaluating "+src+": limit is not followed by ':'");
}
cnt = cnt.substring(1).trim();
int i = 0;
while (i < cnt.length() && Character.isDigit(cnt.charAt(i))) {
i++;
}
if (i == 0) {
throw new FHIRException("Exception evaluating "+src+": limit is not followed by a number");
}
offset = Integer.parseInt(cnt.substring(0, i));
cnt = cnt.substring(i);
} else {
throw new FHIRException("Exception evaluating "+src+": unexpected content at "+cnt);
}
}
}
}
@ -185,11 +329,11 @@ public class LiquidEngine implements IEvaluationContext {
private Map<String, ExpressionNode> params = new HashMap<>();
@Override
public void evaluate(StringBuilder b, Resource resource, LiquidEngineContext ctxt) throws FHIRException {
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
String src = includeResolver.fetchInclude(LiquidEngine.this, page);
LiquidParser parser = new LiquidParser(src);
LiquidDocument doc = parser.parse(page);
LiquidEngineContext nctxt = new LiquidEngineContext(ctxt.externalContext);
LiquidEngineContext nctxt = new LiquidEngineContext(ctxt.externalContext);
Tuple incl = new Tuple();
nctxt.vars.put("include", incl);
for (String s : params.keySet()) {
@ -201,7 +345,7 @@ public class LiquidEngine implements IEvaluationContext {
}
}
public static class LiquidDocument {
public static class LiquidDocument {
private List<LiquidNode> body = new ArrayList<>();
}
@ -225,64 +369,124 @@ public class LiquidEngine implements IEvaluationContext {
}
private char next2() {
if (cursor >= source.length()-1)
if (cursor >= source.length() - 1)
return 0;
else
return source.charAt(cursor+1);
return source.charAt(cursor + 1);
}
private char grab() {
cursor++;
return source.charAt(cursor-1);
return source.charAt(cursor - 1);
}
public LiquidDocument parse(String name) throws FHIRException {
this.name = name;
LiquidDocument doc = new LiquidDocument();
parseList(doc.body, new String[0]);
parseList(doc.body, false, new String[0]);
return doc;
}
private String parseList(List<LiquidNode> list, String[] terminators) throws FHIRException {
public LiquidCycle parseCycle(String cnt) {
LiquidCycle res = new LiquidCycle();
cnt = "," + cnt.substring(5).trim();
while (!Utilities.noString(cnt)) {
if (!cnt.startsWith(",")) {
throw new FHIRException("Script " + name + ": Script " + name + ": Found " + cnt.charAt(0) + " expecting ',' parsing cycle");
}
cnt = cnt.substring(1).trim();
if (!cnt.startsWith("\"")) {
throw new FHIRException("Script " + name + ": Script " + name + ": Found " + cnt.charAt(0) + " expecting '\"' parsing cycle");
}
cnt = cnt.substring(1);
int i = 0;
while (i < cnt.length() && cnt.charAt(i) != '"') {
i++;
}
if (i == cnt.length()) {
throw new FHIRException("Script " + name + ": Script " + name + ": Found unterminated string parsing cycle");
}
res.list.add(cnt.substring(0, i));
cnt = cnt.substring(i + 1).trim();
}
return res;
}
private String parseList(List<LiquidNode> list, boolean inLoop, String[] terminators) throws FHIRException {
String close = null;
while (cursor < source.length()) {
if (next1() == '{' && (next2() == '%' || next2() == '{' )) {
if (next2() == '%') {
if (next1() == '{' && (next2() == '%' || next2() == '{')) {
if (next2() == '%') {
String cnt = parseTag('%');
if (Utilities.existsInList(cnt, terminators)) {
if (isTerminator(cnt, terminators)) {
close = cnt;
break;
} else if (cnt.startsWith("if "))
list.add(parseIf(cnt));
else if (cnt.startsWith("loop "))
list.add(parseIf(cnt, inLoop));
else if (cnt.startsWith("loop ")) // loop is deprecated, but still
// supported
list.add(parseLoop(cnt.substring(4).trim()));
else if (cnt.startsWith("for "))
list.add(parseFor(cnt.substring(3).trim()));
else if (inLoop && cnt.equals("continue"))
list.add(new LiquidContinue());
else if (inLoop && cnt.equals("break"))
list.add(new LiquidBreak());
else if (inLoop && cnt.startsWith("cycle "))
list.add(parseCycle(cnt));
else if (cnt.startsWith("include "))
list.add(parseInclude(cnt.substring(7).trim()));
else
throw new FHIRException("Script "+name+": Script "+name+": Unknown flow control statement "+cnt);
throw new FHIRException("Script " + name + ": Script " + name + ": Unknown flow control statement " + cnt);
} else { // next2() == '{'
list.add(parseStatement());
}
} else {
if (list.size() == 0 || !(list.get(list.size()-1) instanceof LiquidConstant))
if (list.size() == 0 || !(list.get(list.size() - 1) instanceof LiquidConstant))
list.add(new LiquidConstant());
((LiquidConstant) list.get(list.size()-1)).addChar(grab());
((LiquidConstant) list.get(list.size() - 1)).addChar(grab());
}
}
for (LiquidNode n : list)
n.closeUp();
if (terminators.length > 0)
if (!Utilities.existsInList(close, terminators))
throw new FHIRException("Script "+name+": Script "+name+": Found end of script looking for "+terminators);
if (!isTerminator(close, terminators))
throw new FHIRException("Script " + name + ": Script " + name + ": Found end of script looking for " + terminators);
return close;
}
private LiquidNode parseIf(String cnt) throws FHIRException {
private boolean isTerminator(String cnt, String[] terminators) {
if (Utilities.noString(cnt)) {
return false;
}
for (String t : terminators) {
if (t.endsWith(" ")) {
if (cnt.startsWith(t)) {
return true;
}
} else {
if (cnt.equals(t)) {
return true;
}
}
}
return false;
}
private LiquidNode parseIf(String cnt, boolean inLoop) throws FHIRException {
LiquidIf res = new LiquidIf();
res.condition = cnt.substring(3).trim();
String term = parseList(res.thenBody, new String[] { "else", "endif"} );
if ("else".equals(term))
term = parseList(res.elseBody, new String[] { "endif"} );
String term = parseList(res.thenBody, inLoop, new String[] { "else", "elsif ", "endif" });
while (term.startsWith("elsif ")) {
LiquidElsIf elsIf = new LiquidElsIf();
res.elseIf.add(elsIf);
elsIf.condition = term.substring(5).trim();
term = parseList(elsIf.body, inLoop, new String[] { "elsif ", "else", "endif" });
}
if ("else".equals(term)) {
term = parseList(res.elseBody, inLoop, new String[] { "endif" });
}
return res;
}
@ -291,7 +495,7 @@ public class LiquidEngine implements IEvaluationContext {
while (i < cnt.length() && !Character.isWhitespace(cnt.charAt(i)))
i++;
if (i == cnt.length() || i == 0)
throw new FHIRException("Script "+name+": Error reading include: "+cnt);
throw new FHIRException("Script " + name + ": Error reading include: " + cnt);
LiquidInclude res = new LiquidInclude();
res.page = cnt.substring(0, i);
while (i < cnt.length() && Character.isWhitespace(cnt.charAt(i)))
@ -300,27 +504,26 @@ public class LiquidEngine implements IEvaluationContext {
int j = i;
while (i < cnt.length() && cnt.charAt(i) != '=')
i++;
if (i >= cnt.length() || j == i)
throw new FHIRException("Script "+name+": Error reading include: "+cnt);
if (i >= cnt.length() || j == i)
throw new FHIRException("Script " + name + ": Error reading include: " + cnt);
String n = cnt.substring(j, i);
if (res.params.containsKey(n))
throw new FHIRException("Script "+name+": Error reading include: "+cnt);
if (res.params.containsKey(n))
throw new FHIRException("Script " + name + ": Error reading include: " + cnt);
i++;
ExpressionNodeWithOffset t = engine.parsePartial(cnt, i);
i = t.getOffset();
res.params.put(n, t.getNode());
while (i < cnt.length() && Character.isWhitespace(cnt.charAt(i)))
i++;
ExpressionNodeWithOffset t = engine.parsePartial(cnt, i);
i = t.getOffset();
res.params.put(n, t.getNode());
while (i < cnt.length() && Character.isWhitespace(cnt.charAt(i)))
i++;
}
return res;
}
private LiquidNode parseLoop(String cnt) throws FHIRException {
int i = 0;
while (!Character.isWhitespace(cnt.charAt(i)))
i++;
LiquidLoop res = new LiquidLoop();
LiquidFor res = new LiquidFor();
res.varName = cnt.substring(0, i);
while (Character.isWhitespace(cnt.charAt(i)))
i++;
@ -328,36 +531,58 @@ public class LiquidEngine implements IEvaluationContext {
while (!Character.isWhitespace(cnt.charAt(i)))
i++;
if (!"in".equals(cnt.substring(j, i)))
throw new FHIRException("Script "+name+": Script "+name+": Error reading loop: "+cnt);
throw new FHIRException("Script " + name + ": Script " + name + ": Error reading loop: " + cnt);
res.condition = cnt.substring(i).trim();
parseList(res.body, new String[] { "endloop"} );
parseList(res.body, false, new String[] { "endloop" });
return res;
}
private LiquidNode parseFor(String cnt) throws FHIRException {
int i = 0;
while (!Character.isWhitespace(cnt.charAt(i)))
i++;
LiquidFor res = new LiquidFor();
res.varName = cnt.substring(0, i);
while (Character.isWhitespace(cnt.charAt(i)))
i++;
int j = i;
while (!Character.isWhitespace(cnt.charAt(i)))
i++;
if (!"in".equals(cnt.substring(j, i)))
throw new FHIRException("Script " + name + ": Script " + name + ": Error reading loop: " + cnt);
res.condition = cnt.substring(i).trim();
String term = parseList(res.body, true, new String[] { "endfor", "else" });
if ("else".equals(term)) {
parseList(res.elseBody, false, new String[] { "endfor" });
}
return res;
}
private String parseTag(char ch) throws FHIRException {
grab();
grab();
grab();
StringBuilder b = new StringBuilder();
while (cursor < source.length() && !(next1() == '%' && next2() == '}')) {
b.append(grab());
}
if (!(next1() == '%' && next2() == '}'))
throw new FHIRException("Script "+name+": Unterminated Liquid statement {% "+b.toString());
grab();
if (!(next1() == '%' && next2() == '}'))
throw new FHIRException("Script " + name + ": Unterminated Liquid statement {% " + b.toString());
grab();
grab();
return b.toString().trim();
}
private LiquidStatement parseStatement() throws FHIRException {
grab();
grab();
grab();
StringBuilder b = new StringBuilder();
while (cursor < source.length() && !(next1() == '}' && next2() == '}')) {
b.append(grab());
}
if (!(next1() == '}' && next2() == '}'))
throw new FHIRException("Script "+name+": Unterminated Liquid statement {{ "+b.toString());
grab();
if (!(next1() == '}' && next2() == '}'))
throw new FHIRException("Script " + name + ": Unterminated Liquid statement {{ " + b.toString());
grab();
grab();
LiquidStatement res = new LiquidStatement();
res.statement = b.toString().trim();
@ -471,7 +696,7 @@ public class LiquidEngine implements IEvaluationContext {
if (!cnt.equals(node.getAttributes().get(an))) {
node.getAttributes().put(an, cnt);
replaced = true;
}
}
}
}
return replaced;

View File

@ -174,24 +174,27 @@ public class CDARoundTripTests {
@Test
@Disabled
public void testSimple() throws IOException {
FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
SimpleWorkerContext context = SimpleWorkerContext.fromPackage(pcm.loadPackage("hl7.fhir.r4.core", "4.0.1"));
context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "any.xml"), "any.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "ii.xml"), "ii.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "cd.xml"), "cd.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "ce.xml"), "ce.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "cda.xml"), "cda.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "any.xml"), "any.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ii.xml"), "ii.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "cd.xml"), "cd.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ce.xml"), "ce.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "ed.xml"), "ed.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "st.xml"), "st.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "cda.xml"), "cda.xml", null);
for (StructureDefinition sd : context.getStructures()) {
if (!sd.hasSnapshot()) {
System.out.println("generate snapshot for " + sd.getUrl());
context.generateSnapshot(sd, true);
}
}
Element cda = Manager.parse(context, TestingUtilities.loadTestResourceStream("r5", "cda", "example.xml"), FhirFormat.XML);
Element cda = Manager.parse(context, TestingUtilities.loadTestResourceStream("validator", "cda", "example.xml"), FhirFormat.XML);
FHIRPathEngine fp = new FHIRPathEngine(context);
Assertions.assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(null, cda, cda, cda, fp.parse("ClinicalDocument.templateId.root")));
Assertions.assertEquals("SoEN", fp.evaluateToString(null, cda, cda, cda, fp.parse("ClinicalDocument.code.displayName")));
Assertions.assertEquals("SoEN2", fp.evaluateToString(null, cda, cda, cda, fp.parse("ClinicalDocument.code.sdtcDisplayName")));
}
}

View File

@ -57,7 +57,7 @@ public class LiquidEngineTests implements ILiquidEngineIcludeResolver {
this.test = test;
LiquidDocument doc = engine.parse(test.get("template").getAsString(), "test-script");
String output = engine.evaluate(doc, loadResource(), null);
Assertions.assertTrue(test.get("output").getAsString().equals(output));
Assertions.assertEquals(test.get("output").getAsString(), output);
}
@Override

View File

@ -10,16 +10,20 @@ import java.util.stream.Stream;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Questionnaire;
import org.hl7.fhir.r5.renderers.RendererFactory;
import org.hl7.fhir.r5.renderers.ResourceRenderer;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.QuestionnaireRendererMode;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
import org.hl7.fhir.utilities.xml.XMLUtil;
@ -35,6 +39,8 @@ import org.xml.sax.SAXException;
public class NarrativeGenerationTests {
public static final String WINDOWS = "WINDOWS";
private static final String HEADER = "<html><head>"+
"<link rel=\"stylesheet\" href=\"http://hl7.org/fhir/fhir.css\"/>"+
"<link rel=\"stylesheet\" href=\"http://hl7.org/fhir/dist/css/bootstrap.css\"/>"+
@ -87,10 +93,11 @@ public class NarrativeGenerationTests {
@ParameterizedTest(name = "{index}: file {0}")
@MethodSource("data")
public void test(String id, TestDetails test) throws Exception {
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", null, ResourceRendererMode.RESOURCE);
rc.setDestDir("C:\\work\\org.hl7.fhir\\packages\\packages\\hl7.fhir.pubpack\\package\\other\\");
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
rc.setDestDir("");
rc.setHeader(test.isHeader());
rc.setDefinitionsTarget("test.html");
rc.setTerminologyServiceOptions(TerminologyServiceOptions.defaults());
IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-expected.xml"), new FileOutputStream(TestingUtilities.tempFile("narrative", test.getId() + "-expected.xml")));
DomainResource source = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-input.xml"));
DomainResource target = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-expected.xml"));

View File

@ -24,7 +24,7 @@ public class NarrativeGeneratorTests {
@BeforeAll
public static void setUp() throws FHIRException {
rc = new RenderingContext(TestingUtilities.context(), null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
rc = new RenderingContext(TestingUtilities.context(), null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
}
@Test

View File

@ -27,7 +27,7 @@ public class ResourceRoundTripTests {
@Test
public void test() throws IOException, FHIRException, EOperationOutcome {
DomainResource res = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "unicode.xml"));
RenderingContext rc = new RenderingContext(TestingUtilities.context(), null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(TestingUtilities.context(), null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
RendererFactory.factory(res, rc).render(res);
IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "unicode.xml"), new FileOutputStream(TestingUtilities.tempFile("gen", "unicode.xml")));
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(TestingUtilities.tempFile("gen", "unicode.out.xml")), res);

View File

@ -550,7 +550,7 @@ public class SnapShotGenerationTests {
throw e;
}
if (output.getDifferential().hasElement()) {
RenderingContext rc = new RenderingContext(TestingUtilities.context(), null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(TestingUtilities.context(), null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
rc.setProfileUtilities(new ProfileUtilities(TestingUtilities.context(), null, new TestPKP()));
RendererFactory.factory(output, rc).render(output);
}

View File

@ -1,117 +0,0 @@
package org.hl7.fhir.r5.test;
import java.io.File;
import java.io.IOException;
import org.apache.commons.lang3.SystemUtils;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class UtilitiesTests {
private static final String XHTML_SIMPLE = "<div>Some Text</div>";
private static final String XHTML_LANGS = "<div xml:lang=\"en-US\" lang=\"en-US\">Some Text</div>";
public static final String OSX = "OS X";
public static final String MAC = "MAC";
public static final String WINDOWS = "WINDOWS";
public static final String LINUX = "Linux";
public static final String TEST_TXT = "test.txt";
public static final String LINUX_TEMP_DIR = "/tmp/";
public static final String LINUX_USER_DIR = System.getProperty("user.home") + "/";
public static final String LINUX_JAVA_HOME = System.getenv("JAVA_HOME") + "/";
public static final String WIN_TEMP_DIR = "c:\\temp\\";
public static final String WIN_USER_DIR = System.getProperty("user.home") + "\\";
public static final String WIN_JAVA_HOME = System.getenv("JAVA_HOME") + "\\";
public static final String OSX_USER_DIR = System.getProperty("user.home") + "/";
public static final String OSX_JAVA_HOME = System.getenv("JAVA_HOME") + "/";
@DisplayName("Test Utilities.path maps temp directory correctly")
public void testTempDirPath() throws IOException {
Assertions.assertEquals(Utilities.path("[tmp]", TEST_TXT), getTempDirectory() + TEST_TXT);
}
@Test
@DisplayName("Test Utilities.path maps user directory correctly")
public void testUserDirPath() throws IOException {
Assertions.assertEquals(Utilities.path("[user]", TEST_TXT), getUserDirectory() + TEST_TXT);
}
@Test
@DisplayName("Test Utilities.path maps JAVA_HOME correctly")
public void testJavaHomeDirPath() throws IOException {
Assertions.assertEquals(Utilities.path("[JAVA_HOME]", TEST_TXT), getJavaHomeDirectory() + TEST_TXT);
}
private String getJavaHomeDirectory() {
String os = SystemUtils.OS_NAME;
if (os.contains(OSX) || os.contains(MAC)) {
return OSX_JAVA_HOME;
} else if (os.contains(LINUX)) {
return LINUX_JAVA_HOME;
} else if (os.toUpperCase().contains(WINDOWS)) {
return WIN_JAVA_HOME;
} else {
throw new IllegalStateException("OS not recognized...cannot verify created directories.");
}
}
private String getUserDirectory() {
String os = SystemUtils.OS_NAME;
if (os.contains(OSX) || os.contains(MAC)) {
return OSX_USER_DIR;
} else if (os.contains(LINUX)) {
return LINUX_USER_DIR;
} else if (os.toUpperCase().contains(WINDOWS)) {
return WIN_USER_DIR;
} else {
throw new IllegalStateException("OS not recognized...cannot verify created directories.");
}
}
private String getTempDirectory() throws IOException {
String os = SystemUtils.OS_NAME;
if (os.contains(OSX) || os.contains(MAC)) {
return getOsxTempDir();
} else if (os.contains(LINUX)) {
return LINUX_TEMP_DIR;
} else if (os.toUpperCase().contains(WINDOWS)) {
return WIN_TEMP_DIR;
} else {
throw new IllegalStateException("OS not recognized...cannot verify created directories.");
}
}
/**
* Getting the temporary directory in OSX is a little different from Linux and Windows. We need to create a temporary
* file and then extract the directory path from it.
*
* @return Full path to tmp directory on OSX machines.
* @throws IOException
*/
public static String getOsxTempDir() throws IOException {
File file = File.createTempFile("throwaway", ".file");
return file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf('/')) + '/';
}
@Test
public void testXhtmlLangAttributes() throws IOException {
run(XHTML_SIMPLE);
run(XHTML_LANGS);
}
public void run(String src) throws IOException {
XhtmlNode node = new XhtmlParser().parse(src, "div");
String xhtml = new XhtmlComposer(false).compose(node);
Assertions.assertTrue(src.equals(xhtml));
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -652,12 +652,12 @@ public class Utilities {
}
private static boolean isDigit(char c) {
public static boolean isDigit(char c) {
return (c >= '0') && (c <= '9');
}
private static boolean isAlphabetic(char c) {
public static boolean isAlphabetic(char c) {
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
}

View File

@ -46,11 +46,15 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@ -80,15 +84,17 @@ import java.util.Map.Entry;
* @author Grahame Grieve
*/
public class FilesystemPackageCacheManager extends BasePackageCacheManager implements IPackageCacheManager {
public static final String PRIMARY_SERVER = "http://packages.fhir.org";
public static final String SECONDARY_SERVER = "http://packages2.fhir.org/packages";
// private static final String SECONDARY_SERVER = "http://local.fhir.org:960/packages";
public static final String PACKAGE_REGEX = "^[a-z][a-z0-9\\_\\-]*(\\.[a-z0-9\\_\\-]+)+$";
public static final String PACKAGE_VERSION_REGEX = "^[a-z][a-z0-9\\_\\-]*(\\.[a-z0-9\\_\\-]+)+\\#[a-z0-9\\-\\_]+(\\.[a-z0-9\\-\\_]+)*$";
private static final Logger ourLog = LoggerFactory.getLogger(FilesystemPackageCacheManager.class);
private static final String CACHE_VERSION = "3"; // second version - see wiki page
public static final String PRIMARY_SERVER = "http://packages.fhir.org";
public static final String SECONDARY_SERVER = "http://packages2.fhir.org/packages";
private String cacheFolder;
private boolean progress = true;
private List<NpmPackage> temporaryPackages = new ArrayList<NpmPackage>();
private List<NpmPackage> temporaryPackages = new ArrayList<>();
private boolean buildLoaded = false;
private Map<String, String> ciList = new HashMap<String, String>();
private JsonArray buildInfo;
@ -126,11 +132,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return cacheFolder;
}
// private List<String> allUrls;
// private Map<String, VersionHistory> historyCache = new HashMap<>();
// ========================= Initialization ============================================================================
private List<String> sorted(String[] keys) {
List<String> names = new ArrayList<String>();
for (String s : keys)
@ -144,8 +145,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return pi;
}
// ========================= Utilities ============================================================================
private JsonObject fetchJson(String source) throws IOException {
URL url = new URL(source);
URLConnection c = url.openConnection();
@ -155,16 +154,19 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
private void clearCache() throws IOException {
for (File f : new File(cacheFolder).listFiles()) {
if (f.isDirectory()) {
Utilities.clearDirectory(f.getAbsolutePath());
try {
FileUtils.deleteDirectory(f);
} catch (Exception e1) {
new CacheLock(f.getName()).doWithLock(() -> {
Utilities.clearDirectory(f.getAbsolutePath());
try {
FileUtils.deleteDirectory(f);
} catch (Exception e2) {
// just give up
} catch (Exception e1) {
try {
FileUtils.deleteDirectory(f);
} catch (Exception e2) {
// just give up
}
}
}
return null; // must return something
});
} else if (!f.getName().equals("packages.ini"))
FileUtils.forceDelete(f);
}
@ -198,7 +200,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
}
}
private void listSpecs(Map<String, String> specList, String server) throws IOException {
CachingPackageClient pc = new CachingPackageClient(server);
List<PackageClient.PackageInfo> matches = pc.search(null, null, null, false);
@ -256,6 +257,8 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
clearCache();
}
// ========================= Utilities ============================================================================
/**
* Remove a particular package from the cache
*
@ -264,15 +267,18 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
* @throws IOException
*/
public void removePackage(String id, String ver) throws IOException {
String f = Utilities.path(cacheFolder, id + "#" + ver);
File ff = new File(f);
if (ff.exists()) {
Utilities.clearDirectory(f);
IniFile ini = new IniFile(Utilities.path(cacheFolder, "packages.ini"));
ini.removeProperty("packages", id + "#" + ver);
ini.save();
ff.delete();
}
new CacheLock(id + "#" + ver).doWithLock(() -> {
String f = Utilities.path(cacheFolder, id + "#" + ver);
File ff = new File(f);
if (ff.exists()) {
Utilities.clearDirectory(f);
IniFile ini = new IniFile(Utilities.path(cacheFolder, "packages.ini"));
ini.removeProperty("packages", id + "#" + ver);
ini.save();
ff.delete();
}
return null;
});
}
/**
@ -311,9 +317,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return null;
}
// ========================= Package Mgmt API =======================================================================
/**
* Add an already fetched package to the cache
*/
@ -339,71 +342,78 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
if (version == null)
version = npm.version();
String packRoot = Utilities.path(cacheFolder, id + "#" + version);
try {
Utilities.createDirectory(packRoot);
Utilities.clearDirectory(packRoot);
String v = version;
return new CacheLock(id + "#" + version).doWithLock(() -> {
NpmPackage pck = null;
String packRoot = Utilities.path(cacheFolder, id + "#" + v);
try {
// ok, now we have a lock on it... check if something created it while we were waiting
if (!new File(packRoot).exists()) {
Utilities.createDirectory(packRoot);
Utilities.clearDirectory(packRoot);
int i = 0;
int c = 0;
int size = 0;
for (Entry<String, NpmPackageFolder> e : npm.getFolders().entrySet()) {
String dir = e.getKey().equals("package") ? Utilities.path(packRoot, "package") : Utilities.path(packRoot, "package", e.getKey());
;
if (!(new File(dir).exists()))
Utilities.createDirectory(dir);
for (Entry<String, byte[]> fe : e.getValue().getContent().entrySet()) {
String fn = Utilities.path(dir, fe.getKey());
byte[] cnt = fe.getValue();
TextFile.bytesToFile(cnt, fn);
size = size + cnt.length;
i++;
if (progress && i % 50 == 0) {
c++;
System.out.print(".");
if (c == 120) {
System.out.println("");
System.out.print(" ");
c = 2;
int i = 0;
int c = 0;
int size = 0;
for (Entry<String, NpmPackageFolder> e : npm.getFolders().entrySet()) {
String dir = e.getKey().equals("package") ? Utilities.path(packRoot, "package") : Utilities.path(packRoot, "package", e.getKey());
if (!(new File(dir).exists()))
Utilities.createDirectory(dir);
for (Entry<String, byte[]> fe : e.getValue().getContent().entrySet()) {
String fn = Utilities.path(dir, fe.getKey());
byte[] cnt = fe.getValue();
TextFile.bytesToFile(cnt, fn);
size = size + cnt.length;
i++;
if (progress && i % 50 == 0) {
c++;
System.out.print(".");
if (c == 120) {
System.out.println("");
System.out.print(" ");
c = 2;
}
}
}
}
}
}
IniFile ini = new IniFile(Utilities.path(cacheFolder, "packages.ini"));
ini.setTimeStampFormat("yyyyMMddhhmmss");
ini.setTimestampProperty("packages", id + "#" + version, Timestamp.from(Instant.now()), null);
ini.setIntegerProperty("package-sizes", id + "#" + version, size, null);
ini.save();
if (progress)
System.out.println(" done.");
NpmPackage pck = loadPackageInfo(packRoot);
if (!id.equals(JSONUtil.str(npm.getNpm(), "name")) || !version.equals(JSONUtil.str(npm.getNpm(), "version"))) {
if (!id.equals(JSONUtil.str(npm.getNpm(), "name"))) {
npm.getNpm().addProperty("original-name", JSONUtil.str(npm.getNpm(), "name"));
npm.getNpm().remove("name");
npm.getNpm().addProperty("name", id);
IniFile ini = new IniFile(Utilities.path(cacheFolder, "packages.ini"));
ini.setTimeStampFormat("yyyyMMddhhmmss");
ini.setTimestampProperty("packages", id + "#" + v, Timestamp.from(Instant.now()), null);
ini.setIntegerProperty("package-sizes", id + "#" + v, size, null);
ini.save();
if (progress)
System.out.println(" done.");
}
if (!version.equals(JSONUtil.str(npm.getNpm(), "version"))) {
npm.getNpm().addProperty("original-version", JSONUtil.str(npm.getNpm(), "version"));
npm.getNpm().remove("version");
npm.getNpm().addProperty("version", version);
pck = loadPackageInfo(packRoot);
if (!id.equals(JSONUtil.str(npm.getNpm(), "name")) || !v.equals(JSONUtil.str(npm.getNpm(), "version"))) {
if (!id.equals(JSONUtil.str(npm.getNpm(), "name"))) {
npm.getNpm().addProperty("original-name", JSONUtil.str(npm.getNpm(), "name"));
npm.getNpm().remove("name");
npm.getNpm().addProperty("name", id);
}
if (!v.equals(JSONUtil.str(npm.getNpm(), "version"))) {
npm.getNpm().addProperty("original-version", JSONUtil.str(npm.getNpm(), "version"));
npm.getNpm().remove("version");
npm.getNpm().addProperty("version", v);
}
TextFile.stringToFile(new GsonBuilder().setPrettyPrinting().create().toJson(npm.getNpm()), Utilities.path(cacheFolder, id + "#" + v, "package", "package.json"), false);
}
TextFile.stringToFile(new GsonBuilder().setPrettyPrinting().create().toJson(npm.getNpm()), Utilities.path(cacheFolder, id + "#" + version, "package", "package.json"), false);
} catch (Exception e) {
try {
// don't leave a half extracted package behind
System.out.println("Clean up package " + packRoot + " because installation failed: " + e.getMessage());
e.printStackTrace();
Utilities.clearDirectory(packRoot);
new File(packRoot).delete();
} catch (Exception ei) {
// nothing
}
throw e;
}
return pck;
} catch (Exception e) {
try {
// don't leave a half extracted package behind
Utilities.clearDirectory(packRoot);
new File(packRoot).delete();
} catch (Exception ei) {
// nothing
}
throw e;
}
});
}
@Override
@ -461,7 +471,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return addPackageToCache(id, version == null ? source.version : version, source.stream, source.url);
}
private InputStream fetchFromUrlSpecific(String source, boolean optional) throws FHIRException {
try {
URL url = new URL(source);
@ -488,7 +497,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
}
}
private String getPackageUrlFromBuildList(String packageId) throws IOException {
checkBuildLoaded();
for (JsonElement n : buildInfo) {
@ -500,9 +508,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return null;
}
// ---------- Current Build SubSystem --------------------------------------------------------------------------------------
private void addCIBuildSpecs(Map<String, String> specList) throws IOException {
checkBuildLoaded();
for (JsonElement n : buildInfo) {
@ -524,6 +529,9 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return retVal;
}
// ========================= Package Mgmt API =======================================================================
private String getPackageIdFromBuildList(String canonical) throws IOException {
checkBuildLoaded();
if (buildInfo != null) {
@ -667,6 +675,9 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return null;
}
// ---------- Current Build SubSystem --------------------------------------------------------------------------------------
private String fetchVersionTheOldWay(String id) throws IOException {
String url = getUrlForPackage(id);
if (url == null) {
@ -710,7 +721,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return res;
}
/**
* if you don't provide and implementation of this interface, the PackageCacheManager will use the web directly.
* <p>
@ -723,6 +733,10 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
InputStream resolvePackage(String packageId, String version);
}
public interface CacheLockFunction<T> {
T get() throws IOException;
}
public class BuildRecordSorter implements Comparator<BuildRecord> {
@Override
@ -765,6 +779,8 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
}
public class VersionHistory {
private String id;
private String canonical;
@ -803,11 +819,39 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
}
}
private static String userDir() throws IOException {
return Utilities.path(System.getProperty("user.home"), ".fhir", "packages");
public class CacheLock {
private final File lockFile;
public CacheLock(String name) throws IOException {
this.lockFile = new File(cacheFolder, name + ".lock");
if (!lockFile.isFile()) {
TextFile.stringToFile("", lockFile);
}
}
public <T> T doWithLock(CacheLockFunction<T> f) throws FileNotFoundException, IOException {
try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
final FileLock fileLock = channel.lock();
T result = null;
try {
result = f.get();
} finally {
fileLock.release();
}
if (!lockFile.delete()) {
lockFile.deleteOnExit();
}
return result;
}
}
}
//public List<String> getUrls() throws IOException {
// if (allUrls == null)
// {

View File

@ -570,6 +570,9 @@ public class NpmPackage {
public boolean hasFile(String folder, String file) throws IOException {
NpmPackageFolder f = folders.get(folder);
if (f == null) {
f = folders.get(Utilities.path("package", folder));
}
return f != null && f.hasFile(file);
}

View File

@ -0,0 +1,118 @@
package org.hl7.fhir.utilities.cache;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* intenral use only - set the file name to edit in main(), and fill out the edit routine
*
* @author grahame
*
*/
public class PackageHacker {
public static void main(String[] args) throws FileNotFoundException, IOException {
new PackageHacker().edit("C:\\Users\\graha\\Downloads\\package.tgz");
}
private void edit(String name) throws FileNotFoundException, IOException {
File f = new File(name);
if (!f.exists())
throw new Error("Unable to find "+f.getAbsolutePath());
NpmPackage pck = NpmPackage.fromPackage(new FileInputStream(f));
System.out.println("Altering Package "+f.getAbsolutePath());
System.out.println(nice(pck.getNpm()));
change(pck.getNpm(), pck.getFolders().get("package").getContent());
System.out.println("Revised Package");
System.out.println("=======================");
System.out.println(nice(pck.getNpm()));
System.out.println("=======================");
System.out.print("save? y/n: ");
int r = System.in.read();
if (r == 'y') {
f.renameTo(new File(Utilities.changeFileExt(name, ".tgz.bak")));
pck.save(new FileOutputStream(f));
}
}
private String nice(JsonObject json) {
return new GsonBuilder().setPrettyPrinting().create().toJson(json);
}
private void change(JsonObject npm, Map<String, byte[]> content) throws FileNotFoundException, IOException {
// fixVersions(npm);
// npm.addProperty("name", "hl7.terminology");
npm.addProperty("url", "http://terminology.hl7.org/1.0.0");
// npm.remove("canonical");
// npm.addProperty("canonical", "http://hl7.org/fhir/us/davinci-drug-formulary");
//// npm.remove("description");
//// npm.addProperty("description", "Group Wrapper that includes all the R4 packages");
// npm.remove("url");
// npm.addProperty("url", "http://hl7.org/fhir/us/davinci-drug-formulary/Jun2019");
// JsonObject dep = npm.getAsJsonObject("dependencies");
// dep.remove("hl7.fhir.r3.core");
// dep.remove("hl7.fhir.r3.examples");
// dep.remove("hl7.fhir.r3.expansions");
// dep.remove("hl7.fhir.r3.elements");
// dep.addProperty("hl7.fhir.r3.core", "4.0.1");
// dep.addProperty("hl7.fhir.r3.examples", "4.0.1");
// dep.addProperty("hl7.fhir.r3.expansions", "4.0.1");
// dep.addProperty("hl7.fhir.r3.elements", "4.0.1");
}
private void fixVersions(JsonObject npm) {
npm.remove("fhirVersions");
JsonArray a = new JsonArray();
npm.add("fhirVersions", a);
a.add("4.0.1");
npm.remove("fhir-version-list");
a = new JsonArray();
npm.add("fhir-version-list", a);
a.add("4.0.1");
}
private void setProperty(JsonObject npm, String name, String value) {
npm.remove("homepage");
npm.addProperty("homepage", "http://hl7.org/fhir");
}
private void fixNames(Map<String, byte[]> content) {
List<String> names = new ArrayList<>();
names.addAll(content.keySet());
for (String s : names) {
if (s.endsWith("json") && !s.endsWith(".json")) {
String n = s.substring(0, s.length()-4)+".json";
content.put(n, content.get(s));
content.remove(s);
}
}
}
private void addContentFrom(String folder, Map<String, byte[]> content) throws FileNotFoundException, IOException {
for (File f : new File(folder).listFiles()) {
if (f.getName().endsWith(".json") && !f.getName().endsWith(".canonical.json")) {
String cnt = TextFile.fileToString(f);
if (cnt.contains("\"resourceType\"")) {
content.put("package/"+f.getName(), TextFile.fileToBytes(f));
}
}
}
}
}

View File

@ -491,30 +491,10 @@ public class I18nConstants {
public static final String MEASURE_M_CRITERIA_CQL_NO_ELM = "MEASURE_M_CRITERIA_CQL_NO_ELM";
public static final String MEASURE_M_CRITERIA_CQL_ELM_NOT_VALID = "MEASURE_M_CRITERIA_CQL_ELM_NOT_VALID";
public static final String MEASURE_M_CRITERIA_CQL_NOT_FOUND = "MEASURE_M_CRITERIA_CQL_NOT_FOUND";
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
//public static final String
public static final String VALIDATION_VAL_PROFILE_WRONGTYPE2 = "Validation_VAL_Profile_WrongType2";
public static final String XHTML_URL_EMPTY = "XHTML_URL_EMPTY";
public static final String XHTML_URL_INVALID_CHARS = "XHTML_URL_INVALID_CHARS";
public static final String TERMINOLOGY_TX_SYSTEM_HTTPS = "TERMINOLOGY_TX_SYSTEM_HTTPS";
public static final String CODESYSTEM_CS_NO_VS_NOTCOMPLETE = "CODESYSTEM_CS_NO_VS_NOTCOMPLETE";
public static final String VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT = "VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT";
}

View File

@ -0,0 +1,84 @@
package org.hl7.fhir.utilities.tests;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
public class BaseTestingUtilities {
static public boolean silent;
public static String loadTestResource(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
// ok, we'll resolve this locally
return TextFile.fileToString(new File(n));
} else {
// resolve from the package
String contents;
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
try (InputStream inputStream = BaseTestingUtilities.class.getResourceAsStream(classpath)) {
if (inputStream == null) {
throw new IOException("Can't find file on classpath: " + classpath);
}
contents = IOUtils.toString(inputStream, java.nio.charset.StandardCharsets.UTF_8);
}
return contents;
}
}
public static InputStream loadTestResourceStream(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
return new FileInputStream(n);
} else {
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
InputStream s = BaseTestingUtilities.class.getResourceAsStream(classpath);
if (s == null) {
throw new Error("unable to find resource " + classpath);
}
return s;
}
}
public static byte[] loadTestResourceBytes(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
return TextFile.fileToBytes(n);
} else {
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
InputStream s = BaseTestingUtilities.class.getResourceAsStream(classpath);
if (s == null) {
throw new Error("unable to find resource " + classpath);
}
return TextFile.streamToBytes(s);
}
}
public static boolean findTestResource(String... paths) throws IOException {
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
return new File(n).exists();
} else {
String classpath = ("/org/hl7/fhir/testcases/" + Utilities.pathURL(paths));
try {
InputStream inputStream = BaseTestingUtilities.class.getResourceAsStream(classpath);
return inputStream != null;
} catch (Throwable t) {
return false;
}
}
}
// TODO: JA need to figure out how to detect that we're running in maven
public static boolean isTryToLoadFromFileSystem() {
return !"true".equals(System.getProperty("dont_load_from_filesystem"));
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.utils.formats;
package org.hl7.fhir.utilities.turtle;
/*
Copyright (c) 2011+, HL7, Inc.

View File

@ -506,6 +506,10 @@ public class XhtmlNode implements IBaseXhtml {
return addTag("td");
}
public XhtmlNode td(String clss) {
return addTag("td").attribute("class", clss);
}
public XhtmlNode colspan(String n) {
return setAttribute("colspan", n);
}
@ -545,13 +549,31 @@ public class XhtmlNode implements IBaseXhtml {
public XhtmlNode i() {
return addTag("i");
}
public XhtmlNode tx(String cnt) {
return addText(cnt);
}
public XhtmlNode tx(int cnt) {
return addText(Integer.toString(cnt));
}
public XhtmlNode ah(String href) {
return addTag("a").attribute("href", href);
}
public XhtmlNode ah(String href, String title) {
return addTag("a").attribute("href", href).attribute("title", title);
}
public XhtmlNode img(String src) {
return addTag("img").attribute("src", src);
}
public XhtmlNode img(String src, String title) {
return addTag("img").attribute("src", src).attribute("title", title);
}
public void an(String href) {
addTag("a").attribute("name", href).tx(" ");
}
@ -659,6 +681,13 @@ public class XhtmlNode implements IBaseXhtml {
return this;
}
public XhtmlNode addChildren(XhtmlNode x) {
if (x != null) {
getChildNodes().addAll(x.getChildNodes());
}
return this;
}
public XhtmlNode input(String name, String type, String placeholder, int size) {
XhtmlNode p = new XhtmlNode(NodeType.Element, "input");
@ -686,7 +715,23 @@ public class XhtmlNode implements IBaseXhtml {
getChildNodes().add(p);
return p;
}
public XhtmlNode remove(XhtmlNode x) {
getChildNodes().remove(x);
return this;
}
public void clear() {
getChildNodes().clear();
}
}

View File

@ -495,3 +495,6 @@ TYPE_SPECIFIC_CHECKS_DT_ATT_TOO_LONG = Attachment size is {0} bytes which exceed
TYPE_SPECIFIC_CHECKS_DT_ATT_NO_CONTENT = Attachments have data and/or url, or else must have either contentType and/oor language
TYPE_SPECIFIC_CHECKS_DT_BASE64_TOO_LONG = Base64 size is {0} bytes which exceeds the stated limit of {1} bytes
TYPE_SPECIFIC_CHECKS_DT_DECIMAL_CHARS = Found {0} decimal places which exceeds the stated limit of {1} digits
Validation_VAL_Profile_WrongType = Specified profile type was "{0}", but found type "{1}"
Validation_VAL_Profile_WrongType2 = Type mismatch processing profile {0} at path {1}: The element type is {4}, but the profile {3} is for a different type {2}
VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT = Illegal constraint in profile {0} at path {1} - cannot constrain to type {2} from base types {3}

View File

@ -1,7 +1,7 @@
package org.hl7.fhir.utilities.tests;
import org.hl7.fhir.utilities.cache.CachingPackageClient;
import org.hl7.fhir.utilities.cache.BasePackageClient.PackageInfo;
import org.hl7.fhir.utilities.cache.PackageClient.PackageInfo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -1167,7 +1167,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
private OperationOutcome exceptionToOutcome(Exception ex) throws IOException, FHIRException, EOperationOutcome {
OperationOutcome op = new OperationOutcome();
op.addIssue().setCode(org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXCEPTION).setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.FATAL).getDetails().setText(ex.getMessage());
RenderingContext rc = new RenderingContext(context, null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
RendererFactory.factory(op, rc).render(op);
return op;
}
@ -1183,7 +1183,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
op.getIssue().add(OperationOutcomeUtilities.convertToIssue(vm, op));
}
RenderingContext rc = new RenderingContext(context, null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
RendererFactory.factory(op, rc).render(op);
return op;
}
@ -1241,7 +1241,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
public DomainResource generate(String source, String version) throws Exception {
Content cnt = loadContent(source, "validate");
Resource res = loadResourceByVersion(version, cnt.focus, source);
RenderingContext rc = new RenderingContext(context, null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
RendererFactory.factory(res, rc).render((DomainResource) res);
return (DomainResource) res;
}
@ -1546,7 +1546,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
}
private void genScanOutputItem(ScanOutputItem item, String filename) throws IOException, FHIRException, EOperationOutcome {
RenderingContext rc = new RenderingContext(context, null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
rc.setNoSlowLookup(true);
RendererFactory.factory(item.outcome, rc).render(item.outcome);
String s = new XhtmlComposer(XhtmlComposer.HTML).compose(item.outcome.getText().getDiv());

View File

@ -731,7 +731,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, System.nanoTime());
if (ss) {
t = System.nanoTime();
ValidationResult s = context.validateCode(new ValidationOptions(stack.getWorkingLang()), system, code, checkDisplay ? display : null);
ValidationResult s = checkCodeOnServer(stack, code, system, display, checkDisplay);
timeTracker.tx(t, System.nanoTime());
if (s == null)
return true;
@ -894,7 +894,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) {
// ignore this since we can't validate but it doesn't matter..
} else {
ValidationResult vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()).checkValueSetOnly(), cc, valueset); // we're going to validate the codings directly, so only check the valueset
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, true); // we're going to validate the codings directly, so only check the valueset
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
@ -936,7 +936,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (bindingsOk) {
for (Coding nextCoding : cc.getCoding()) {
if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) {
ValidationResult vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()).noCheckValueSetMembership(), nextCoding, valueset);
ValidationResult vr = checkCodeOnServer(stack, valueset, nextCoding, false);
if (vr.getSeverity() != null) {
if (vr.getSeverity() == IssueSeverity.INFORMATION) {
txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
@ -1003,7 +1003,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) {
// ignore this since we can't validate but it doesn't matter..
} else {
ValidationResult vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()), cc, valueset); // we're going to validate the codings directly
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false); // we're going to validate the codings directly
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
@ -1047,7 +1047,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String nextCode = nextCoding.getCode();
String nextSystem = nextCoding.getSystem();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
ValidationResult vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()), nextSystem, nextCode, null);
ValidationResult vr = checkCodeOnServer(stack, nextCode, nextSystem, null, false);
if (!vr.isOk()) {
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_NOTVALID, nextCode, nextSystem);
}
@ -1096,7 +1096,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime();
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()), c, valueset);
vr = checkCodeOnServer(stack, valueset, c, true);
}
timeTracker.tx(t, System.nanoTime());
if (vr != null && !vr.isOk()) {
@ -1221,7 +1221,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()), cc, valueset);
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false);
timeTracker.tx(t, System.nanoTime());
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1240,7 +1240,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()), c, valueset);
ValidationResult vr = checkCodeOnServer(stack, valueset, c, true);
timeTracker.tx(t, System.nanoTime());
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1259,7 +1259,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()), value, valueset);
ValidationResult vr = checkCodeOnServer(stack, valueset, value, new ValidationOptions(stack.getWorkingLang()));
timeTracker.tx(t, System.nanoTime());
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1315,7 +1315,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime();
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(new ValidationOptions(stack.getWorkingLang()), c, valueset);
vr = checkCodeOnServer(stack, valueset, c, true);
}
timeTracker.tx(t, System.nanoTime());
if (vr != null && !vr.isOk()) {
@ -2100,7 +2100,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
ValidationOptions options = new ValidationOptions(stack.getWorkingLang()).guessSystem();
vr = context.validateCode(options, value, vs);
vr = checkCodeOnServer(stack, vs, value, options);
}
timeTracker.tx(t, System.nanoTime());
if (vr != null && !vr.isOk()) {
@ -4541,4 +4541,29 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet vs, String value, ValidationOptions options) {
return context.validateCode(options, value, vs);
}
// no delay on this one?
public ValidationResult checkCodeOnServer(NodeStack stack, String code, String system, String display, boolean checkDisplay) {
return context.validateCode(new ValidationOptions(stack.getWorkingLang()), system, code, checkDisplay ? display : null);
}
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, Coding c, boolean checkMembership) {
if (checkMembership) {
return context.validateCode(new ValidationOptions(stack.getWorkingLang()), c, valueset);
} else {
return context.validateCode(new ValidationOptions(stack.getWorkingLang()).noCheckValueSetMembership(), c, valueset);
}
}
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, CodeableConcept cc, boolean vsOnly) {
if (vsOnly) {
return context.validateCode(new ValidationOptions(stack.getWorkingLang()).checkValueSetOnly(), cc, valueset);
} else {
return context.validateCode(new ValidationOptions(stack.getWorkingLang()), cc, valueset);
}
}
}

View File

@ -10,7 +10,7 @@ import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.cache.NpmPackage;
import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.cache.CachingPackageClient;
import org.hl7.fhir.utilities.cache.BasePackageClient.PackageInfo;
import org.hl7.fhir.utilities.cache.PackageClient.PackageInfo;
import org.hl7.fhir.utilities.cache.ToolsVersion;
public class PackageValidator {

View File

@ -90,61 +90,61 @@ public class ComparisonTests {
@MethodSource("data")
public void test(String name, JsonObject content) throws Exception {
this.content = content;
if (content.has("use-test") && !content.get("use-test").getAsBoolean())
return;
if (context == null) {
System.out.println("---- Load R5 ----------------------------------------------------------------");
context = TestingUtilities.context();
context = TestingUtilities.context();
}
if (!new File(Utilities.path("[tmp]", "comparison")).exists()) {
System.out.println("---- Set up Output ----------------------------------------------------------");
Utilities.createDirectory(Utilities.path("[tmp]", "comparison"));
FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
NpmPackage npm = pcm.loadPackage("hl7.fhir.pubpack", "0.0.4");
NpmPackage npm = pcm.loadPackage("hl7.fhir.pubpack", "0.0.5");
for (String f : npm.list("other")) {
TextFile.streamToFile(npm.load("other", f), Utilities.path("[tmp]", "comparison", f));
}
}
System.out.println("---- " + name + " ----------------------------------------------------------------");
System.out.println("---- " + name + " ----------------------------------------------------------------");
CanonicalResource left = load("left");
CanonicalResource right = load("right");
ComparisonSession session = new ComparisonSession(context);
if (left instanceof CodeSystem && right instanceof CodeSystem) {
CodeSystemComparer cs = new CodeSystemComparer(session );
CodeSystemComparer cs = new CodeSystemComparer(session);
CodeSystemComparison csc = cs.compare((CodeSystem) left, (CodeSystem) right);
Assertions.assertTrue(csc.getUnion().getConcept().size() > csc.getIntersection().getConcept().size());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name+"-union.json")), csc.getUnion());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name+"-intersection.json")), csc.getIntersection());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name + "-union.json")), csc.getUnion());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name + "-intersection.json")), csc.getIntersection());
String xmle = new XhtmlComposer(true).compose(cs.renderErrors(csc));
String xml1 = new XhtmlComposer(true).compose(cs.renderMetadata(csc, "", ""));
String xml2 = new XhtmlComposer(true).compose(cs.renderConcepts(csc, "", ""));
TextFile.stringToFile(HEADER+hd("Messages")+xmle+BREAK+hd("Metadata")+xml1+BREAK+hd("Concepts")+xml2+FOOTER, Utilities.path("[tmp]", "comparison", name+".html"));
TextFile.stringToFile(HEADER + hd("Messages") + xmle + BREAK + hd("Metadata") + xml1 + BREAK + hd("Concepts") + xml2 + FOOTER, Utilities.path("[tmp]", "comparison", name + ".html"));
checkOutcomes(csc.getMessages(), content);
} else if (left instanceof ValueSet && right instanceof ValueSet) {
ValueSetComparer cs = new ValueSetComparer(session);
ValueSetComparison csc = cs.compare((ValueSet) left, (ValueSet) right);
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name+"-union.json")), csc.getUnion());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name+"-intersection.json")), csc.getIntersection());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name + "-union.json")), csc.getUnion());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name + "-intersection.json")), csc.getIntersection());
String xmle = new XhtmlComposer(true).compose(cs.renderErrors(csc));
String xml1 = new XhtmlComposer(true).compose(cs.renderMetadata(csc, "", ""));
String xml2 = new XhtmlComposer(true).compose(cs.renderCompose(csc, "", ""));
String xml3 = new XhtmlComposer(true).compose(cs.renderExpansion(csc, "", ""));
TextFile.stringToFile(HEADER+hd("Messages")+xmle+BREAK+hd("Metadata")+xml1+BREAK+hd("Definition")+xml2+BREAK+hd("Expansion")+xml3+FOOTER, Utilities.path("[tmp]", "comparison", name+".html"));
TextFile.stringToFile(HEADER + hd("Messages") + xmle + BREAK + hd("Metadata") + xml1 + BREAK + hd("Definition") + xml2 + BREAK + hd("Expansion") + xml3 + FOOTER, Utilities.path("[tmp]", "comparison", name + ".html"));
checkOutcomes(csc.getMessages(), content);
} else {
throw new FHIRException("Can't compare "+left.fhirType()+" to "+right.fhirType());
throw new FHIRException("Can't compare " + left.fhirType() + " to " + right.fhirType());
}
}
private String hd(String text) {
return "<h2>"+text+"</h2>\r\n";
return "<h2>" + text + "</h2>\r\n";
}
private CanonicalResource load(String name) throws IOException {
@ -183,7 +183,7 @@ public class ComparisonTests {
throw new FHIRException("unknown version " + ver);
}
}
}
}
private void checkOutcomes(List<ValidationMessage> errors, JsonObject focus) {
JsonObject output = focus.getAsJsonObject("output");

View File

@ -1,12 +0,0 @@
package org.hl7.fhir.conversion.tests;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
SnapShotGenerationTestsX.class})
public class CrossVersionLibraryTests {
}

View File

@ -520,7 +520,8 @@ public class SnapShotGenerationTestsX {
throw e;
}
if (output.getDifferential().hasElement()) {
RenderingContext rc = new RenderingContext(TestingUtilities.context(), null, null, "", "http://hl7.org/fhir", ResourceRendererMode.RESOURCE);
RenderingContext rc = new RenderingContext(TestingUtilities.context(), null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE);
rc.setDestDir(makeTempDir());
rc.setProfileUtilities(new ProfileUtilities(TestingUtilities.context(), null, new TestPKP()));
RendererFactory.factory(output, rc).render(output);
}
@ -540,6 +541,13 @@ public class SnapShotGenerationTestsX {
}
}
private String makeTempDir() throws IOException {
String path = Utilities.path("[tmp]", "snapshot");
Utilities.createDirectory(path);
return path;
}
private StructureDefinition getSD(String url) throws DefinitionException, FHIRException, IOException {
StructureDefinition sd = context.getByUrl(url);
if (sd == null)

View File

@ -13,11 +13,11 @@
each other. It is fine to bump the point version of this POM without affecting
HAPI FHIR.
-->
<version>5.0.2-SNAPSHOT</version>
<version>5.0.5-SNAPSHOT</version>
<properties>
<hapi_fhir_version>5.0.0</hapi_fhir_version>
<validator_test_case_version>1.1.14</validator_test_case_version>
<validator_test_case_version>1.1.17</validator_test_case_version>
<junit_jupiter_version>5.6.2</junit_jupiter_version>
<maven_surefire_version>3.0.0-M4</maven_surefire_version>
<jacoco_version>0.8.5</jacoco_version>
@ -136,6 +136,7 @@
<module>org.hl7.fhir.report</module>
</modules>
<build>
<plugins>
<plugin>

View File

@ -8,6 +8,27 @@ title: FHIR Validator Release Notes
(no changes yet)
## v5.0.5 (2020-05-30)
(no changes yet)
## v5.0.5 (2020-05-30)
* Snapshot Generator: Add more testing for type consistency when profiling elements
* Snapshot Generator: Fix bug constraining elements once they are sliced
* Add support for http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name for CDA use
## v5.0.4 (2020-05-27)
(no changes yet)
## v5.0.3 (2020-05-26)
* Fix for Core issue #95 - recursion on profile definition
## v5.0.2 (2020-05-22)
@ -194,4 +215,4 @@ title: FHIR Validator Release Notes
## v4.1.60 (2020-02-02)
* This r
* Th

View File

@ -1,7 +1,7 @@
@echo off
set oldver=4.2.31
set newver=5.0.2
set oldver=5.0.4
set newver=5.0.5
echo ..
echo =========================================================================