Add validator for DSTU2.1

This commit is contained in:
James 2016-12-11 17:39:05 -05:00
parent 191e3b6d4d
commit 9cb014724b
251 changed files with 943097 additions and 11542 deletions

View File

@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
@ -65,6 +66,8 @@ public class MyURIResolver implements URIResolver {
return new StreamSource(new FileInputStream(href.contains(File.separator) ? href : Utilities.path(path, href)));
} catch (FileNotFoundException e) {
throw new TransformerException(e);
} catch (IOException e) {
throw new TransformerException(e);
}
}

View File

@ -73,7 +73,6 @@ public class Decimal {
* @param value - a string representation of the value
* @param precision - a
* @throws UcumException
* @
*/
public Decimal(String value, int precision) throws UcumException {
super();

View File

@ -21,14 +21,12 @@
<version>2.2-SNAPSHOT</version>
</dependency>
<!--
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2.1</artifactId>
<version>2.2-SNAPSHOT</version>
<scope>test</scope>
</dependency>
-->
<!--
The JPA project uses a newer API but we'll try to hold to this version

View File

@ -0,0 +1,83 @@
package org.hl7.fhir.dstu2016may.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.
*/
import java.math.BigDecimal;
import java.net.URI;
import org.apache.commons.codec.binary.Base64;
public abstract class FormatUtilities {
public static final String ID_REGEX = "[A-Za-z0-9\\-\\.]{1,64}";
public static final String FHIR_NS = "http://hl7.org/fhir";
public static final String XHTML_NS = "http://www.w3.org/1999/xhtml";
public static final String NS_XSI = "http://www.w3.org/2001/XMLSchema-instance";
protected String toString(String value) {
return value;
}
protected String toString(int value) {
return java.lang.Integer.toString(value);
}
protected String toString(boolean value) {
return java.lang.Boolean.toString(value);
}
protected String toString(BigDecimal value) {
return value.toString();
}
protected String toString(URI value) {
return value.toString();
}
public static String toString(byte[] value) {
byte[] encodeBase64 = Base64.encodeBase64(value);
return new String(encodeBase64);
}
public static boolean isValidId(String tail) {
return tail.matches(ID_REGEX);
}
public static String makeId(String candidate) {
StringBuilder b = new StringBuilder();
for (char c : candidate.toCharArray())
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '.' || c == '-')
b.append(c);
return b.toString();
}
}

View File

@ -0,0 +1,221 @@
package org.hl7.fhir.dstu2016may.formats;
import java.io.IOException;
/*
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 java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import org.hl7.fhir.dstu2016may.model.Resource;
import org.hl7.fhir.dstu2016may.model.Type;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.xmlpull.v1.XmlPullParserException;
/**
* General interface - either an XML or JSON parser: read or write instances
*
* Defined to allow a factory to create a parser of the right type
*/
public interface IParser {
/**
* check what kind of parser this is
*
* @return what kind of parser this is
*/
public ParserType getType();
// -- Parser Configuration ----------------------------------
/**
* Whether to parse or ignore comments - either reading or writing
*/
public boolean getHandleComments();
public IParser setHandleComments(boolean value);
/**
* @param allowUnknownContent Whether to throw an exception if unknown content is found (or just skip it) when parsing
*/
public boolean isAllowUnknownContent();
public IParser setAllowUnknownContent(boolean value);
public enum OutputStyle {
/**
* Produce normal output - no whitespace, except in HTML where whitespace is untouched
*/
NORMAL,
/**
* Produce pretty output - human readable whitespace, HTML whitespace untouched
*/
PRETTY,
/**
* Produce canonical output - no comments, no whitspace, HTML whitespace normlised, JSON attributes sorted alphabetically (slightly slower)
*/
CANONICAL,
}
/**
* Writing:
*/
public OutputStyle getOutputStyle();
public IParser setOutputStyle(OutputStyle value);
/**
* This method is used by the publication tooling to stop the xhrtml narrative being generated.
* It is not valid to use in production use. The tooling uses it to generate json/xml representations in html that are not cluttered by escaped html representations of the html representation
*/
public IParser setSuppressXhtml(String message);
// -- Reading methods ----------------------------------------
/**
* parse content that is known to be a resource
* @throws XmlPullParserException
* @throws FHIRFormatError
* @throws IOException
*/
public Resource parse(InputStream input) throws IOException, FHIRFormatError;
/**
* parse content that is known to be a resource
* @throws UnsupportedEncodingException
* @throws IOException
* @throws FHIRFormatError
*/
public Resource parse(String input) throws UnsupportedEncodingException, FHIRFormatError, IOException;
/**
* parse content that is known to be a resource
* @throws IOException
* @throws FHIRFormatError
*/
public Resource parse(byte[] bytes) throws FHIRFormatError, IOException;
/**
* This is used to parse a type - a fragment of a resource.
* There's no reason to use this in production - it's used
* in the build tools
*
* Not supported by all implementations
*
* @param input
* @param knownType. if this is blank, the parser may try to infer the type (xml only)
* @return
* @throws XmlPullParserException
* @throws FHIRFormatError
* @throws IOException
*/
public Type parseType(InputStream input, String knownType) throws IOException, FHIRFormatError;
/**
* This is used to parse a type - a fragment of a resource.
* There's no reason to use this in production - it's used
* in the build tools
*
* Not supported by all implementations
*
* @param input
* @param knownType. if this is blank, the parser may try to infer the type (xml only)
* @return
* @throws UnsupportedEncodingException
* @throws IOException
* @throws FHIRFormatError
*/
public Type parseType(String input, String knownType) throws UnsupportedEncodingException, FHIRFormatError, IOException;
/**
* This is used to parse a type - a fragment of a resource.
* There's no reason to use this in production - it's used
* in the build tools
*
* Not supported by all implementations
*
* @param input
* @param knownType. if this is blank, the parser may try to infer the type (xml only)
* @return
* @throws IOException
* @throws FHIRFormatError
*/
public Type parseType(byte[] bytes, String knownType) throws FHIRFormatError, IOException;
// -- Writing methods ----------------------------------------
/**
* Compose a resource to a stream, possibly using pretty presentation for a human reader (used in the spec, for example, but not normally in production)
* @throws IOException
*/
public void compose(OutputStream stream, Resource resource) throws IOException;
/**
* Compose a resource to a stream, possibly using pretty presentation for a human reader (used in the spec, for example, but not normally in production)
* @throws IOException
*/
public String composeString(Resource resource) throws IOException;
/**
* Compose a resource to a stream, possibly using pretty presentation for a human reader (used in the spec, for example, but not normally in production)
* @throws IOException
*/
public byte[] composeBytes(Resource resource) throws IOException;
/**
* Compose a type to a stream, possibly using pretty presentation for a human reader (used in the spec, for example, but not normally in production)
*
* Not supported by all implementations. rootName is ignored in the JSON format
* @throws XmlPullParserException
* @throws FHIRFormatError
* @throws IOException
*/
public void compose(OutputStream stream, Type type, String rootName) throws IOException;
/**
* Compose a type to a stream, possibly using pretty presentation for a human reader (used in the spec, for example, but not normally in production)
*
* Not supported by all implementations. rootName is ignored in the JSON format
* @throws IOException
*/
public String composeString(Type type, String rootName) throws IOException;
/**
* Compose a type to a stream, possibly using pretty presentation for a human reader (used in the spec, for example, but not normally in production)
*
* Not supported by all implementations. rootName is ignored in the JSON format
* @throws IOException
*/
public byte[] composeBytes(Type type, String rootName) throws IOException;
}

View File

@ -0,0 +1,38 @@
package org.hl7.fhir.dstu2016may.formats;
import java.io.IOException;
import java.math.BigDecimal;
/**
* Facade to GSON writer, or something that imposes property ordering first
*
* @author Grahame
*
*/
public interface JsonCreator {
void setIndent(String string);
void beginObject() throws IOException;
void endObject() throws IOException;
void nullValue() throws IOException;
void name(String name) throws IOException;
void value(String value) throws IOException;
void value(Boolean value) throws IOException;
void value(BigDecimal value) throws IOException;
void value(Integer value) throws IOException;
void beginArray() throws IOException;
void endArray() throws IOException;
void finish() throws IOException;
}

View File

@ -0,0 +1,227 @@
package org.hl7.fhir.dstu2016may.formats;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import com.google.gson.stream.JsonWriter;
public class JsonCreatorCanonical implements JsonCreator {
public class JsonCanValue {
String name;
private JsonCanValue(String name) {
this.name = name;
}
}
private class JsonCanNumberValue extends JsonCanValue {
private BigDecimal value;
private JsonCanNumberValue(String name, BigDecimal value) {
super(name);
this.value = value;
}
}
private class JsonCanIntegerValue extends JsonCanValue {
private Integer value;
private JsonCanIntegerValue(String name, Integer value) {
super(name);
this.value = value;
}
}
private class JsonCanBooleanValue extends JsonCanValue {
private Boolean value;
private JsonCanBooleanValue(String name, Boolean value) {
super(name);
this.value = value;
}
}
private class JsonCanStringValue extends JsonCanValue {
private String value;
private JsonCanStringValue(String name, String value) {
super(name);
this.value = value;
}
}
private class JsonCanNullValue extends JsonCanValue {
private JsonCanNullValue(String name) {
super(name);
}
}
public class JsonCanObject extends JsonCanValue {
boolean array;
List<JsonCanValue> children = new ArrayList<JsonCanValue>();
public JsonCanObject(String name, boolean array) {
super(name);
this.array = array;
}
public void addProp(JsonCanValue obj) {
children.add(obj);
}
}
Stack<JsonCanObject> stack;
JsonCanObject root;
JsonWriter gson;
String name;
public JsonCreatorCanonical(OutputStreamWriter osw) {
stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
gson = new JsonWriter(osw);
name = null;
}
private String takeName() {
String res = name;
name = null;
return res;
}
@Override
public void setIndent(String indent) {
if (!indent.equals(""))
throw new Error("do not use pretty when canonical is set");
gson.setIndent(indent);
}
@Override
public void beginObject() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), false);
if (stack.isEmpty())
root = obj;
else
stack.peek().addProp(obj);
stack.push(obj);
}
@Override
public void endObject() throws IOException {
stack.pop();
}
@Override
public void nullValue() throws IOException {
stack.peek().addProp(new JsonCanNullValue(takeName()));
}
@Override
public void name(String name) throws IOException {
this.name = name;
}
@Override
public void value(String value) throws IOException {
stack.peek().addProp(new JsonCanStringValue(takeName(), value));
}
@Override
public void value(Boolean value) throws IOException {
stack.peek().addProp(new JsonCanBooleanValue(takeName(), value));
}
@Override
public void value(BigDecimal value) throws IOException {
stack.peek().addProp(new JsonCanNumberValue(takeName(), value));
}
@Override
public void value(Integer value) throws IOException {
stack.peek().addProp(new JsonCanIntegerValue(takeName(), value));
}
@Override
public void beginArray() throws IOException {
JsonCanObject obj = new JsonCanObject(takeName(), true);
if (!stack.isEmpty())
stack.peek().addProp(obj);
stack.push(obj);
}
@Override
public void endArray() throws IOException {
stack.pop();
}
@Override
public void finish() throws IOException {
writeObject(root);
}
private void writeObject(JsonCanObject obj) throws IOException {
gson.beginObject();
List<String> names = new ArrayList<String>();
for (JsonCanValue v : obj.children)
names.add(v.name);
Collections.sort(names);
for (String n : names) {
gson.name(n);
JsonCanValue v = getPropForName(n, obj.children);
if (v instanceof JsonCanNumberValue)
gson.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
gson.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
gson.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
gson.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
gson.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
writeArray(o);
else
writeObject(o);
} else
throw new Error("not possible");
}
gson.endObject();
}
private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
for (JsonCanValue child : children)
if (child.name.equals(name))
return child;
return null;
}
private void writeArray(JsonCanObject arr) throws IOException {
gson.beginArray();
for (JsonCanValue v : arr.children) {
if (v instanceof JsonCanNumberValue)
gson.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
gson.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
gson.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
gson.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
gson.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
writeArray(o);
else
writeObject(o);
} else
throw new Error("not possible");
}
gson.endArray();
}
}

View File

@ -0,0 +1,78 @@
package org.hl7.fhir.dstu2016may.formats;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import com.google.gson.stream.JsonWriter;
public class JsonCreatorGson implements JsonCreator {
JsonWriter gson;
public JsonCreatorGson(OutputStreamWriter osw) {
gson = new JsonWriter(osw);
}
@Override
public void setIndent(String indent) {
gson.setIndent(indent);
}
@Override
public void beginObject() throws IOException {
gson.beginObject();
}
@Override
public void endObject() throws IOException {
gson.endObject();
}
@Override
public void nullValue() throws IOException {
gson.nullValue();
}
@Override
public void name(String name) throws IOException {
gson.name(name);
}
@Override
public void value(String value) throws IOException {
gson.value(value);
}
@Override
public void value(Boolean value) throws IOException {
gson.value(value);
}
@Override
public void value(BigDecimal value) throws IOException {
gson.value(value);
}
@Override
public void value(Integer value) throws IOException {
gson.value(value);
}
@Override
public void beginArray() throws IOException {
gson.beginArray();
}
@Override
public void endArray() throws IOException {
gson.endArray();
}
@Override
public void finish() {
// nothing to do here
}
}

View File

@ -0,0 +1,30 @@
package org.hl7.fhir.dstu2016may.formats;
/**
* Used in factory methods for parsers, for requesting a parser of a particular type
* (see IWorkerContext)
*
* @author Grahame
*
*/
public enum ParserType {
/**
* XML as specified in specification
*/
XML,
/**
* JSON as specified in the specification
*/
JSON,
/**
* XHTML - write narrative (generate if necessary). No read
*/
XHTML,
/**
* RDF is not supported yet
*/
RDF_TURTLE
}

View File

@ -116,7 +116,7 @@ public class FhirDstu2_1 implements IFhirVersion {
@Override
public String getPathToSchemaDefinitions() {
return "/org/hl7/fhir/instance/model/dstu3/schema";
return "/org/hl7/fhir/dstu2016may/schema";
}
@Override

View File

@ -0,0 +1,55 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import java.util.List;
import org.hl7.fhir.dstu2016may.validation.ValidationMessage;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage;
/**
* Base class for a bridge between the RI validation tools and HAPI
*/
abstract class BaseValidatorBridge implements IValidatorModule {
public BaseValidatorBridge() {
super();
}
private void doValidate(IValidationContext<?> theCtx) {
List<ValidationMessage> messages = validate(theCtx);
for (ValidationMessage riMessage : messages) {
SingleValidationMessage hapiMessage = new SingleValidationMessage();
if (riMessage.getCol() != -1) {
hapiMessage.setLocationCol(riMessage.getCol());
}
if (riMessage.getLine() != -1) {
hapiMessage.setLocationLine(riMessage.getLine());
}
hapiMessage.setLocationString(riMessage.getLocation());
hapiMessage.setMessage(riMessage.getMessage());
if (riMessage.getLevel() != null) {
hapiMessage.setSeverity(ResultSeverityEnum.fromCode(riMessage.getLevel().toCode()));
}
theCtx.addValidationMessage(hapiMessage);
}
}
protected abstract List<ValidationMessage> validate(IValidationContext<?> theCtx);
@Override
public void validateBundle(IValidationContext<Bundle> theCtx) {
doValidate(theCtx);
}
@Override
public void validateResource(IValidationContext<IBaseResource> theCtx) {
doValidate(theCtx);
}
}

View File

@ -0,0 +1,272 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.Charsets;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu2016may.model.Bundle;
import org.hl7.fhir.dstu2016may.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.DomainResource;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
public class DefaultProfileValidationSupport implements IValidationSupport {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
private Map<String, CodeSystem> myCodeSystems;
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSetExpansionComponent retVal = new ValueSetExpansionComponent();
Set<String> wantCodes = new HashSet<String>();
for (ConceptReferenceComponent next : theInclude.getConcept()) {
wantCodes.add(next.getCode());
}
CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem());
for (ConceptDefinitionComponent next : system.getConcept()) {
if (wantCodes.isEmpty() || wantCodes.contains(next.getCode())) {
retVal.addContains().setSystem(theInclude.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
}
}
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values());
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true);
}
private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) {
Map<String, CodeSystem> codeSystems = myCodeSystems;
Map<String, ValueSet> valueSets = myValueSets;
if (codeSystems == null) {
codeSystems = new HashMap<String, CodeSystem>();
valueSets = new HashMap<String, ValueSet>();
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v3-codesystems.xml");
myCodeSystems = codeSystems;
myValueSets = valueSets;
}
if (codeSystem) {
return codeSystems.get(theSystem);
} else {
return valueSets.get(theSystem);
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
Validate.notBlank(theUri, "theUri must not be null or blank");
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
return (T) fetchStructureDefinition(theContext, theUri);
}
if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
return (T) fetchValueSet(theContext, theUri);
}
// if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
// Map<String, ValueSet> defaultValueSets = myDefaultValueSets;
// if (defaultValueSets == null) {
// String path = theContext.getVersion().getPathToSchemaDefinitions().replace("/schema", "/valueset") + "/valuesets.xml";
// InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(path);
// if (valuesetText == null) {
// return null;
// }
// InputStreamReader reader;
// try {
// reader = new InputStreamReader(valuesetText, "UTF-8");
// } catch (UnsupportedEncodingException e) {
// // Shouldn't happen!
// throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
// }
//
// defaultValueSets = new HashMap<String, ValueSet>();
//
// Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
// for (BundleEntryComponent next : bundle.getEntry()) {
// IdType nextId = new IdType(next.getFullUrl());
// if (nextId.isEmpty() || !nextId.getValue().startsWith("http://hl7.org/fhir/ValueSet/")) {
// continue;
// }
// defaultValueSets.put(nextId.toVersionless().getValue(), (ValueSet) next.getResource());
// }
//
// myDefaultValueSets = defaultValueSets;
// }
//
// return (T) defaultValueSets.get(theUri);
// }
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) {
return provideStructureDefinitionMap(theContext).get(theUrl);
}
ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
return (ValueSet) fetchCodeSystemOrValueSet(theContext, theSystem, false);
}
public void flush() {
myCodeSystems = null;
myStructureDefinitions = null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
CodeSystem cs = fetchCodeSystem(theContext, theSystem);
return cs != null;
}
private void loadCodeSystems(FhirContext theContext, Map<String, CodeSystem> theCodeSystems, Map<String, ValueSet> theValueSets, String theClasspath) {
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
if (valuesetText != null) {
InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8);
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof CodeSystem) {
CodeSystem nextValueSet = (CodeSystem) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextValueSet);
}
} else if (next.getResource() instanceof ValueSet) {
ValueSet nextValueSet = (ValueSet) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theValueSets.put(system, nextValueSet);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private void loadStructureDefinitions(FhirContext theContext, Map<String, StructureDefinition> theCodeSystems, String theClasspath) {
ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
if (valuesetText != null) {
InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8);
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof StructureDefinition) {
StructureDefinition nextSd = (StructureDefinition) next.getResource();
nextSd.getText().setDivAsString("");
String system = nextSd.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextSd);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private Map<String, StructureDefinition> provideStructureDefinitionMap(FhirContext theContext) {
Map<String, StructureDefinition> structureDefinitions = myStructureDefinitions;
if (structureDefinitions == null) {
structureDefinitions = new HashMap<String, StructureDefinition>();
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-resources.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-types.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-others.xml");
myStructureDefinitions = structureDefinitions;
}
return structureDefinitions;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
code = code.toUpperCase();
}
return testIfConceptIsInListInner(conceptList, theCaseSensitive, code);
}
private CodeValidationResult testIfConceptIsInListInner(List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) {
CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode();
if (theCaseSensitive == false) {
nextCandidate = nextCandidate.toUpperCase();
}
if (nextCandidate.equals(code)) {
retVal = new CodeValidationResult(next);
break;
}
// recurse
retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive);
if (retVal != null) {
break;
}
}
return retVal;
}
}

View File

@ -0,0 +1,208 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.validation.IResourceValidator;
import org.hl7.fhir.dstu2016may.validation.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.dstu2016may.validation.InstanceValidator;
import org.hl7.fhir.dstu2016may.validation.ValidationMessage;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
private BestPracticeWarningLevel myBestPracticeWarningLevel;
private DocumentBuilderFactory myDocBuilderFactory;
private StructureDefinition myStructureDefintion;
private IValidationSupport myValidationSupport;
/**
* Constructor
*
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support}
*/
public FhirInstanceValidator() {
this(new DefaultProfileValidationSupport());
}
/**
* Constructor which uses the given validation support
*
* @param theValidationSupport
* The validation support
*/
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
myDocBuilderFactory.setNamespaceAware(true);
myValidationSupport = theValidationSupport;
}
private String determineResourceName(Document theDocument) {
Element root = null;
NodeList list = theDocument.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if (list.item(i) instanceof Element) {
root = (Element) list.item(i);
break;
}
}
root = theDocument.getDocumentElement();
return root.getLocalName();
}
/**
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}).
* <p>
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
* guielines will be ignored.
* </p>
*
* @see {@link #setBestPracticeWarningLevel(BestPracticeWarningLevel)}
*/
public BestPracticeWarningLevel getBestPracticeWarningLevel() {
return myBestPracticeWarningLevel;
}
/**
* Returns the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
*/
public IValidationSupport getValidationSupport() {
return myValidationSupport;
}
/**
* Sets the "best practice warning level". When validating, any deviations from best practices will be reported at
* this level.
* <p>
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
* guielines will be ignored.
* </p>
*
* @param theBestPracticeWarningLevel
* The level, must not be <code>null</code>
*/
public void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
Validate.notNull(theBestPracticeWarningLevel);
myBestPracticeWarningLevel = theBestPracticeWarningLevel;
}
public void setStructureDefintion(StructureDefinition theStructureDefintion) {
myStructureDefintion = theStructureDefintion;
}
/**
* Sets the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
*/
public void setValidationSupport(IValidationSupport theValidationSupport) {
myValidationSupport = theValidationSupport;
}
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
InstanceValidator v;
try {
v = new InstanceValidator(workerContext);
} catch (Exception e) {
throw new ConfigurationException(e);
}
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
v.setAnyExtensionsAllowed(true);
v.setResourceIdRule(IResourceValidator.IdStatus.OPTIONAL);
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (theEncoding == EncodingEnum.XML) {
Document document;
try {
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
InputSource src = new InputSource(new StringReader(theInput));
document = builder.parse(src);
} catch (Exception e2) {
ourLog.error("Failure to parse XML input", e2);
ValidationMessage m = new ValidationMessage();
m.setLevel(IssueSeverity.FATAL);
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
return Collections.singletonList(m);
}
String resourceName = determineResourceName(document);
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
if (profile != null) {
try {
v.validate(messages, document, profile);
} catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e);
}
}
} else if (theEncoding == EncodingEnum.JSON) {
Gson gson = new GsonBuilder().create();
JsonObject json = gson.fromJson(theInput, JsonObject.class);
String resourceName = json.get("resourceType").getAsString();
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
if (profile != null) {
try {
v.validate(messages, json, profile);
} catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e);
}
}
} else {
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
}
for (int i = 0; i < messages.size(); i++) {
ValidationMessage next = messages.get(i);
if ("Binding has no source, so can't be checked".equals(next.getMessage())) {
messages.remove(i);
i--;
}
}
return messages;
}
private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) {
String sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName;
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchStructureDefinition(theCtx, sdName);
return profile;
}
@Override
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
}
}

View File

@ -0,0 +1,274 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu2016may.formats.IParser;
import org.hl7.fhir.dstu2016may.formats.ParserType;
import org.hl7.fhir.dstu2016may.hapi.validation.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.CodeType;
import org.hl7.fhir.dstu2016may.model.CodeableConcept;
import org.hl7.fhir.dstu2016may.model.Coding;
import org.hl7.fhir.dstu2016may.model.ConceptMap;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.Resource;
import org.hl7.fhir.dstu2016may.model.ResourceType;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
import org.hl7.fhir.dstu2016may.validation.IResourceValidator;
import ca.uhn.fhir.context.FhirContext;
public final class HapiWorkerContext implements IWorkerContext {
private final FhirContext myCtx;
private Map<String, Resource> myFetchedResourceCache = new HashMap<String, Resource>();
private IValidationSupport myValidationSupport;
public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
Validate.notNull(theCtx, "theCtx must not be null");
Validate.notNull(theValidationSupport, "theValidationSupport must not be null");
myCtx = theCtx;
myValidationSupport = theValidationSupport;
}
@Override
public List<StructureDefinition> allStructures() {
return myValidationSupport.fetchAllStructureDefinitions(myCtx);
}
@Override
public CodeSystem fetchCodeSystem(String theSystem) {
if (myValidationSupport == null) {
return null;
} else {
return myValidationSupport.fetchCodeSystem(myCtx, theSystem);
}
}
@Override
public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) {
if (myValidationSupport == null) {
return null;
} else {
@SuppressWarnings("unchecked")
T retVal = (T) myFetchedResourceCache.get(theUri);
if (retVal == null) {
retVal = myValidationSupport.fetchResource(myCtx, theClass, theUri);
if (retVal != null) {
myFetchedResourceCache.put(theUri, retVal);
}
}
return retVal;
}
}
@Override
public List<ConceptMap> findMapsForSource(String theUrl) {
throw new UnsupportedOperationException();
}
@Override
public String getAbbreviation(String theName) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(ParserType theType) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(String theType) {
throw new UnsupportedOperationException();
}
@Override
public List<String> getResourceNames() {
List<String> result = new ArrayList<String>();
for (ResourceType next : ResourceType.values()) {
result.add(next.name());
}
Collections.sort(result);
return result;
}
@Override
public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) {
throw new UnsupportedOperationException();
}
@Override
public IParser newJsonParser() {
throw new UnsupportedOperationException();
}
@Override
public IResourceValidator newValidator() {
throw new UnsupportedOperationException();
}
@Override
public IParser newXmlParser() {
throw new UnsupportedOperationException();
}
@Override
public String oid2Uri(String theCode) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsSystem(String theSystem) {
if (myValidationSupport == null) {
return false;
} else {
return myValidationSupport.isCodeSystemSupported(myCtx, theSystem);
}
}
@Override
public Set<String> typeTails() {
return new HashSet<String>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code",
"Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint",
"Timing", "Reference", "Annotation", "Signature", "Meta"));
}
@Override
public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) {
for (Coding next : theCode.getCoding()) {
ValidationResult retVal = validateCode(next, theVs);
if (retVal != null && retVal.isOk()) {
return retVal;
}
}
return new ValidationResult(null, null);
}
@Override
public ValidationResult validateCode(Coding theCode, ValueSet theVs) {
String system = theCode.getSystem();
String code = theCode.getCode();
String display = theCode.getDisplay();
return validateCode(system, code, display, theVs);
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
if (result == null) {
return null;
}
return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition());
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) {
throw new UnsupportedOperationException();
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
if (theVs != null && isNotBlank(theCode)) {
for (ConceptSetComponent next : theVs.getCompose().getInclude()) {
if (isBlank(theSystem) || theSystem.equals(next.getSystem())) {
for (ConceptReferenceComponent nextCode : next.getConcept()) {
if (theCode.equals(nextCode.getCode())) {
CodeType code = new CodeType(theCode);
return new ValidationResult(new ConceptDefinitionComponent(code));
}
}
}
}
}
boolean caseSensitive = true;
if (isNotBlank(theSystem)) {
CodeSystem system = fetchCodeSystem(theSystem);
if (system == null) {
return new ValidationResult(IssueSeverity.INFORMATION, "Code " + theSystem + "/" + theCode + " was not validated because the code system is not present");
}
if (system.hasCaseSensitive()) {
caseSensitive = system.getCaseSensitive();
}
}
String wantCode = theCode;
if (!caseSensitive) {
wantCode = wantCode.toUpperCase();
}
ValueSetExpansionOutcome expandedValueSet = null;
/*
* The following valueset is a special case, since the BCP codesystem is very difficult to expand
*/
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) {
ValueSet expansion = new ValueSet();
for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay());
}
}
expandedValueSet = new ValueSetExpansionOutcome(expansion);
}
if (expandedValueSet == null) {
expandedValueSet = expandVS(theVs, true);
}
for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) {
String nextCode = next.getCode();
if (!caseSensitive) {
nextCode = nextCode.toUpperCase();
}
if (nextCode.equals(wantCode)) {
if (theSystem == null || next.getSystem().equals(theSystem)) {
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
definition.setCode(next.getCode());
definition.setDisplay(next.getDisplay());
ValidationResult retVal = new ValidationResult(definition);
return retVal;
}
}
}
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
}
@Override
public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk) {
throw new UnsupportedOperationException();
}
@Override
public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,108 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import java.util.List;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IContextValidationSupport;
public interface IValidationSupport
extends ca.uhn.fhir.context.support.IContextValidationSupport<ConceptSetComponent, ValueSetExpansionComponent, StructureDefinition, CodeSystem, ConceptDefinitionComponent, IssueSeverity> {
/**
* Expands the given portion of a ValueSet
*
* @param theInclude
* The portion to include
* @return The expansion
*/
@Override
ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude);
/**
* Load and return all possible structure definitions
*/
@Override
List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext);
/**
* Fetch a code system by ID
*
* @param theSystem
* The code system
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
@Override
CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem);
/**
* Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet)
*
* @param theContext
* The HAPI FHIR Context object current in use by the validator
* @param theClass
* The type of the resource to load
* @param theUri
* The resource URI
* @return Returns the resource, or <code>null</code> if no resource with the
* given URI can be found
*/
@Override
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
@Override
StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl);
/**
* Returns <code>true</code> if codes in the given code system can be expanded
* or validated
*
* @param theSystem
* The URI for the code system, e.g. <code>"http://loinc.org"</code>
* @return Returns <code>true</code> if codes in the given code system can be
* validated
*/
@Override
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theCodeSystem
* The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode
* The code, e.g. "<code>1234-5</code>"
* @param theDisplay
* The display name, if it should also be validated
* @return Returns a validation result object
*/
@Override
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
public class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
public CodeValidationResult(ConceptDefinitionComponent theNext) {
super(theNext);
}
public CodeValidationResult(IssueSeverity theSeverity, String theMessage) {
super(theSeverity, theMessage);
}
public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
super(severity, message, definition);
}
}
}

View File

@ -0,0 +1,132 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
/**
* This class is an implementation of {@link IValidationSupport} which may be pre-populated
* with a collection of validation resources to be used by the validator.
*/
public class PrePopulatedValidationSupport implements IValidationSupport {
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
private Map<String, CodeSystem> myCodeSystems;
/**
* Constructor
*/
public PrePopulatedValidationSupport() {
myStructureDefinitions = new HashMap<String,StructureDefinition>();
myValueSets = new HashMap<String,ValueSet>();
myCodeSystems = new HashMap<String,CodeSystem>();
}
/**
* Add a new StructureDefinition resource which will be available to the validator. Note that
* {@link StructureDefinition#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
*/
public void addStructureDefinition(StructureDefinition theStructureDefinition) {
Validate.notBlank(theStructureDefinition.getUrl(), "theStructureDefinition.getUrl() must not return a value");
myStructureDefinitions.put(theStructureDefinition.getUrl(), theStructureDefinition);
}
/**
* Add a new ValueSet resource which will be available to the validator. Note that
* {@link ValueSet#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
*/
public void addValueSet(ValueSet theValueSet) {
Validate.notBlank(theValueSet.getUrl(), "theValueSet.getUrl() must not return a value");
myValueSets.put(theValueSet.getUrl(), theValueSet);
}
/**
* Add a new CodeSystem resource which will be available to the validator. Note that
* {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
*/
public void addCodeSystem(CodeSystem theCodeSystem) {
Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value");
myCodeSystems.put(theCodeSystem.getUrl(), theCodeSystem);
}
/**
* Constructor
*
* @param theStructureDefinitions
* The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets
* The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems
* The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
*/
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
myValueSets = theValueSets;
myCodeSystems = theCodeSystems;
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return myCodeSystems.get(theSystem);
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
if (theClass.equals(StructureDefinition.class)) {
return (T) myStructureDefinitions.get(theUri);
}
if (theClass.equals(ValueSet.class)) {
return (T) myValueSets.get(theUri);
}
if (theClass.equals(CodeSystem.class)) {
return (T) myCodeSystems.get(theUri);
}
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return myStructureDefinitions.get(theUrl);
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return false;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
}

View File

@ -0,0 +1,122 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
public class ValidationSupportChain implements IValidationSupport {
private List<IValidationSupport> myChain;
/**
* Constructor
*/
public ValidationSupportChain() {
myChain = new ArrayList<IValidationSupport>();
}
/**
* Constructor
*/
public ValidationSupportChain(IValidationSupport... theValidationSupportModules) {
this();
for (IValidationSupport next : theValidationSupportModules) {
if (next != null) {
myChain.add(next);
}
}
}
public void addValidationSupport(IValidationSupport theValidationSupport) {
myChain.add(theValidationSupport);
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) {
return next.expandValueSet(theCtx, theInclude);
}
}
return myChain.get(0).expandValueSet(theCtx, theInclude);
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
for (IValidationSupport next : myChain) {
T retVal = next.fetchResource(theContext, theClass, theUri);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
for (IValidationSupport next : myChain) {
StructureDefinition retVal = next.fetchStructureDefinition(theCtx, theUrl);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theSystem)) {
return true;
}
}
return false;
}
@Override
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
return next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
}
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
Set<String> urls= new HashSet<String>();
for (IValidationSupport nextSupport : myChain) {
for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) {
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
retVal.add(next);
}
}
}
return retVal;
}
}

View File

@ -0,0 +1,304 @@
package org.hl7.fhir.dstu2016may.metamodel;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu2016may.model.Base;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
/**
* This class represents the reference model of FHIR
*
* A resource is nothing but a set of elements, where every element has a
* name, maybe a stated type, maybe an id, and either a value or child elements
* (one or the other, or both (but not neither if it's null)
*
* @author Grahame Grieve
*
*/
public class Element extends Base {
public enum SpecialElement {
CONTAINED, BUNDLE_ENTRY;
}
private List<String> comments;// not relevant for production, but useful in documentation
private String name;
private String type;
private String value;
private int index = -1;
private List<Element> children;
private Property property;
private int line;
private int col;
private SpecialElement special;
private XhtmlNode xhtml; // if this is populated, then value will also hold the string representation
public Element(String name) {
super();
this.name = name;
}
public Element(String name, Property property) {
super();
this.name = name;
this.property = property;
}
public Element(String name, Property property, String type, String value) {
super();
this.name = name;
this.property = property;
this.type = type;
this.value = value;
}
public void updateProperty(Property property, SpecialElement special) {
this.property = property;
this.special = special;
}
public SpecialElement getSpecial() {
return special;
}
public String getName() {
return name;
}
public String getType() {
if (type == null)
return property.getType(name);
else
return type;
}
public String getValue() {
return value;
}
public boolean hasChildren() {
return !(children == null || children.isEmpty());
}
public List<Element> getChildren() {
if (children == null)
children = new ArrayList<Element>();
return children;
}
public boolean hasComments() {
return !(comments == null || comments.isEmpty());
}
public List<String> getComments() {
if (comments == null)
comments = new ArrayList<String>();
return comments;
}
public Property getProperty() {
return property;
}
public void setValue(String value) {
this.value = value;
}
public void setType(String type) {
this.type = type;
}
public boolean hasValue() {
return value != null;
}
public List<Element> getChildrenByName(String name) {
List<Element> res = new ArrayList<Element>();
if (hasChildren()) {
for (Element child : children)
if (name.equals(child.getName()))
res.add(child);
}
return res;
}
public void numberChildren() {
if (children == null)
return;
String last = "";
int index = 0;
for (Element child : children) {
if (child.getProperty().isList()) {
if (last.equals(child.getName())) {
index++;
} else {
last = child.getName();
index = 0;
}
child.index = index;
} else {
child.index = -1;
}
child.numberChildren();
}
}
public int getIndex() {
return index;
}
public boolean hasIndex() {
return index > -1;
}
public void setIndex(int index) {
this.index = index;
}
public String getChildValue(String name) {
if (children == null)
return null;
for (Element child : children) {
if (name.equals(child.getName()))
return child.getValue();
}
return null;
}
public List<Element> getChildren(String name) {
List<Element> res = new ArrayList<Element>();
for (Element child : children) {
if (name.equals(child.getName()))
res.add(child);
}
return res;
}
public boolean hasType() {
if (type == null)
return property.hasType(name);
else
return true;
}
@Override
public String fhirType() {
return getType();
}
@Override
public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
if (isPrimitive() && (hash == "value".hashCode()) && !Utilities.noString(value)) {
String tn = getType();
throw new Error("not done yet");
}
List<Base> result = new ArrayList<Base>();
for (Element child : children) {
if (child.getName().equals(name))
result.add(child);
if (child.getName().startsWith(name) && child.getProperty().isChoice() && child.getProperty().getName().equals(name+"[x]"))
result.add(child);
}
if (result.isEmpty() && checkValid) {
// throw new FHIRException("not determined yet");
}
return result.toArray(new Base[result.size()]);
}
@Override
protected void listChildren(
List<org.hl7.fhir.dstu2016may.model.Property> result) {
// TODO Auto-generated method stub
}
@Override
public boolean isPrimitive() {
return type != null ? ParserBase.isPrimitive(type) : property.isPrimitive(name);
}
@Override
public boolean hasPrimitiveValue() {
return property.isPrimitive(name) || property.IsLogicalAndHasPrimitiveValue(name);
}
@Override
public String primitiveValue() {
if (isPrimitive())
return value;
else {
if (hasPrimitiveValue()) {
for (Element c : children) {
if (c.getName().equals("value"))
return c.primitiveValue();
}
}
return null;
}
}
// for the validator
public int line() {
return line;
}
public int col() {
return col;
}
public Element markLocation(int line, int col) {
this.line = line;
this.col = col;
return this;
}
public Element getNamedChild(String name) {
if (children == null)
return null;
Element result = null;
for (Element child : children) {
if (child.getName().equals(name)) {
if (result == null)
result = child;
else
throw new Error("Attempt to read a single element when there is more than one present ("+name+")");
}
}
return result;
}
public void getNamedChildren(String name, List<Element> list) {
if (children != null)
for (Element child : children)
if (child.getName().equals(name))
list.add(child);
}
public String getNamedChildValue(String name) {
Element child = getNamedChild(name);
return child == null ? null : child.value;
}
public void getNamedChildrenWithWildcard(String string, List<Element> values) {
throw new Error("not done yet");
}
public XhtmlNode getXhtml() {
return xhtml;
}
public Element setXhtml(XhtmlNode xhtml) {
this.xhtml = xhtml;
return this;
}
}

View File

@ -0,0 +1,421 @@
package org.hl7.fhir.dstu2016may.metamodel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle;
import org.hl7.fhir.dstu2016may.formats.JsonCreator;
import org.hl7.fhir.dstu2016may.formats.JsonCreatorCanonical;
import org.hl7.fhir.dstu2016may.formats.JsonCreatorGson;
import org.hl7.fhir.dstu2016may.metamodel.Element.SpecialElement;
import org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
import org.hl7.fhir.dstu2016may.utils.JsonTrackingParser;
import org.hl7.fhir.dstu2016may.utils.JsonTrackingParser.LocationData;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
public class JsonParser extends ParserBase {
private JsonCreator json;
private Map<JsonElement, LocationData> map;
public JsonParser(IWorkerContext context) {
super(context);
}
@Override
public Element parse(InputStream stream) throws Exception {
// if we're parsing at this point, then we're going to use the custom parser
map = new HashMap<JsonElement, LocationData>();
String source = TextFile.streamToString(stream);
if (policy == ValidationPolicy.EVERYTHING) {
JsonObject obj = null;
try {
obj = JsonTrackingParser.parse(source, map);
} catch (Exception e) {
logError(-1, -1, "(document)", IssueType.INVALID, "Error parsing JSON: "+e.getMessage(), IssueSeverity.FATAL);
return null;
}
assert (map.containsKey(obj));
return parse(obj);
} else {
JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source);
assert (map.containsKey(obj));
return parse(obj);
}
}
public Element parse(JsonObject object, Map<JsonElement, LocationData> map) throws Exception {
this.map = map;
return parse(object);
}
public Element parse(JsonObject object) throws Exception {
JsonElement rt = object.get("resourceType");
if (rt == null) {
logError(line(object), col(object), "$", IssueType.INVALID, "Unable to find resourceType property", IssueSeverity.FATAL);
return null;
} else {
String name = rt.getAsString();
String path = "/"+name;
StructureDefinition sd = getDefinition(line(object), col(object), name);
if (sd == null)
return null;
Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd));
checkObject(object, path);
result.markLocation(line(object), col(object));
result.setType(name);
parseChildren(path, object, result, true);
result.numberChildren();
return result;
}
}
private void checkObject(JsonObject object, String path) throws FHIRFormatError {
if (policy == ValidationPolicy.EVERYTHING) {
boolean found = false;
for (Entry<String, JsonElement> e : object.entrySet()) {
// if (!e.getKey().equals("fhir_comments")) {
found = true;
break;
// }
}
if (!found)
logError(line(object), col(object), path, IssueType.INVALID, "Object must have some content", IssueSeverity.ERROR);
}
}
private void parseChildren(String path, JsonObject object, Element context, boolean hasResourceType) throws DefinitionException, FHIRFormatError {
reapComments(object, context);
List<Property> properties = getChildProperties(context.getProperty(), context.getName(), null);
Set<String> processed = new HashSet<String>();
if (hasResourceType)
processed.add("resourceType");
processed.add("fhir_comments");
// note that we do not trouble ourselves to maintain the wire format order here - we don't even know what it was anyway
// first pass: process the properties
for (Property property : properties) {
if (property.isChoice()) {
for (TypeRefComponent type : property.getDefinition().getType()) {
String eName = property.getName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getCode());
if (!ParserBase.isPrimitive(type.getCode()) && object.has(eName)) {
parseChildComplex(path, object, context, processed, property, eName);
break;
} else if (ParserBase.isPrimitive(type.getCode()) && (object.has(eName) || object.has("_"+eName))) {
parseChildPrimitive(object, context, processed, property, path, eName);
break;
}
}
} else if (property.isPrimitive(null)) {
parseChildPrimitive(object, context, processed, property, path, property.getName());
} else if (object.has(property.getName())) {
parseChildComplex(path, object, context, processed, property, property.getName());
}
}
// second pass: check for things not processed
if (policy != ValidationPolicy.NONE) {
for (Entry<String, JsonElement> e : object.entrySet()) {
if (!processed.contains(e.getKey())) {
logError(line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, "Unrecognised property '@"+e.getKey()+"'", IssueSeverity.ERROR);
}
}
}
}
private void parseChildComplex(String path, JsonObject object, Element context, Set<String> processed, Property property, String name) throws FHIRFormatError, DefinitionException {
processed.add(name);
String npath = path+"/"+property.getName();
JsonElement e = object.get(name);
if (property.isList() && (e instanceof JsonArray)) {
JsonArray arr = (JsonArray) e;
for (JsonElement am : arr) {
parseChildComplexInstance(npath, object, context, property, name, am);
}
} else {
parseChildComplexInstance(npath, object, context, property, name, e);
}
}
private void parseChildComplexInstance(String npath, JsonObject object, Element context, Property property, String name, JsonElement e) throws FHIRFormatError, DefinitionException {
if (e instanceof JsonObject) {
JsonObject child = (JsonObject) e;
Element n = new Element(name, property).markLocation(line(child), col(child));
checkObject(child, npath);
context.getChildren().add(n);
if (property.isResource())
parseResource(npath, child, n);
else
parseChildren(npath, child, n, false);
} else
logError(line(e), col(e), npath, IssueType.INVALID, "This property must be "+(property.isList() ? "an Array" : "an Object")+", not a "+e.getClass().getName(), IssueSeverity.ERROR);
}
private void parseChildPrimitive(JsonObject object, Element context, Set<String> processed, Property property, String path, String name) throws FHIRFormatError, DefinitionException {
String npath = path+"/"+property.getName();
processed.add(name);
processed.add("_"+name);
JsonElement main = object.has(name) ? object.get(name) : null;
JsonElement fork = object.has("_"+name) ? object.get("_"+name) : null;
if (main != null || fork != null) {
if (property.isList() && ((main == null) || (main instanceof JsonArray)) &&((fork == null) || (fork instanceof JsonArray)) ) {
JsonArray arr1 = (JsonArray) main;
JsonArray arr2 = (JsonArray) fork;
for (int i = 0; i < Math.max(arrC(arr1), arrC(arr2)); i++) {
JsonElement m = arrI(arr1, i);
JsonElement f = arrI(arr2, i);
parseChildPrimitiveInstance(context, property, name, npath, m, f);
}
} else
parseChildPrimitiveInstance(context, property, name, npath, main, fork);
}
}
private JsonElement arrI(JsonArray arr, int i) {
return arr == null || i >= arr.size() || arr.get(i) instanceof JsonNull ? null : arr.get(i);
}
private int arrC(JsonArray arr) {
return arr == null ? 0 : arr.size();
}
private void parseChildPrimitiveInstance(Element context, Property property, String name, String npath,
JsonElement main, JsonElement fork) throws FHIRFormatError, DefinitionException {
if (main != null && !(main instanceof JsonPrimitive))
logError(line(main), col(main), npath, IssueType.INVALID, "This property must be an simple value, not a "+main.getClass().getName(), IssueSeverity.ERROR);
else if (fork != null && !(fork instanceof JsonObject))
logError(line(fork), col(fork), npath, IssueType.INVALID, "This property must be an object, not a "+fork.getClass().getName(), IssueSeverity.ERROR);
else {
Element n = new Element(name, property).markLocation(line(main != null ? main : fork), col(main != null ? main : fork));
context.getChildren().add(n);
if (main != null) {
JsonPrimitive p = (JsonPrimitive) main;
n.setValue(p.getAsString());
if (!n.getProperty().isChoice() && n.getType().equals("xhtml")) {
try {
n.setXhtml(new XhtmlParser().setValidatorMode(policy == ValidationPolicy.EVERYTHING).parse(n.getValue(), null).getDocumentElement());
} catch (Exception e) {
logError(line(main), col(main), npath, IssueType.INVALID, "Error parsing XHTML: "+e.getMessage(), IssueSeverity.ERROR);
}
}
if (policy == ValidationPolicy.EVERYTHING) {
// now we cross-check the primitive format against the stated type
if (Utilities.existsInList(n.getType(), "boolean")) {
if (!p.isBoolean())
logError(line(main), col(main), npath, IssueType.INVALID, "Error parsing JSON: the primitive value must be a boolean", IssueSeverity.ERROR);
} else if (Utilities.existsInList(n.getType(), "integer", "unsignedInt", "positiveInt", "decimal")) {
if (!p.isNumber())
logError(line(main), col(main), npath, IssueType.INVALID, "Error parsing JSON: the primitive value must be a number", IssueSeverity.ERROR);
} else if (!p.isString())
logError(line(main), col(main), npath, IssueType.INVALID, "Error parsing JSON: the primitive value must be a string", IssueSeverity.ERROR);
}
}
if (fork != null) {
JsonObject child = (JsonObject) fork;
checkObject(child, npath);
parseChildren(npath, child, n, false);
}
}
}
private void parseResource(String npath, JsonObject res, Element parent) throws DefinitionException, FHIRFormatError {
JsonElement rt = res.get("resourceType");
if (rt == null) {
logError(line(res), col(res), npath, IssueType.INVALID, "Unable to find resourceType property", IssueSeverity.FATAL);
} else {
String name = rt.getAsString();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+name);
if (sd == null)
throw new FHIRFormatError("Contained resource does not appear to be a FHIR resource (unknown name '"+name+"')");
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), parent.getProperty().getName().equals("contained") ? SpecialElement.CONTAINED : SpecialElement.BUNDLE_ENTRY);
parent.setType(name);
parseChildren(npath, res, parent, true);
}
}
private void reapComments(JsonObject object, Element context) {
if (object.has("fhir_comments")) {
JsonArray arr = object.getAsJsonArray("fhir_comments");
for (JsonElement e : arr) {
context.getComments().add(e.getAsString());
}
}
}
private int line(JsonElement e) {
if (map == null|| !map.containsKey(e))
return -1;
else
return map.get(e).getLine();
}
private int col(JsonElement e) {
if (map == null|| !map.containsKey(e))
return -1;
else
return map.get(e).getCol();
}
protected void prop(String name, String value) throws IOException {
if (name != null)
json.name(name);
json.value(value);
}
protected void open(String name) throws IOException {
if (name != null)
json.name(name);
json.beginObject();
}
protected void close() throws IOException {
json.endObject();
}
protected void openArray(String name) throws IOException {
if (name != null)
json.name(name);
json.beginArray();
}
protected void closeArray() throws IOException {
json.endArray();
}
@Override
public void compose(Element e, OutputStream stream, OutputStyle style, String identity) throws Exception {
OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
if (style == OutputStyle.CANONICAL)
json = new JsonCreatorCanonical(osw);
else
json = new JsonCreatorGson(osw);
json.setIndent(style == OutputStyle.PRETTY ? " " : "");
json.beginObject();
prop("resourceType", e.getType());
Set<String> done = new HashSet<String>();
for (Element child : e.getChildren()) {
compose(e.getName(), e, done, child);
}
json.endObject();
json.finish();
osw.flush();
}
private void compose(String path, Element e, Set<String> done, Element child) throws IOException {
if (child.getSpecial() == SpecialElement.BUNDLE_ENTRY || !child.getProperty().isList()) {// for specials, ignore the cardinality of the stated type
compose(path, child);
} else if (!done.contains(child.getName())) {
done.add(child.getName());
List<Element> list = e.getChildrenByName(child.getName());
composeList(path, list);
}
}
private void composeList(String path, List<Element> list) throws IOException {
// there will be at least one element
String name = list.get(0).getName();
boolean complex = true;
if (list.get(0).isPrimitive()) {
boolean prim = false;
complex = false;
for (Element item : list) {
if (item.hasValue())
prim = true;
if (item.hasChildren())
complex = true;
}
if (prim) {
openArray(name);
for (Element item : list) {
if (item.hasValue())
primitiveValue(null, item);
else
json.nullValue();
}
closeArray();
}
name = "_"+name;
}
if (complex) {
openArray(name);
for (Element item : list) {
if (item.hasChildren()) {
open(null);
if (item.getProperty().isResource()) {
prop("resourceType", item.getType());
}
Set<String> done = new HashSet<String>();
for (Element child : item.getChildren()) {
compose(path+"."+name+"[]", item, done, child);
}
close();
} else
json.nullValue();
}
closeArray();
}
}
private void primitiveValue(String name, Element item) throws IOException {
if (name != null)
json.name(name);
String type = item.getType();
if (Utilities.existsInList(type, "boolean"))
json.value(item.getValue().trim().equals("true") ? new Boolean(true) : new Boolean(false));
else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt"))
json.value(new Integer(item.getValue()));
else if (Utilities.existsInList(type, "decimal"))
json.value(new BigDecimal(item.getValue()));
else
json.value(item.getValue());
}
private void compose(String path, Element element) throws IOException {
String name = element.getName();
if (element.isPrimitive() || ParserBase.isPrimitive(element.getType())) {
if (element.hasValue())
primitiveValue(name, element);
name = "_"+name;
}
if (element.hasChildren()) {
open(name);
if (element.getProperty().isResource()) {
prop("resourceType", element.getType());
}
Set<String> done = new HashSet<String>();
for (Element child : element.getChildren()) {
compose(path+"."+element.getName(), element, done, child);
}
close();
}
}
}

View File

@ -0,0 +1,29 @@
package org.hl7.fhir.dstu2016may.metamodel;
import java.io.InputStream;
import java.io.OutputStream;
import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
public class Manager {
public enum FhirFormat { XML, JSON, JSONLD, TURTLE }
public static Element parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws Exception {
return makeParser(context, inputFormat).parse(source);
}
public static void compose(IWorkerContext context, Element e, OutputStream destination, FhirFormat outputFormat, OutputStyle style, String base) throws Exception {
makeParser(context, outputFormat).compose(e, destination, style, base);
}
public static ParserBase makeParser(IWorkerContext context, FhirFormat format) {
switch (format) {
case JSON : return new JsonParser(context);
case XML : return new XmlParser(context);
}
return null;
}
}

View File

@ -0,0 +1,166 @@
package org.hl7.fhir.dstu2016may.metamodel;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu2016may.formats.FormatUtilities;
import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle;
import org.hl7.fhir.dstu2016may.model.ElementDefinition;
import org.hl7.fhir.dstu2016may.model.ElementDefinition.PropertyRepresentation;
import org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
import org.hl7.fhir.dstu2016may.utils.ProfileUtilities;
import org.hl7.fhir.dstu2016may.utils.ToolingExtensions;
import org.hl7.fhir.dstu2016may.validation.ValidationMessage;
import org.hl7.fhir.dstu2016may.validation.ValidationMessage.Source;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.Utilities;
public abstract class ParserBase {
interface IErrorNotifier {
}
public enum ValidationPolicy { NONE, QUICK, EVERYTHING }
public static boolean isPrimitive(String code) {
return Utilities.existsInList(code,
"xhtml", "boolean", "integer", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime",
"time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "xhtml", "base64Binary");
}
protected IWorkerContext context;
protected ValidationPolicy policy;
protected List<ValidationMessage> errors;
public ParserBase(IWorkerContext context) {
super();
this.context = context;
policy = ValidationPolicy.NONE;
}
public void setupValidation(ValidationPolicy policy, List<ValidationMessage> errors) {
this.policy = policy;
this.errors = errors;
}
public abstract Element parse(InputStream stream) throws Exception;
public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base) throws Exception;
public void logError(int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError {
if (policy == ValidationPolicy.EVERYTHING) {
ValidationMessage msg = new ValidationMessage(Source.InstanceValidator, type, line, col, path, message, level);
errors.add(msg);
} else if (level == IssueSeverity.FATAL || (level == IssueSeverity.ERROR && policy == ValidationPolicy.QUICK))
throw new FHIRFormatError(message+String.format(" at line %d col %d", line, col));
}
protected StructureDefinition getDefinition(int line, int col, String ns, String name) throws FHIRFormatError {
if (ns == null) {
logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no namespace)", IssueSeverity.FATAL);
return null;
}
if (name == null) {
logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no name)", IssueSeverity.FATAL);
return null;
}
for (StructureDefinition sd : context.allStructures()) {
if (name.equals(sd.getId())) {
if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
return sd;
String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
if (ns != null && ns.equals(sns))
return sd;
}
}
logError(line, col, name, IssueType.STRUCTURE, "This does not appear to be a FHIR resource (unknown namespace/name '"+ns+"::"+name+"')", IssueSeverity.FATAL);
return null;
}
protected StructureDefinition getDefinition(int line, int col, String name) throws FHIRFormatError {
if (name == null) {
logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no name)", IssueSeverity.FATAL);
return null;
}
for (StructureDefinition sd : context.allStructures()) {
if (name.equals(sd.getId())) {
return sd;
}
}
logError(line, col, name, IssueType.STRUCTURE, "This does not appear to be a FHIR resource (unknown name '"+name+"')", IssueSeverity.FATAL);
return null;
}
protected List<Property> getChildProperties(Property property, String elementName, String statedType) throws DefinitionException {
ElementDefinition ed = property.getDefinition();
StructureDefinition sd = property.getStructure();
List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed);
if (children.isEmpty()) {
// ok, find the right definitions
String t = null;
if (ed.getType().size() == 1)
t = ed.getType().get(0).getCode();
else if (ed.getType().size() == 0)
throw new Error("types == 0, and no children found");
else {
t = ed.getType().get(0).getCode();
boolean all = true;
for (TypeRefComponent tr : ed.getType()) {
if (!tr.getCode().equals(t)) {
all = false;
break;
}
}
if (!all) {
// ok, it's polymorphic
if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
t = statedType;
if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaultype"))
t = ToolingExtensions.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaultype");
boolean ok = false;
for (TypeRefComponent tr : ed.getType())
if (tr.getCode().equals(t))
ok = true;
if (!ok)
throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+property.getDefinition().getPath());
} else {
t = elementName.substring(tail(ed.getPath()).length() - 3);
if (isPrimitive(lowFirst(t)))
t = lowFirst(t);
}
}
}
if (!"xhtml".equals(t)) {
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t);
if (sd == null)
throw new DefinitionException("Unable to find class '"+t+"' for name '"+elementName+"' on property "+property.getDefinition().getPath());
children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0));
}
}
List<Property> properties = new ArrayList<Property>();
for (ElementDefinition child : children) {
properties.add(new Property(context, child, sd));
}
return properties;
}
private String lowFirst(String t) {
return t.substring(0, 1).toLowerCase()+t.substring(1);
}
private String tail(String path) {
return path.contains(".") ? path.substring(path.lastIndexOf(".")+1) : path;
}
}

View File

@ -0,0 +1,157 @@
package org.hl7.fhir.dstu2016may.metamodel;
import org.hl7.fhir.dstu2016may.formats.FormatUtilities;
import org.hl7.fhir.dstu2016may.model.ElementDefinition;
import org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
import org.hl7.fhir.dstu2016may.utils.ToolingExtensions;
public class Property {
private IWorkerContext context;
private ElementDefinition definition;
private StructureDefinition structure;
private Boolean canBePrimitive;
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure) {
this.context = context;
this.definition = definition;
this.structure = structure;
}
public String getName() {
return definition.getPath().substring(definition.getPath().lastIndexOf(".")+1);
}
public ElementDefinition getDefinition() {
return definition;
}
public String getType() {
if (definition.getType().size() == 0)
return null;
else if (definition.getType().size() > 1) {
String tn = definition.getType().get(0).getCode();
for (int i = 1; i < definition.getType().size(); i++) {
if (!tn.equals(definition.getType().get(i).getCode()))
throw new Error("logic error, gettype when types > 1");
}
return tn;
} else
return definition.getType().get(0).getCode();
}
public String getType(String elementName) {
if (definition.getType().size() == 0)
return null;
else if (definition.getType().size() > 1) {
String t = definition.getType().get(0).getCode();
boolean all = true;
for (TypeRefComponent tr : definition.getType()) {
if (!t.equals(tr.getCode()))
all = false;
}
if (all)
return t;
String tail = definition.getPath().substring(definition.getPath().lastIndexOf(".")+1);
if (tail.endsWith("[x]") && elementName != null && elementName.startsWith(tail.substring(0, tail.length()-3))) {
String name = elementName.substring(tail.length()-3);
return ParserBase.isPrimitive(lowFirst(name)) ? lowFirst(name) : name;
} else
throw new Error("logic error, gettype when types > 1, name mismatch for "+elementName+" on at "+definition.getPath());
} else if (definition.getType().get(0).getCode() == null) {
return structure.getId();
} else
return definition.getType().get(0).getCode();
}
public boolean hasType(String elementName) {
if (definition.getType().size() == 0)
return false;
else if (definition.getType().size() > 1) {
String t = definition.getType().get(0).getCode();
boolean all = true;
for (TypeRefComponent tr : definition.getType()) {
if (!t.equals(tr.getCode()))
all = false;
}
if (all)
return true;
String tail = definition.getPath().substring(definition.getPath().lastIndexOf(".")+1);
if (tail.endsWith("[x]") && elementName.startsWith(tail.substring(0, tail.length()-3))) {
String name = elementName.substring(tail.length()-3);
return true;
} else
return false;
} else
return true;
}
public StructureDefinition getStructure() {
return structure;
}
public boolean isPrimitive(String name) {
return ParserBase.isPrimitive(getType(name));
}
private String lowFirst(String t) {
return t.substring(0, 1).toLowerCase()+t.substring(1);
}
public boolean isResource() {
return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) || "DomainResource".equals(definition.getType().get(0).getCode()));
}
public boolean isList() {
return !definition.getMax().equals("1");
}
public String getScopedPropertyName() {
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;
}
public boolean IsLogicalAndHasPrimitiveValue(String name) {
if (canBePrimitive!= null)
return canBePrimitive;
canBePrimitive = false;
if (structure.getKind() != StructureDefinitionKind.LOGICAL)
return false;
if (!hasType(name))
return false;
StructureDefinition sd = context.fetchResource(StructureDefinition.class, structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/")+1)+getType(name));
if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL)
return false;
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(sd.getId()+".value") && ed.getType().size() == 1 && ParserBase.isPrimitive(ed.getType().get(0).getCode())) {
canBePrimitive = true;
return true;
}
}
return false;
}
public boolean isChoice() {
if (definition.getType().size() <= 1)
return false;
String tn = definition.getType().get(0).getCode();
for (int i = 1; i < definition.getType().size(); i++)
if (!definition.getType().get(i).getCode().equals(tn))
return true;
return false;
}
}

View File

@ -0,0 +1,412 @@
package org.hl7.fhir.dstu2016may.metamodel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXSource;
import org.hl7.fhir.dstu2016may.formats.FormatUtilities;
import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle;
import org.hl7.fhir.dstu2016may.metamodel.Element.SpecialElement;
import org.hl7.fhir.dstu2016may.model.DateTimeType;
import org.hl7.fhir.dstu2016may.model.ElementDefinition.PropertyRepresentation;
import org.hl7.fhir.dstu2016may.model.Enumeration;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
import org.hl7.fhir.dstu2016may.utils.ToolingExtensions;
import org.hl7.fhir.dstu2016may.utils.XmlLocationAnnotator;
import org.hl7.fhir.dstu2016may.utils.XmlLocationData;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
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.hl7.fhir.utilities.xml.XMLUtil;
import org.hl7.fhir.utilities.xml.XMLWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
public class XmlParser extends ParserBase {
public XmlParser(IWorkerContext context) {
super(context);
}
public Element parse(InputStream stream) throws Exception {
Document doc = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// xxe protection
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
factory.setNamespaceAware(true);
if (policy == ValidationPolicy.EVERYTHING) {
// use a slower parser that keeps location data
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer nullTransformer = transformerFactory.newTransformer();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
doc = docBuilder.newDocument();
DOMResult domResult = new DOMResult(doc);
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(false);
SAXParser saxParser = spf.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
// xxe protection
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
XmlLocationAnnotator locationAnnotator = new XmlLocationAnnotator(xmlReader, doc);
InputSource inputSource = new InputSource(stream);
SAXSource saxSource = new SAXSource(locationAnnotator, inputSource);
nullTransformer.transform(saxSource, domResult);
} else {
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(stream);
}
} catch (Exception e) {
logError(0, 0, "(syntax)", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL);
doc = null;
}
if (doc == null)
return null;
else
return parse(doc);
}
private void checkForProcessingInstruction(Document document) throws FHIRFormatError {
if (policy == ValidationPolicy.EVERYTHING) {
Node node = document.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE)
logError(line(document), col(document), "(document)", IssueType.INVALID, "No processing instructions allowed in resources", IssueSeverity.ERROR);
node = node.getNextSibling();
}
}
}
private int line(Node node) {
XmlLocationData loc = (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY);
return loc == null ? 0 : loc.getStartLine();
}
private int col(Node node) {
XmlLocationData loc = (XmlLocationData) node.getUserData(XmlLocationData.LOCATION_DATA_KEY);
return loc == null ? 0 : loc.getStartColumn();
}
public Element parse(Document doc) throws Exception {
checkForProcessingInstruction(doc);
org.w3c.dom.Element element = doc.getDocumentElement();
return parse(element);
}
public Element parse(org.w3c.dom.Element element) throws Exception {
String ns = element.getNamespaceURI();
String name = element.getLocalName();
String path = "/"+pathPrefix(ns)+name;
StructureDefinition sd = getDefinition(line(element), col(element), ns, name);
if (sd == null)
return null;
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd));
checkElement(element, path, result.getProperty());
result.markLocation(line(element), col(element));
result.setType(element.getLocalName());
parseChildren(path, element, result);
result.numberChildren();
return result;
}
private String pathPrefix(String ns) {
if (Utilities.noString(ns))
return "";
if (ns.equals(FormatUtilities.FHIR_NS))
return "f:";
if (ns.equals(FormatUtilities.XHTML_NS))
return "h:";
if (ns.equals("urn:hl7-org:v3"))
return "v3:";
return "?:";
}
private boolean empty(org.w3c.dom.Element element) {
for (int i = 0; i < element.getAttributes().getLength(); i++) {
String n = element.getAttributes().item(i).getNodeName();
if (!n.equals("xmlns") && !n.startsWith("xmlns:"))
return false;
}
if (!Utilities.noString(element.getTextContent().trim()))
return false;
Node n = element.getFirstChild();
while (n != null) {
if (n.getNodeType() == Node.ELEMENT_NODE)
return false;
n = n.getNextSibling();
}
return true;
}
private void checkElement(org.w3c.dom.Element element, String path, Property prop) throws FHIRFormatError {
if (policy == ValidationPolicy.EVERYTHING) {
if (empty(element))
logError(line(element), col(element), path, IssueType.INVALID, "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");
if (!element.getNamespaceURI().equals(ns))
logError(line(element), col(element), path, IssueType.INVALID, "Wrong namespace - expected '"+ns+"'", IssueSeverity.ERROR);
}
}
public Element parse(org.w3c.dom.Element base, String type) throws Exception {
StructureDefinition sd = getDefinition(0, 0, FormatUtilities.FHIR_NS, type);
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd));
String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName();
checkElement(base, path, result.getProperty());
result.setType(base.getLocalName());
parseChildren(path, base, result);
result.numberChildren();
return result;
}
private void parseChildren(String path, org.w3c.dom.Element node, Element context) throws Exception {
// this parsing routine retains the original order in a the XML file, to support validation
reapComments(node, context);
List<Property> properties = getChildProperties(context.getProperty(), context.getName(), XMLUtil.getXsiType(node));
String text = XMLUtil.getDirectText(node).trim();
if (!Utilities.noString(text)) {
Property property = getTextProp(properties);
if (property != null) {
context.getChildren().add(new Element(property.getName(), property, property.getType(), text).markLocation(line(node), col(node)));
} else {
logError(line(node), col(node), path, IssueType.STRUCTURE, "Text should not be present", IssueSeverity.ERROR);
}
}
for (int i = 0; i < node.getAttributes().getLength(); i++) {
Node attr = node.getAttributes().item(i);
if (!(attr.getNodeName().equals("xmlns") || attr.getNodeName().startsWith("xmlns:"))) {
Property property = getAttrProp(properties, attr.getNodeName());
if (property != null) {
String av = attr.getNodeValue();
if (ToolingExtensions.hasExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"))
av = convertForDateFormat(ToolingExtensions.readStringExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), av);
if (property.getName().equals("value") && context.isPrimitive())
context.setValue(av);
else
context.getChildren().add(new Element(property.getName(), property, property.getType(), av).markLocation(line(node), col(node)));
} else {
logError(line(node), col(node), path, IssueType.STRUCTURE, "Undefined attribute '@"+attr.getNodeName()+"'", IssueSeverity.ERROR);
}
}
}
Node child = node.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
Property property = getElementProp(properties, child.getLocalName());
if (property != null) {
if (!property.isChoice() && "xhtml".equals(property.getType())) {
XhtmlNode xhtml = new XhtmlParser().setValidatorMode(true).parseHtmlNode((org.w3c.dom.Element) child);
context.getChildren().add(new Element("div", property, "xhtml", new XhtmlComposer().setXmlOnly(true).compose(xhtml)).setXhtml(xhtml).markLocation(line(child), col(child)));
} else {
String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName();
Element n = new Element(child.getLocalName(), property).markLocation(line(child), col(child));
checkElement((org.w3c.dom.Element) child, npath, n.getProperty());
boolean ok = true;
if (property.isChoice()) {
if (property.getDefinition().hasRepresentation(PropertyRepresentation.TYPEATTR)) {
String xsiType = ((org.w3c.dom.Element) child).getAttributeNS(FormatUtilities.NS_XSI, "type");
if (xsiType == null) {
logError(line(child), col(child), path, IssueType.STRUCTURE, "No type found on '"+child.getLocalName()+'"', IssueSeverity.ERROR);
ok = false;
} else {
if (xsiType.contains(":"))
xsiType = xsiType.substring(xsiType.indexOf(":")+1);
n.setType(xsiType);
}
} else
n.setType(n.getType());
}
context.getChildren().add(n);
if (ok) {
if (property.isResource())
parseResource(npath, (org.w3c.dom.Element) child, n);
else
parseChildren(npath, (org.w3c.dom.Element) child, n);
}
}
} else
logError(line(child), col(child), path, IssueType.STRUCTURE, "Undefined element '"+child.getLocalName()+'"', IssueSeverity.ERROR);
} else if (child.getNodeType() == Node.CDATA_SECTION_NODE){
logError(line(child), col(child), path, IssueType.STRUCTURE, "CDATA is not allowed", IssueSeverity.ERROR);
} else if (!Utilities.existsInList(child.getNodeType(), 3, 8)) {
logError(line(child), col(child), path, IssueType.STRUCTURE, "Node type "+Integer.toString(child.getNodeType())+" is not allowed", IssueSeverity.ERROR);
}
child = child.getNextSibling();
}
}
private Property getElementProp(List<Property> properties, String nodeName) {
for (Property p : properties)
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;
}
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;
return null;
}
private Property getTextProp(List<Property> properties) {
for (Property p : properties)
if (p.getDefinition().hasRepresentation(PropertyRepresentation.XMLTEXT))
return p;
return null;
}
private String convertForDateFormat(String fmt, String av) throws FHIRException {
if ("v3".equals(fmt)) {
DateTimeType d = DateTimeType.parseV3(av);
return d.asStringValue();
} else
throw new FHIRException("Unknown Data format '"+fmt+"'");
}
private void parseResource(String string, org.w3c.dom.Element container, Element parent) throws Exception {
org.w3c.dom.Element res = XMLUtil.getFirstChild(container);
String name = res.getLocalName();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+name);
if (sd == null)
throw new FHIRFormatError("Contained resource does not appear to be a FHIR resource (unknown name '"+res.getLocalName()+"')");
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), parent.getProperty().getName().equals("contained") ? SpecialElement.CONTAINED : SpecialElement.BUNDLE_ENTRY);
parent.setType(name);
parseChildren(res.getLocalName(), res, parent);
}
private void reapComments(org.w3c.dom.Element element, Element context) {
Node node = element.getPreviousSibling();
while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
if (node.getNodeType() == Node.COMMENT_NODE)
context.getComments().add(0, node.getTextContent());
node = node.getPreviousSibling();
}
node = element.getLastChild();
while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
node = node.getPreviousSibling();
}
while (node != null) {
if (node.getNodeType() == Node.COMMENT_NODE)
context.getComments().add(node.getTextContent());
node = node.getNextSibling();
}
}
private boolean isAttr(Property property) {
for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) {
if (r.getValue() == PropertyRepresentation.XMLATTR) {
return true;
}
}
return false;
}
private boolean isText(Property property) {
for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) {
if (r.getValue() == PropertyRepresentation.XMLTEXT) {
return true;
}
}
return false;
}
@Override
public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws Exception {
XMLWriter xml = new XMLWriter(stream, "UTF-8");
xml.setPretty(style == OutputStyle.PRETTY);
xml.start();
xml.setDefaultNamespace(e.getProperty().getNamespace());
composeElement(xml, e, e.getType());
xml.end();
}
private void composeElement(XMLWriter xml, Element element, String elementName) throws IOException {
for (String s : element.getComments()) {
xml.comment(s, true);
}
if (isText(element.getProperty())) {
xml.enter(elementName);
xml.text(element.getValue());
xml.exit(elementName);
} else if (element.isPrimitive() || (element.hasType() && ParserBase.isPrimitive(element.getType()))) {
if (element.getType().equals("xhtml")) {
xml.escapedText(element.getValue());
} else if (isText(element.getProperty())) {
xml.text(element.getValue());
} else {
if (element.hasValue())
xml.attribute("value", element.getValue());
if (element.hasChildren()) {
xml.enter(elementName);
for (Element child : element.getChildren())
composeElement(xml, child, child.getName());
xml.exit(elementName);
} else
xml.element(elementName);
}
} else {
for (Element child : element.getChildren()) {
if (isAttr(child.getProperty()))
xml.attribute(child.getName(), child.getValue());
}
xml.enter(elementName);
if (element.getSpecial() != null)
xml.enter(element.getType());
for (Element child : element.getChildren()) {
if (isText(child.getProperty()))
xml.text(child.getValue());
else if (!isAttr(child.getProperty()))
composeElement(xml, child, child.getName());
}
if (element.getSpecial() != null)
xml.exit(element.getType());
xml.exit(elementName);
}
}
}

View File

@ -0,0 +1,72 @@
package org.hl7.fhir.dstu2016may.terminologies;
import java.util.List;
import org.hl7.fhir.dstu2016may.model.BooleanType;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.CodeSystemPropertyComponent;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionPropertyComponent;
import org.hl7.fhir.dstu2016may.model.CodeSystem.PropertyType;
public class CodeSystemUtilities {
public static boolean isDeprecated(CodeSystem cs, ConceptDefinitionComponent def) {
for (ConceptDefinitionPropertyComponent p : def.getProperty()) {
if (p.getCode().equals("deprecated") && p.hasValue() && p.getValue() instanceof BooleanType)
return ((BooleanType) p.getValue()).getValue();
}
return false;
}
public static boolean isAbstract(CodeSystem cs, ConceptDefinitionComponent def) {
for (ConceptDefinitionPropertyComponent p : def.getProperty()) {
if (p.getCode().equals("abstract") && p.hasValue() && p.getValue() instanceof BooleanType)
return ((BooleanType) p.getValue()).getValue();
}
return false;
}
public static void setAbstract(CodeSystem cs, ConceptDefinitionComponent concept) {
defineAbstractProperty(cs);
concept.addProperty().setCode("abstract").setValue(new BooleanType(true));
}
public static void setDeprecated(CodeSystem cs, ConceptDefinitionComponent concept) {
defineAbstractProperty(cs);
concept.addProperty().setCode("deprecated").setValue(new BooleanType(true));
}
public static void defineAbstractProperty(CodeSystem cs) {
defineCodeSystemProperty(cs, "abstract", "Indicates that the code is abstract - only intended to be used as a selector for other concepts", PropertyType.BOOLEAN);
}
public static void defineDeprecatedProperty(CodeSystem cs) {
defineCodeSystemProperty(cs, "deprecated", "Indicates that the code should not longer be used", PropertyType.BOOLEAN);
}
public static void defineCodeSystemProperty(CodeSystem cs, String code, String description, PropertyType type) {
for (CodeSystemPropertyComponent p : cs.getProperty()) {
if (p.getCode().equals(code))
return;
}
cs.addProperty().setCode(code).setDescription(description).setType(type);
}
public static String getCodeDefinition(CodeSystem cs, String code) {
return getCodeDefinition(cs.getConcept(), code);
}
private static String getCodeDefinition(List<ConceptDefinitionComponent> list, String code) {
for (ConceptDefinitionComponent c : list) {
if (c.getCode().equals(code))
return c.getDefinition();
String s = getCodeDefinition(c.getConcept(), code);
if (s != null)
return s;
}
return null;
}
}

View File

@ -0,0 +1,10 @@
package org.hl7.fhir.dstu2016may.terminologies;
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ETooCostly;
import org.hl7.fhir.dstu2016may.utils.EOperationOutcome;
public interface ValueSetChecker {
boolean codeInValueSet(String system, String code) throws ETooCostly, EOperationOutcome, Exception;
}

View File

@ -0,0 +1,126 @@
package org.hl7.fhir.dstu2016may.terminologies;
import java.util.List;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.UriType;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.dstu2016may.utils.EOperationOutcome;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext.ValidationResult;
public class ValueSetCheckerSimple implements ValueSetChecker {
private ValueSet valueset;
private ValueSetExpanderFactory factory;
private IWorkerContext context;
public ValueSetCheckerSimple(ValueSet source, ValueSetExpanderFactory factory, IWorkerContext context) {
this.valueset = source;
this.factory = factory;
this.context = context;
}
@Override
public boolean codeInValueSet(String system, String code) throws EOperationOutcome, Exception {
if (valueset.hasCompose()) {
boolean ok = false;
for (UriType uri : valueset.getCompose().getImport()) {
ok = ok || inImport(uri.getValue(), system, code);
}
for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
ok = ok || inComponent(vsi, system, code);
}
for (ConceptSetComponent vsi : valueset.getCompose().getExclude()) {
ok = ok && !inComponent(vsi, system, code);
}
}
return false;
}
private boolean inImport(String uri, String system, String code) throws EOperationOutcome, Exception {
ValueSet vs = context.fetchResource(ValueSet.class, uri);
if (vs == null)
return false ; // we can't tell
return codeInExpansion(factory.getExpander().expand(vs), system, code);
}
private boolean codeInExpansion(ValueSetExpansionOutcome vso, String system, String code) throws EOperationOutcome, Exception {
if (vso.getService() != null) {
return vso.getService().codeInValueSet(system, code);
} else {
for (ValueSetExpansionContainsComponent c : vso.getValueset().getExpansion().getContains()) {
if (code.equals(c.getCode()) && (system == null || system.equals(c.getSystem())))
return true;
if (codeinExpansion(c, system, code))
return true;
}
}
return false;
}
private boolean codeinExpansion(ValueSetExpansionContainsComponent cnt, String system, String code) {
for (ValueSetExpansionContainsComponent c : cnt.getContains()) {
if (code.equals(c.getCode()) && system.equals(c.getSystem().toString()))
return true;
if (codeinExpansion(c, system, code))
return true;
}
return false;
}
private boolean inComponent(ConceptSetComponent vsi, String system, String code) {
if (!vsi.getSystem().equals(system))
return false;
// whether we know the system or not, we'll accept the stated codes at face value
for (ConceptReferenceComponent cc : vsi.getConcept())
if (cc.getCode().equals(code)) {
return true;
}
CodeSystem def = context.fetchCodeSystem(system);
if (def != null) {
if (!def.getCaseSensitive()) {
// well, ok, it's not case sensitive - we'll check that too now
for (ConceptReferenceComponent cc : vsi.getConcept())
if (cc.getCode().equalsIgnoreCase(code)) {
return false;
}
}
if (vsi.getConcept().isEmpty() && vsi.getFilter().isEmpty()) {
return codeInDefine(def.getConcept(), code, def.getCaseSensitive());
}
for (ConceptSetFilterComponent f: vsi.getFilter())
throw new Error("not done yet: "+f.getValue());
return false;
} else if (context.supportsSystem(system)) {
ValidationResult vv = context.validateCode(system, code, null, vsi);
return vv.isOk();
} else
// we don't know this system, and can't resolve it
return false;
}
private boolean codeInDefine(List<ConceptDefinitionComponent> concepts, String code, boolean caseSensitive) {
for (ConceptDefinitionComponent c : concepts) {
if (caseSensitive && code.equals(c.getCode()))
return true;
if (!caseSensitive && code.equalsIgnoreCase(c.getCode()))
return true;
if (codeInDefine(c.getConcept(), code, caseSensitive))
return true;
}
return false;
}
}

View File

@ -0,0 +1,64 @@
package org.hl7.fhir.dstu2016may.terminologies;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.hl7.fhir.dstu2016may.model.ValueSet;
public interface ValueSetExpander {
public class ETooCostly extends Exception {
public ETooCostly(String msg) {
super(msg);
}
}
/**
* Some value sets are just too big to expand. Instead of an expanded value set,
* you get back an interface that can test membership - usually on a server somewhere
*
* @author Grahame
*/
public class ValueSetExpansionOutcome {
private ValueSet valueset;
private ValueSetChecker service;
private String error;
public ValueSetExpansionOutcome(ValueSet valueset) {
super();
this.valueset = valueset;
this.service = null;
this.error = null;
}
public ValueSetExpansionOutcome(ValueSet valueset, String error) {
super();
this.valueset = valueset;
this.service = null;
this.error = error;
}
public ValueSetExpansionOutcome(ValueSetChecker service, String error) {
super();
this.valueset = null;
this.service = service;
this.error = error;
}
public ValueSetExpansionOutcome(String error) {
this.valueset = null;
this.service = null;
this.error = error;
}
public ValueSet getValueset() {
return valueset;
}
public ValueSetChecker getService() {
return service;
}
public String getError() {
return error;
}
}
public ValueSetExpansionOutcome expand(ValueSet source) throws ETooCostly, FileNotFoundException, IOException;
}

View File

@ -0,0 +1,34 @@
package org.hl7.fhir.dstu2016may.terminologies;
/*
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.
*/
public interface ValueSetExpanderFactory {
public ValueSetExpander getExpander();
}

View File

@ -0,0 +1,270 @@
package org.hl7.fhir.dstu2016may.terminologies;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.DateTimeType;
import org.hl7.fhir.dstu2016may.model.Factory;
import org.hl7.fhir.dstu2016may.model.PrimitiveType;
import org.hl7.fhir.dstu2016may.model.Type;
import org.hl7.fhir.dstu2016may.model.UriType;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.FilterOperator;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.Utilities;
public class ValueSetExpanderSimple implements ValueSetExpander {
private IWorkerContext context;
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private ValueSet focus;
private ValueSetExpanderFactory factory;
public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
super();
this.context = context;
this.factory = factory;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet source) {
try {
focus = source.copy();
focus.setExpansion(new ValueSet.ValueSetExpansionComponent());
focus.getExpansion().setTimestampElement(DateTimeType.now());
focus.getExpansion().setIdentifier(Factory.createUUID());
if (source.hasCompose())
handleCompose(source.getCompose(), focus.getExpansion().getParameter());
for (ValueSetExpansionContainsComponent c : codes) {
if (map.containsKey(key(c))) {
focus.getExpansion().getContains().add(c);
}
}
return new ValueSetExpansionOutcome(focus, null);
} catch (RuntimeException e) {
// TODO: we should put something more specific instead of just Exception below, since
// it swallows bugs.. what would be expected to be caught there?
throw e;
} catch (Exception e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage());
}
}
private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException, ETooCostly, FileNotFoundException, IOException {
for (UriType imp : compose.getImport())
importValueSet(imp.getValue(), params);
for (ConceptSetComponent inc : compose.getInclude())
includeCodes(inc, params);
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params);
}
private void importValueSet(String value, List<ValueSetExpansionParameterComponent> params) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException {
if (value == null)
throw new TerminologyServiceException("unable to find value set with no identity");
ValueSet vs = context.fetchResource(ValueSet.class, value);
if (vs == null)
throw new TerminologyServiceException("Unable to find imported value set "+value);
ValueSetExpansionOutcome vso = factory.getExpander().expand(vs);
if (vso.getService() != null)
throw new TerminologyServiceException("Unable to expand imported value set "+value);
if (vs.hasVersion())
if (!existsInParams(params, "version", new UriType(vs.getUrl()+"?version="+vs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl()+"?version="+vs.getVersion())));
for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
for (ValueSetExpansionContainsComponent c : vso.getValueset().getExpansion().getContains()) {
addCode(c.getSystem(), c.getCode(), c.getDisplay());
}
}
private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) {
for (ValueSetExpansionParameterComponent p : params) {
if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false))
return true;
}
return false;
}
private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException, ETooCostly {
if (context.supportsSystem(inc.getSystem())) {
try {
int i = codes.size();
addCodes(context.expandVS(inc), params);
if (codes.size() > i)
return;
} catch (Exception e) {
// ok, we'll try locally
}
}
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if (cs == null)
throw new TerminologyServiceException("unable to find code system "+inc.getSystem().toString());
if (cs.hasVersion())
if (!existsInParams(params, "version", new UriType(cs.getUrl()+"?version="+cs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl()+"?version="+cs.getVersion())));
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def);
}
}
for (ConceptReferenceComponent c : inc.getConcept()) {
addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay());
}
if (inc.getFilter().size() > 1)
throw new TerminologyServiceException("Multiple filters not handled yet"); // need to and them, and this isn't done yet. But this shouldn't arise in non loinc and snomed value sets
if (inc.getFilter().size() == 1) {
ConceptSetFilterComponent fc = inc.getFilter().get(0);
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
// special: all non-abstract codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'");
addCodeAndDescendents(cs, inc.getSystem(), def);
} else
throw new NotImplementedException("not done yet");
}
}
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) throws ETooCostly {
if (expand.getContains().size() > 500)
throw new ETooCostly("Too many codes to display (>"+Integer.toString(expand.getContains().size())+")");
for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
addCode(c.getSystem(), c.getCode(), c.getDisplay());
}
}
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def) {
if (!CodeSystemUtilities.isDeprecated(cs, def)) {
if (!CodeSystemUtilities.isAbstract(cs, def))
addCode(system, def.getCode(), def.getDisplay());
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c);
}
}
private void excludeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException {
CodeSystem cs = context.fetchCodeSystem(inc.getSystem().toString());
if (cs == null)
throw new TerminologyServiceException("unable to find value set "+inc.getSystem().toString());
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system
// for (ConceptDefinitionComponent def : cs.getDefine().getConcept()) {
//!!!! addCodeAndDescendents(inc.getSystem(), def);
// }
}
for (ConceptReferenceComponent c : inc.getConcept()) {
// we don't need to check whether the codes are valid here- they can't have gotten into this list if they aren't valid
map.remove(key(inc.getSystem(), c.getCode()));
}
if (inc.getFilter().size() > 0)
throw new NotImplementedException("not done yet");
}
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null)
throw new TerminologyServiceException("Unable to find code '"+code+"' in code system "+cs.getUrl());
return def.getDisplay();
}
private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) {
for (ConceptDefinitionComponent c : clist) {
if (code.equals(c.getCode()))
return c;
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
if (v != null)
return v;
}
return null;
}
private String key(ValueSetExpansionContainsComponent c) {
return key(c.getSystem(), c.getCode());
}
private String key(String uri, String code) {
return "{"+uri+"}"+code;
}
private void addCode(String system, String code, String display) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(system);
n.setCode(code);
n.setDisplay(display);
String s = key(n);
if (!map.containsKey(s)) {
codes.add(n);
map.put(s, n);
}
}
}

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.dstu2016may.utils;
import org.hl7.fhir.dstu2016may.model.OperationOutcome;
public class EOperationOutcome extends Exception {
private static final long serialVersionUID = 8887222532359256131L;
private OperationOutcome outcome;
public EOperationOutcome(OperationOutcome outcome) {
super();
this.outcome = outcome;
}
public OperationOutcome getOutcome() {
return outcome;
}
}

View File

@ -0,0 +1,318 @@
package org.hl7.fhir.dstu2016may.utils;
import org.hl7.fhir.dstu2016may.model.ExpressionNode;
import org.hl7.fhir.dstu2016may.model.ExpressionNode.SourceLocation;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.Utilities;
// shared lexer for concrete syntaxes
// - FluentPath
// - Mapping language
public class FHIRLexer {
public class FHIRLexerException extends FHIRException {
public FHIRLexerException() {
super();
}
public FHIRLexerException(String message, Throwable cause) {
super(message, cause);
}
public FHIRLexerException(String message) {
super(message);
}
public FHIRLexerException(Throwable cause) {
super(cause);
}
}
private String path;
private int cursor;
private int currentStart;
private String current;
private SourceLocation currentLocation;
private SourceLocation currentStartLocation;
private int id;
public FHIRLexer(String source) throws FHIRLexerException {
this.path = source;
currentLocation = new SourceLocation(1, 1);
next();
}
public String getCurrent() {
return current;
}
public SourceLocation getCurrentLocation() {
return currentLocation;
}
public boolean isConstant(boolean incDoubleQuotes) {
return current.charAt(0) == '\'' || (incDoubleQuotes && current.charAt(0) == '"') || current.charAt(0) == '@' || current.charAt(0) == '%' || current.charAt(0) == '-' || (current.charAt(0) >= '0' && current.charAt(0) <= '9') || current.equals("true") || current.equals("false") || current.equals("{}");
}
public boolean isStringConstant() {
return current.charAt(0) == '\'' || current.charAt(0) == '"';
}
public String take() throws FHIRLexerException {
String s = current;
next();
return s;
}
public boolean isToken() {
if (Utilities.noString(current))
return false;
if (current.startsWith("$"))
return true;
if (current.equals("*") || current.equals("**"))
return true;
if ((current.charAt(0) >= 'A' && current.charAt(0) <= 'Z') || (current.charAt(0) >= 'a' && current.charAt(0) <= 'z')) {
for (int i = 1; i < current.length(); i++)
if (!( (current.charAt(1) >= 'A' && current.charAt(1) <= 'Z') || (current.charAt(1) >= 'a' && current.charAt(1) <= 'z') ||
(current.charAt(1) >= '0' && current.charAt(1) <= '9')))
return false;
return true;
}
return false;
}
public FHIRLexerException error(String msg) {
return error(msg, currentLocation.toString());
}
public FHIRLexerException error(String msg, String location) {
return new FHIRLexerException("Error in "+path+" at "+location+": "+msg);
}
public void next() throws FHIRLexerException {
current = null;
boolean last13 = false;
while (cursor < path.length() && Character.isWhitespace(path.charAt(cursor))) {
if (path.charAt(cursor) == '\r') {
currentLocation.setLine(currentLocation.getLine() + 1);
currentLocation.setColumn(1);
last13 = true;
} else if (!last13 && (path.charAt(cursor) == '\n')) {
currentLocation.setLine(currentLocation.getLine() + 1);
currentLocation.setColumn(1);
last13 = false;
} else {
last13 = false;
currentLocation.setColumn(currentLocation.getColumn() + 1);
}
cursor++;
}
currentStart = cursor;
currentStartLocation = currentLocation;
if (cursor < path.length()) {
char ch = path.charAt(cursor);
if (ch == '!' || ch == '>' || ch == '<' || ch == ':' || ch == '-' || ch == '=') {
cursor++;
if (cursor < path.length() && (path.charAt(cursor) == '=' || path.charAt(cursor) == '~' || path.charAt(cursor) == '-'))
cursor++;
current = path.substring(currentStart, cursor);
} else if (ch >= '0' && ch <= '9') {
cursor++;
boolean dotted = false;
while (cursor < path.length() && ((path.charAt(cursor) >= '0' && path.charAt(cursor) <= '9') || (path.charAt(cursor) == '.') && !dotted)) {
if (path.charAt(cursor) == '.')
dotted = true;
cursor++;
}
if (path.charAt(cursor-1) == '.')
cursor--;
current = path.substring(currentStart, cursor);
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
while (cursor < path.length() && ((path.charAt(cursor) >= 'A' && path.charAt(cursor) <= 'Z') || (path.charAt(cursor) >= 'a' && path.charAt(cursor) <= 'z') ||
(path.charAt(cursor) >= '0' && path.charAt(cursor) <= '9') || path.charAt(cursor) == '_'))
cursor++;
current = path.substring(currentStart, cursor);
} else if (ch == '%') {
cursor++;
if (cursor < path.length() && (path.charAt(cursor) == '"')) {
cursor++;
while (cursor < path.length() && (path.charAt(cursor) != '"'))
cursor++;
cursor++;
} else
while (cursor < path.length() && ((path.charAt(cursor) >= 'A' && path.charAt(cursor) <= 'Z') || (path.charAt(cursor) >= 'a' && path.charAt(cursor) <= 'z') ||
(path.charAt(cursor) >= '0' && path.charAt(cursor) <= '9') || path.charAt(cursor) == ':' || path.charAt(cursor) == '-'))
cursor++;
current = path.substring(currentStart, cursor);
} else if (ch == '/') {
cursor++;
if (cursor < path.length() && (path.charAt(cursor) == '/')) {
cursor++;
while (cursor < path.length() && !((path.charAt(cursor) == '\r') || path.charAt(cursor) == '\n'))
cursor++;
}
current = path.substring(currentStart, cursor);
} else if (ch == '$') {
cursor++;
while (cursor < path.length() && (path.charAt(cursor) >= 'a' && path.charAt(cursor) <= 'z'))
cursor++;
current = path.substring(currentStart, cursor);
} else if (ch == '{') {
cursor++;
ch = path.charAt(cursor);
if (ch == '}')
cursor++;
current = path.substring(currentStart, cursor);
} else if (ch == '"'){
cursor++;
boolean escape = false;
while (cursor < path.length() && (escape || path.charAt(cursor) != '"')) {
if (escape)
escape = false;
else
escape = (path.charAt(cursor) == '\\');
cursor++;
}
if (cursor == path.length())
throw error("Unterminated string");
cursor++;
current = "\""+path.substring(currentStart+1, cursor-1)+"\"";
} else if (ch == '\''){
cursor++;
char ech = ch;
boolean escape = false;
while (cursor < path.length() && (escape || path.charAt(cursor) != ech)) {
if (escape)
escape = false;
else
escape = (path.charAt(cursor) == '\\');
cursor++;
}
if (cursor == path.length())
throw error("Unterminated string");
cursor++;
current = path.substring(currentStart, cursor);
if (ech == '\'')
current = "\'"+current.substring(1, current.length() - 1)+"\'";
} else if (ch == '@'){
cursor++;
while (cursor < path.length() && isDateChar(path.charAt(cursor)))
cursor++;
current = path.substring(currentStart, cursor);
} else { // if CharInSet(ch, ['.', ',', '(', ')', '=', '$']) then
cursor++;
current = path.substring(currentStart, cursor);
}
}
}
private boolean isDateChar(char ch) {
return ch == '-' || ch == ':' || ch == 'T' || ch == '+' || ch == 'Z' || Character.isDigit(ch);
}
public boolean isOp() {
return ExpressionNode.Operation.fromCode(current) != null;
}
public boolean done() {
return currentStart >= path.length();
}
public int nextId() {
id++;
return id;
}
public SourceLocation getCurrentStartLocation() {
return currentStartLocation;
}
// special case use
public void setCurrent(String current) {
this.current = current;
}
public boolean hasComment() {
return !done() && current.startsWith("//");
}
public boolean hasToken(String kw) {
return !done() && kw.equals(current);
}
public void token(String kw) throws FHIRLexerException {
if (!kw.equals(current))
throw error("Found \""+current+"\" expecting \""+kw+"\"");
next();
}
public String readConstant(String desc) throws FHIRLexerException {
if (!isStringConstant())
throw error("Found "+current+" expecting \"["+desc+"]\"");
return processConstant(take());
}
public String processConstant(String s) throws FHIRLexerException {
StringBuilder b = new StringBuilder();
int i = 1;
while (i < s.length()-1) {
char ch = s.charAt(i);
if (ch == '\\') {
i++;
switch (s.charAt(i)) {
case 't':
b.append('\t');
break;
case 'r':
b.append('\r');
break;
case 'n':
b.append('\n');
break;
case 'f':
b.append('\f');
break;
case '\'':
b.append('\'');
break;
case '\\':
b.append('\\');
break;
case '/':
b.append('\\');
break;
case 'u':
i++;
int uc = Integer.parseInt(s.substring(i, i+4), 16);
b.append((char) uc);
i = i + 4;
break;
default:
throw new FHIRLexerException("Unknown character escape \\"+s.charAt(i));
}
} else {
b.append(ch);
i++;
}
}
return b.toString();
}
public void skipToken(String token) throws FHIRLexerException {
if (getCurrent().equals(token))
next();
}
public String takeDottedToken() throws FHIRLexerException {
StringBuilder b = new StringBuilder();
b.append(take());
while (!done() && getCurrent().equals(".")) {
b.append(take());
b.append(take());
}
return b.toString();
}
void skipComments() throws FHIRLexerException {
while (!done() && hasComment())
next();
}
}

View File

@ -0,0 +1,294 @@
package org.hl7.fhir.dstu2016may.utils;
import java.util.List;
import java.util.Set;
import org.hl7.fhir.dstu2016may.formats.IParser;
import org.hl7.fhir.dstu2016may.formats.ParserType;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.CodeableConcept;
import org.hl7.fhir.dstu2016may.model.Coding;
import org.hl7.fhir.dstu2016may.model.ConceptMap;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.Resource;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.dstu2016may.validation.IResourceValidator;
/**
* This is the standard interface used for access to underlying FHIR
* services through the tools and utilities provided by the reference
* implementation.
*
* The functionality it provides is
* - get access to parsers, validators, narrative builders etc
* (you can't create these directly because they need access
* to the right context for their information)
*
* - find resources that the tools need to carry out their tasks
*
* - provide access to terminology services they need.
* (typically, these terminology service requests are just
* passed through to the local implementation's terminology
* service)
*
* @author Grahame
*/
public interface IWorkerContext {
// -- Parsers (read and write instances) ----------------------------------------
/**
* Get a parser to read/write instances. Use the defined type (will be extended
* as further types are added, though the only currently anticipate type is RDF)
*
* XML/JSON - the standard renderers
* XHTML - render the narrative only (generate it if necessary)
*
* @param type
* @return
*/
public IParser getParser(ParserType type);
/**
* Get a parser to read/write instances. Determine the type
* from the stated type. Supported value for type:
* - the recommended MIME types
* - variants of application/xml and application/json
* - _format values xml, json
*
* @param type
* @return
*/
public IParser getParser(String type);
/**
* Get a JSON parser
*
* @return
*/
public IParser newJsonParser();
/**
* Get an XML parser
*
* @return
*/
public IParser newXmlParser();
/**
* Get a validator that can check whether a resource is valid
*
* @return a prepared generator
* @
*/
public IResourceValidator newValidator();
// -- resource fetchers ---------------------------------------------------
/**
* Find an identified resource. The most common use of this is to access the the
* standard conformance resources that are part of the standard - structure
* definitions, value sets, concept maps, etc.
*
* Also, the narrative generator uses this, and may access any kind of resource
*
* The URI is called speculatively for things that might exist, so not finding
* a matching resouce, return null, not an error
*
* The URI can have one of 3 formats:
* - a full URL e.g. http://acme.org/fhir/ValueSet/[id]
* - a relative URL e.g. ValueSet/[id]
* - a logical id e.g. [id]
*
* It's an error if the second form doesn't agree with class_. It's an
* error if class_ is null for the last form
*
* @param resource
* @param Reference
* @return
* @throws Exception
*/
public <T extends Resource> T fetchResource(Class<T> class_, String uri);
/**
* find whether a resource is available.
*
* Implementations of the interface can assume that if hasResource ruturns
* true, the resource will usually be fetched subsequently
*
* @param class_
* @param uri
* @return
*/
public <T extends Resource> boolean hasResource(Class<T> class_, String uri);
// -- profile services ---------------------------------------------------------
public List<String> getResourceNames();
public List<StructureDefinition> allStructures();
// -- Terminology services ------------------------------------------------------
// these are the terminology services used internally by the tools
/**
* Find the code system definition for the nominated system uri.
* return null if there isn't one (then the tool might try
* supportsSystem)
*
* @param system
* @return
*/
public CodeSystem fetchCodeSystem(String system);
/**
* True if the underlying terminology service provider will do
* expansion and code validation for the terminology. Corresponds
* to the extension
*
* http://hl7.org/fhir/StructureDefinition/conformance-supported-system
*
* in the Conformance resource
*
* @param system
* @return
*/
public boolean supportsSystem(String system);
/**
* find concept maps for a source
* @param url
* @return
*/
public List<ConceptMap> findMapsForSource(String url);
/**
* ValueSet Expansion - see $expand
*
* @param source
* @return
*/
public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk);
/**
* Value set expanion inside the internal expansion engine - used
* for references to supported system (see "supportsSystem") for
* which there is no value set.
*
* @param inc
* @return
*/
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc);
public class ValidationResult {
private ConceptDefinitionComponent definition;
private IssueSeverity severity;
private String message;
public ValidationResult(IssueSeverity severity, String message) {
this.severity = severity;
this.message = message;
}
public ValidationResult(ConceptDefinitionComponent definition) {
this.definition = definition;
}
public ValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
this.severity = severity;
this.message = message;
this.definition = definition;
}
public boolean isOk() {
return definition != null;
}
public String getDisplay() {
//Don't want question-marks as that stops something more useful from being displayed, such as the code
//return definition == null ? "??" : definition.getDisplay();
return definition == null ? null : definition.getDisplay();
}
public ConceptDefinitionComponent asConceptDefinition() {
return definition;
}
public IssueSeverity getSeverity() {
return severity;
}
public String getMessage() {
return message;
}
}
/**
* Validation of a code - consult the terminology service
* to see whether it is known. If known, return a description of it
*
* note: always return a result, with either an error or a code description
*
* corresponds to 2 terminology service calls: $validate-code and $lookup
*
* @param system
* @param code
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display);
/**
* Validation of a code - consult the terminology service
* to see whether it is known. If known, return a description of it
* Also, check whether it's in the provided value set
*
* note: always return a result, with either an error or a code description, or both (e.g. known code, but not in the value set)
*
* corresponds to 2 terminology service calls: $validate-code and $lookup
*
* @param system
* @param code
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(Coding code, ValueSet vs);
public ValidationResult validateCode(CodeableConcept code, ValueSet vs);
/**
* Validation of a code - consult the terminology service
* to see whether it is known. If known, return a description of it
* Also, check whether it's in the provided value set fragment (for supported systems with no value set definition)
*
* note: always return a result, with either an error or a code description, or both (e.g. known code, but not in the value set)
*
* corresponds to 2 terminology service calls: $validate-code and $lookup
*
* @param system
* @param code
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi);
/**
* returns the recommended tla for the type
*
* @param name
* @return
*/
public String getAbbreviation(String name);
// return a set of types that have tails
public Set<String> typeTails();
public String oid2Uri(String code);
}

View File

@ -0,0 +1,498 @@
package org.hl7.fhir.dstu2016may.utils;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Stack;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.Utilities;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
/**
* This is created to get a json parser that can track line numbers... grr...
*
* @author Grahame Grieve
*
*/
public class JsonTrackingParser {
public enum TokenType {
Open, Close, String, Number, Colon, Comma, OpenArray, CloseArray, Eof, Null, Boolean;
}
public class LocationData {
private int line;
private int col;
protected LocationData(int line, int col) {
super();
this.line = line;
this.col = col;
}
public int getLine() {
return line;
}
public int getCol() {
return col;
}
public void newLine() {
line++;
col = 1;
}
public LocationData copy() {
return new LocationData(line, col);
}
}
private class State {
private String name;
private boolean isProp;
protected State(String name, boolean isProp) {
super();
this.name = name;
this.isProp = isProp;
}
public String getName() {
return name;
}
public boolean isProp() {
return isProp;
}
}
private class Lexer {
private String source;
private int cursor;
private String peek;
private String value;
private TokenType type;
private Stack<State> states = new Stack<State>();
private LocationData lastLocationBWS;
private LocationData lastLocationAWS;
private LocationData location;
private StringBuilder b = new StringBuilder();
public Lexer(String source) throws FHIRException {
this.source = source;
cursor = -1;
location = new LocationData(1, 1);
start();
}
private boolean more() {
return peek != null || cursor < source.length();
}
private String getNext(int length) throws FHIRException {
String result = "";
if (peek != null) {
if (peek.length() > length) {
result = peek.substring(0, length);
peek = peek.substring(length);
} else {
result = peek;
peek = null;
}
}
if (result.length() < length) {
int len = length - result.length();
if (cursor > source.length() - len)
throw error("Attempt to read past end of source");
result = result + source.substring(cursor+1, cursor+len+1);
cursor = cursor + len;
}
for (char ch : result.toCharArray())
if (ch == '\n')
location.newLine();
else
location.col++;
return result;
}
private char getNextChar() throws FHIRException {
if (peek != null) {
char ch = peek.charAt(0);
peek = peek.length() == 1 ? null : peek.substring(1);
return ch;
} else {
cursor++;
if (cursor >= source.length())
return (char) 0;
char ch = source.charAt(cursor);
if (ch == '\n') {
location.newLine();
} else {
location.col++;
}
return ch;
}
}
private void push(char ch){
peek = peek == null ? String.valueOf(ch) : String.valueOf(ch)+peek;
}
private void parseWord(String word, char ch, TokenType type) throws FHIRException {
this.type = type;
value = ""+ch+getNext(word.length()-1);
if (!value.equals(word))
throw error("Syntax error in json reading special word "+word);
}
private FHIRException error(String msg) {
return new FHIRException("Error parsing JSON source: "+msg+" at Line "+Integer.toString(location.line)+" (path=["+path()+"])");
}
private String path() {
if (states.empty())
return value;
else {
String result = "";
for (State s : states)
result = result + '/'+ s.getName();
result = result + value;
return result;
}
}
public void start() throws FHIRException {
// char ch = getNextChar();
// if (ch = '\.uEF')
// begin
// // skip BOM
// getNextChar();
// getNextChar();
// end
// else
// push(ch);
next();
}
public TokenType getType() {
return type;
}
public String getValue() {
return value;
}
public LocationData getLastLocationBWS() {
return lastLocationBWS;
}
public LocationData getLastLocationAWS() {
return lastLocationAWS;
}
public void next() throws FHIRException {
lastLocationBWS = location.copy();
char ch;
do {
ch = getNextChar();
} while (more() && Utilities.charInSet(ch, ' ', '\r', '\n', '\t'));
lastLocationAWS = location.copy();
if (!more()) {
type = TokenType.Eof;
} else {
switch (ch) {
case '{' :
type = TokenType.Open;
break;
case '}' :
type = TokenType.Close;
break;
case '"' :
type = TokenType.String;
b.setLength(0);
do {
ch = getNextChar();
if (ch == '\\') {
ch = getNextChar();
switch (ch) {
case '"': b.append('"'); break;
case '\\': b.append('\\'); break;
case '/': b.append('/'); break;
case 'n': b.append('\n'); break;
case 'r': b.append('\r'); break;
case 't': b.append('\t'); break;
case 'u': b.append((char) Integer.parseInt(getNext(4), 16)); break;
default :
throw error("unknown escape sequence: \\"+ch);
}
ch = ' ';
} else if (ch != '"')
b.append(ch);
} while (more() && (ch != '"'));
if (!more())
throw error("premature termination of json stream during a string");
value = b.toString();
break;
case ':' :
type = TokenType.Colon;
break;
case ',' :
type = TokenType.Comma;
break;
case '[' :
type = TokenType.OpenArray;
break;
case ']' :
type = TokenType.CloseArray;
break;
case 't' :
parseWord("true", ch, TokenType.Boolean);
break;
case 'f' :
parseWord("false", ch, TokenType.Boolean);
break;
case 'n' :
parseWord("null", ch, TokenType.Null);
break;
default:
if ((ch >= '0' && ch <= '9') || ch == '-') {
type = TokenType.Number;
b.setLength(0);
while (more() && ((ch >= '0' && ch <= '9') || ch == '-' || ch == '.')) {
b.append(ch);
ch = getNextChar();
}
value = b.toString();
push(ch);
} else
throw error("Unexpected char '"+ch+"' in json stream");
}
}
}
public String consume(TokenType type) throws FHIRException {
if (this.type != type)
throw error("JSON syntax error - found "+type.toString()+" expecting "+type.toString());
String result = value;
next();
return result;
}
}
enum ItemType {
Object, String, Number, Boolean, Array, End, Eof, Null;
}
private Map<JsonElement, LocationData> map;
private Lexer lexer;
private ItemType itemType = ItemType.Object;
private String itemName;
private String itemValue;
public static JsonObject parse(String source, Map<JsonElement, LocationData> map) throws FHIRException {
JsonTrackingParser self = new JsonTrackingParser();
self.map = map;
return self.parse(source);
}
private JsonObject parse(String source) throws FHIRException {
lexer = new Lexer(source);
JsonObject result = new JsonObject();
LocationData loc = lexer.location.copy();
if (lexer.getType() == TokenType.Open) {
lexer.next();
lexer.states.push(new State("", false));
}
else
throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());
parseProperty();
readObject(result, true);
map.put(result, loc);
return result;
}
private void readObject(JsonObject obj, boolean root) throws FHIRException {
map.put(obj, lexer.location.copy());
while (!(itemType == ItemType.End) || (root && (itemType == ItemType.Eof))) {
if (obj.has(itemName))
throw lexer.error("Duplicated property name: "+itemName);
switch (itemType) {
case Object:
JsonObject child = new JsonObject(); //(obj.path+'.'+ItemName);
LocationData loc = lexer.location.copy();
obj.add(itemName, child);
next();
readObject(child, false);
map.put(obj, loc);
break;
case Boolean :
JsonPrimitive v = new JsonPrimitive(Boolean.valueOf(itemValue));
obj.add(itemName, v);
map.put(v, lexer.location.copy());
break;
case String:
v = new JsonPrimitive(itemValue);
obj.add(itemName, v);
map.put(v, lexer.location.copy());
break;
case Number:
v = new JsonPrimitive(new BigDecimal(itemValue));
obj.add(itemName, v);
map.put(v, lexer.location.copy());
break;
case Null:
JsonNull n = new JsonNull();
obj.add(itemName, n);
map.put(n, lexer.location.copy());
break;
case Array:
JsonArray arr = new JsonArray(); // (obj.path+'.'+ItemName);
loc = lexer.location.copy();
obj.add(itemName, arr);
next();
readArray(arr, false);
map.put(arr, loc);
break;
case Eof :
throw lexer.error("Unexpected End of File");
}
next();
}
}
private void readArray(JsonArray arr, boolean root) throws FHIRException {
while (!((itemType == ItemType.End) || (root && (itemType == ItemType.Eof)))) {
switch (itemType) {
case Object:
JsonObject obj = new JsonObject(); // (arr.path+'['+inttostr(i)+']');
LocationData loc = lexer.location.copy();
arr.add(obj);
next();
readObject(obj, false);
map.put(obj, loc);
break;
case String:
JsonPrimitive v = new JsonPrimitive(itemValue);
arr.add(v);
map.put(v, lexer.location.copy());
break;
case Number:
v = new JsonPrimitive(new BigDecimal(itemValue));
arr.add(v);
map.put(v, lexer.location.copy());
break;
case Null :
JsonNull n = new JsonNull();
arr.add(n);
map.put(n, lexer.location.copy());
break;
case Array:
JsonArray child = new JsonArray(); // (arr.path+'['+inttostr(i)+']');
loc = lexer.location.copy();
arr.add(child);
next();
readArray(child, false);
map.put(arr, loc);
break;
case Eof :
throw lexer.error("Unexpected End of File");
}
next();
}
}
private void next() throws FHIRException {
switch (itemType) {
case Object :
lexer.consume(TokenType.Open);
lexer.states.push(new State(itemName, false));
if (lexer.getType() == TokenType.Close) {
itemType = ItemType.End;
lexer.next();
} else
parseProperty();
break;
case Null:
case String:
case Number:
case End:
case Boolean :
if (itemType == ItemType.End)
lexer.states.pop();
if (lexer.getType() == TokenType.Comma) {
lexer.next();
parseProperty();
} else if (lexer.getType() == TokenType.Close) {
itemType = ItemType.End;
lexer.next();
} else if (lexer.getType() == TokenType.CloseArray) {
itemType = ItemType.End;
lexer.next();
} else if (lexer.getType() == TokenType.Eof) {
itemType = ItemType.Eof;
} else
throw lexer.error("Unexpected JSON syntax");
break;
case Array :
lexer.next();
lexer.states.push(new State(itemName+"[]", true));
parseProperty();
break;
case Eof :
throw lexer.error("JSON Syntax Error - attempt to read past end of json stream");
default:
throw lexer.error("not done yet (a): "+itemType.toString());
}
}
private void parseProperty() throws FHIRException {
if (!lexer.states.peek().isProp) {
itemName = lexer.consume(TokenType.String);
itemValue = null;
lexer.consume(TokenType.Colon);
}
switch (lexer.getType()) {
case Null :
itemType = ItemType.Null;
itemValue = lexer.value;
lexer.next();
break;
case String :
itemType = ItemType.String;
itemValue = lexer.value;
lexer.next();
break;
case Boolean :
itemType = ItemType.Boolean;
itemValue = lexer.value;
lexer.next();
break;
case Number :
itemType = ItemType.Number;
itemValue = lexer.value;
lexer.next();
break;
case Open :
itemType = ItemType.Object;
break;
case OpenArray :
itemType = ItemType.Array;
break;
case CloseArray :
itemType = ItemType.End;
break;
// case Close, , case Colon, case Comma, case OpenArray, !
default:
throw lexer.error("not done yet (b): "+lexer.getType().toString());
}
}
}

View File

@ -0,0 +1,492 @@
package org.hl7.fhir.dstu2016may.utils;
import java.net.URISyntaxException;
/*
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 java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu2016may.model.BooleanType;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.CodeType;
import org.hl7.fhir.dstu2016may.model.CodeableConcept;
import org.hl7.fhir.dstu2016may.model.Coding;
import org.hl7.fhir.dstu2016may.model.DataElement;
import org.hl7.fhir.dstu2016may.model.DomainResource;
import org.hl7.fhir.dstu2016may.model.Element;
import org.hl7.fhir.dstu2016may.model.ElementDefinition;
import org.hl7.fhir.dstu2016may.model.Extension;
import org.hl7.fhir.dstu2016may.model.ExtensionHelper;
import org.hl7.fhir.dstu2016may.model.Factory;
import org.hl7.fhir.dstu2016may.model.Identifier;
import org.hl7.fhir.dstu2016may.model.IntegerType;
import org.hl7.fhir.dstu2016may.model.MarkdownType;
import org.hl7.fhir.dstu2016may.model.PrimitiveType;
import org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.dstu2016may.model.StringType;
import org.hl7.fhir.dstu2016may.model.Type;
import org.hl7.fhir.dstu2016may.model.UriType;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.validation.ValidationMessage.Source;
import org.hl7.fhir.exceptions.FHIRFormatError;
public class ToolingExtensions {
// validated
public static final String EXT_SUBSUMES = "http://hl7.org/fhir/StructureDefinition/codesystem-subsumes";
private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid";
// public static final String EXT_DEPRECATED = "http://hl7.org/fhir/StructureDefinition/codesystem-deprecated";
public static final String EXT_DEFINITION = "http://hl7.org/fhir/StructureDefinition/valueset-definition";
public static final String EXT_COMMENT = "http://hl7.org/fhir/StructureDefinition/valueset-comments";
private static final String EXT_IDENTIFIER = "http://hl7.org/fhir/StructureDefinition/identifier";
private static final String EXT_TRANSLATION = "http://hl7.org/fhir/StructureDefinition/translation";
public static final String EXT_ISSUE_SOURCE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source";
public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint";
public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby";
public static final String EXT_JSON_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type";
public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type";
public static final String EXT_REGEX = "http://hl7.org/fhir/StructureDefinition/structuredefinition-regex";
public static final String EXT_CONTROL = "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl";
public static final String EXT_MINOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs";
public static final String EXT_MAXOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs";
public static final String EXT_ALLOWEDRESOURCE = "http://hl7.org/fhir/StructureDefinition/questionnaire-allowedResource";
public static final String EXT_REFERENCEFILTER = "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceFilter";
// unregistered?
// public static final String EXT_FLYOVER = "http://hl7.org/fhir/Profile/questionnaire-extensions#flyover";
// private static final String EXT_QTYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
// private static final String EXT_QREF = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
// private static final String EXTENSION_FILTER_ONLY = "http://www.healthintersections.com.au/fhir/Profile/metadata#expandNeedsFilter";
// private static final String EXT_TYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
// private static final String EXT_REFERENCE = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
private static final String EXT_FHIRTYPE = "http://hl7.org/fhir/StructureDefinition/questionnaire-fhirType";
private static final String EXT_ALLOWABLE_UNITS = "http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits";
public static final String EXT_CIMI_REFERENCE = "http://hl7.org/fhir/StructureDefinition/cimi-reference";
public static final String EXT_UNCLOSED = "http://hl7.org/fhir/StructureDefinition/valueset-unclosed";
public static final String EXT_FMM_LEVEL = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm";
// specific extension helpers
public static Extension makeIssueSource(Source source) {
Extension ex = new Extension();
// todo: write this up and get it published with the pack (and handle the redirect?)
ex.setUrl(ToolingExtensions.EXT_ISSUE_SOURCE);
CodeType c = new CodeType();
c.setValue(source.toString());
ex.setValue(c);
return ex;
}
public static boolean hasExtension(DomainResource de, String url) {
return getExtension(de, url) != null;
}
public static boolean hasExtension(Element e, String url) {
return getExtension(e, url) != null;
}
public static void addStringExtension(DomainResource dr, String url, String content) {
if (!StringUtils.isBlank(content)) {
Extension ex = getExtension(dr, url);
if (ex != null)
ex.setValue(new StringType(content));
else
dr.getExtension().add(Factory.newExtension(url, new StringType(content), true));
}
}
public static void addMarkdownExtension(DomainResource dr, String url, String content) {
if (!StringUtils.isBlank(content)) {
Extension ex = getExtension(dr, url);
if (ex != null)
ex.setValue(new StringType(content));
else
dr.getExtension().add(Factory.newExtension(url, new MarkdownType(content), true));
}
}
public static void addStringExtension(Element e, String url, String content) {
if (!StringUtils.isBlank(content)) {
Extension ex = getExtension(e, url);
if (ex != null)
ex.setValue(new StringType(content));
else
e.getExtension().add(Factory.newExtension(url, new StringType(content), true));
}
}
public static void addIntegerExtension(DomainResource dr, String url, int value) {
Extension ex = getExtension(dr, url);
if (ex != null)
ex.setValue(new IntegerType(value));
else
dr.getExtension().add(Factory.newExtension(url, new IntegerType(value), true));
}
public static void addComment(Element nc, String comment) {
if (!StringUtils.isBlank(comment))
nc.getExtension().add(Factory.newExtension(EXT_COMMENT, Factory.newString_(comment), true));
}
// public static void markDeprecated(Element nc) {
// setDeprecated(nc);
// }
//
public static void addSubsumes(ConceptDefinitionComponent nc, String code) {
nc.getModifierExtension().add(Factory.newExtension(EXT_SUBSUMES, Factory.newCode(code), true));
}
public static void addDefinition(Element nc, String definition) {
if (!StringUtils.isBlank(definition))
nc.getExtension().add(Factory.newExtension(EXT_DEFINITION, Factory.newString_(definition), true));
}
public static void addDisplayHint(Element def, String hint) {
if (!StringUtils.isBlank(hint))
def.getExtension().add(Factory.newExtension(EXT_DISPLAY_HINT, Factory.newString_(hint), true));
}
public static String getDisplayHint(Element def) {
return readStringExtension(def, EXT_DISPLAY_HINT);
}
public static String readStringExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return null;
if (ex.getValue() instanceof UriType)
return ((UriType) ex.getValue()).getValue();
if (!(ex.getValue() instanceof StringType))
return null;
return ((StringType) ex.getValue()).getValue();
}
public static String readStringExtension(DomainResource c, String uri) {
Extension ex = getExtension(c, uri);
if (ex == null)
return null;
if ((ex.getValue() instanceof StringType))
return ((StringType) ex.getValue()).getValue();
if ((ex.getValue() instanceof UriType))
return ((UriType) ex.getValue()).getValue();
if ((ex.getValue() instanceof MarkdownType))
return ((MarkdownType) ex.getValue()).getValue();
return null;
}
@SuppressWarnings("unchecked")
public static PrimitiveType<Type> readPrimitiveExtension(DomainResource c, String uri) {
Extension ex = getExtension(c, uri);
if (ex == null)
return null;
return (PrimitiveType<Type>) ex.getValue();
}
public static boolean findStringExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return false;
if (!(ex.getValue() instanceof StringType))
return false;
return !StringUtils.isBlank(((StringType) ex.getValue()).getValue());
}
public static Boolean readBooleanExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return null;
if (!(ex.getValue() instanceof BooleanType))
return null;
return ((BooleanType) ex.getValue()).getValue();
}
public static boolean findBooleanExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return false;
if (!(ex.getValue() instanceof BooleanType))
return false;
return true;
}
public static String getComment(ConceptDefinitionComponent c) {
return readStringExtension(c, EXT_COMMENT);
}
//
// public static Boolean getDeprecated(Element c) {
// return readBooleanExtension(c, EXT_DEPRECATED);
// }
public static boolean hasComment(ConceptDefinitionComponent c) {
return findStringExtension(c, EXT_COMMENT);
}
// public static boolean hasDeprecated(Element c) {
// return findBooleanExtension(c, EXT_DEPRECATED);
// }
public static List<CodeType> getSubsumes(ConceptDefinitionComponent c) {
List<CodeType> res = new ArrayList<CodeType>();
for (Extension e : c.getExtension()) {
if (EXT_SUBSUMES.equals(e.getUrl()))
res.add((CodeType) e.getValue());
}
return res;
}
public static void addFlyOver(QuestionnaireItemComponent item, String text){
if (!StringUtils.isBlank(text)) {
QuestionnaireItemComponent display = item.addItem();
display.setType(QuestionnaireItemType.DISPLAY);
display.setText(text);
display.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept("flyover", "http://hl7.org/fhir/questionnaire-item-control", "Fly-over"), true));
}
}
public static void addMin(QuestionnaireItemComponent item, int min) {
item.getExtension().add(Factory.newExtension(EXT_MINOCCURS, Factory.newInteger(min), true));
}
public static void addMax(QuestionnaireItemComponent item, int max) {
item.getExtension().add(Factory.newExtension(EXT_MAXOCCURS, Factory.newInteger(max), true));
}
public static void addFhirType(QuestionnaireItemComponent group, String value) {
group.getExtension().add(Factory.newExtension(EXT_FHIRTYPE, Factory.newString_(value), true));
}
public static void addControl(QuestionnaireItemComponent group, String value) {
group.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept(value, "http://hl7.org/fhir/questionnaire-item-control", value), true));
}
public static void addAllowedResource(QuestionnaireItemComponent group, String value) {
group.getExtension().add(Factory.newExtension(EXT_ALLOWEDRESOURCE, Factory.newCode(value), true));
}
public static void addReferenceFilter(QuestionnaireItemComponent group, String value) {
group.getExtension().add(Factory.newExtension(EXT_REFERENCEFILTER, Factory.newString_(value), true));
}
public static void addIdentifier(Element element, Identifier value) {
element.getExtension().add(Factory.newExtension(EXT_IDENTIFIER, value, true));
}
/**
* @param name the identity of the extension of interest
* @return The extension, if on this element, else null
*/
public static Extension getExtension(DomainResource resource, String name) {
if (name == null)
return null;
if (!resource.hasExtension())
return null;
for (Extension e : resource.getExtension()) {
if (name.equals(e.getUrl()))
return e;
}
return null;
}
public static Extension getExtension(Element el, String name) {
if (name == null)
return null;
if (!el.hasExtension())
return null;
for (Extension e : el.getExtension()) {
if (name.equals(e.getUrl()))
return e;
}
return null;
}
public static void setStringExtension(DomainResource resource, String uri, String value) {
Extension ext = getExtension(resource, uri);
if (ext != null)
ext.setValue(new StringType(value));
else
resource.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value)));
}
public static String getOID(CodeSystem define) {
return readStringExtension(define, EXT_OID);
}
public static String getOID(ValueSet vs) {
return readStringExtension(vs, EXT_OID);
}
public static void setOID(CodeSystem define, String oid) throws FHIRFormatError, URISyntaxException {
if (!oid.startsWith("urn:oid:"))
throw new FHIRFormatError("Error in OID format");
if (oid.startsWith("urn:oid:urn:oid:"))
throw new FHIRFormatError("Error in OID format");
if (!hasExtension(define, EXT_OID))
define.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));
else if (!oid.equals(readStringExtension(define, EXT_OID)))
throw new Error("Attempt to assign multiple OIDs to a code system");
}
public static void setOID(ValueSet vs, String oid) throws FHIRFormatError, URISyntaxException {
if (!oid.startsWith("urn:oid:"))
throw new FHIRFormatError("Error in OID format");
if (oid.startsWith("urn:oid:urn:oid:"))
throw new FHIRFormatError("Error in OID format");
if (!hasExtension(vs, EXT_OID))
vs.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));
else if (!oid.equals(readStringExtension(vs, EXT_OID)))
throw new Error("Attempt to assign multiple OIDs to value set "+vs.getName()+" ("+vs.getUrl()+"). Has "+readStringExtension(vs, EXT_OID)+", trying to add "+oid);
}
public static boolean hasLanguageTranslation(Element element, String lang) {
for (Extension e : element.getExtension()) {
if (e.getUrl().equals(EXT_TRANSLATION)) {
Extension e1 = ExtensionHelper.getExtension(e, "lang");
if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang))
return true;
}
}
return false;
}
public static String getLanguageTranslation(Element element, String lang) {
for (Extension e : element.getExtension()) {
if (e.getUrl().equals(EXT_TRANSLATION)) {
Extension e1 = ExtensionHelper.getExtension(e, "lang");
if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang)) {
e1 = ExtensionHelper.getExtension(e, "content");
return ((StringType) e.getValue()).getValue();
}
}
}
return null;
}
public static void addLanguageTranslation(Element element, String lang, String value) {
Extension extension = new Extension().setUrl(EXT_TRANSLATION);
extension.addExtension().setUrl("lang").setValue(new StringType(lang));
extension.addExtension().setUrl("content").setValue(new StringType(value));
element.getExtension().add(extension);
}
public static Type getAllowedUnits(ElementDefinition eld) {
for (Extension e : eld.getExtension())
if (e.getUrl().equals(EXT_ALLOWABLE_UNITS))
return e.getValue();
return null;
}
public static void setAllowableUnits(ElementDefinition eld, CodeableConcept cc) {
for (Extension e : eld.getExtension())
if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) {
e.setValue(cc);
return;
}
eld.getExtension().add(new Extension().setUrl(EXT_ALLOWABLE_UNITS).setValue(cc));
}
public static List<Extension> getExtensions(Element element, String url) {
List<Extension> results = new ArrayList<Extension>();
for (Extension ex : element.getExtension())
if (ex.getUrl().equals(url))
results.add(ex);
return results;
}
public static List<Extension> getExtensions(DomainResource resource, String url) {
List<Extension> results = new ArrayList<Extension>();
for (Extension ex : resource.getExtension())
if (ex.getUrl().equals(url))
results.add(ex);
return results;
}
public static void addDEReference(DataElement de, String value) {
for (Extension e : de.getExtension())
if (e.getUrl().equals(EXT_CIMI_REFERENCE)) {
e.setValue(new UriType(value));
return;
}
de.getExtension().add(new Extension().setUrl(EXT_CIMI_REFERENCE).setValue(new UriType(value)));
}
// public static void setDeprecated(Element nc) {
// for (Extension e : nc.getExtension())
// if (e.getUrl().equals(EXT_DEPRECATED)) {
// e.setValue(new BooleanType(true));
// return;
// }
// nc.getExtension().add(new Extension().setUrl(EXT_DEPRECATED).setValue(new BooleanType(true)));
// }
public static void setExtension(Element focus, String url, Coding c) {
for (Extension e : focus.getExtension())
if (e.getUrl().equals(url)) {
e.setValue(c);
return;
}
focus.getExtension().add(new Extension().setUrl(url).setValue(c));
}
public static void removeExtension(DomainResource focus, String url) {
Iterator<Extension> i = focus.getExtension().iterator();
while (i.hasNext()) {
Extension e = i.next(); // must be called before you can call i.remove()
if (e.getUrl().equals(url)) {
i.remove();
}
}
}
public static void removeExtension(Element focus, String url) {
Iterator<Extension> i = focus.getExtension().iterator();
while (i.hasNext()) {
Extension e = i.next(); // must be called before you can call i.remove()
if (e.getUrl().equals(url)) {
i.remove();
}
}
}
public static boolean hasOID(ValueSet vs) {
return hasExtension(vs, EXT_OID);
}
}

View File

@ -0,0 +1,102 @@
package org.hl7.fhir.dstu2016may.utils;
import java.util.Stack;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.UserDataHandler;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.events.MutationEvent;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.LocatorImpl;
import org.xml.sax.helpers.XMLFilterImpl;
// http://javacoalface.blogspot.com.au/2011/04/line-and-column-numbers-in-xml-dom.html
public class XmlLocationAnnotator extends XMLFilterImpl {
private Locator locator;
private Stack<Locator> locatorStack = new Stack<Locator>();
private Stack<Element> elementStack = new Stack<Element>();
private UserDataHandler dataHandler = new LocationDataHandler();
public XmlLocationAnnotator(XMLReader xmlReader, Document dom) {
super(xmlReader);
// Add listener to DOM, so we know which node was added.
EventListener modListener = new EventListener() {
@Override
public void handleEvent(Event e) {
EventTarget target = ((MutationEvent) e).getTarget();
elementStack.push((Element) target);
}
};
((EventTarget) dom).addEventListener("DOMNodeInserted", modListener, true);
}
@Override
public void setDocumentLocator(Locator locator) {
super.setDocumentLocator(locator);
this.locator = locator;
}
@Override
public void startElement(String uri, String localName,
String qName, Attributes atts) throws SAXException {
super.startElement(uri, localName, qName, atts);
// Keep snapshot of start location,
// for later when end of element is found.
locatorStack.push(new LocatorImpl(locator));
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// Mutation event fired by the adding of element end,
// and so lastAddedElement will be set.
super.endElement(uri, localName, qName);
if (locatorStack.size() > 0) {
Locator startLocator = locatorStack.pop();
XmlLocationData location = new XmlLocationData(
startLocator.getSystemId(),
startLocator.getLineNumber(),
startLocator.getColumnNumber(),
locator.getLineNumber(),
locator.getColumnNumber());
Element lastAddedElement = elementStack.pop();
lastAddedElement.setUserData(
XmlLocationData.LOCATION_DATA_KEY, location,
dataHandler);
}
}
// Ensure location data copied to any new DOM node.
private class LocationDataHandler implements UserDataHandler {
@Override
public void handle(short operation, String key, Object data,
Node src, Node dst) {
if (src != null && dst != null) {
XmlLocationData locatonData = (XmlLocationData)
src.getUserData(XmlLocationData.LOCATION_DATA_KEY);
if (locatonData != null) {
dst.setUserData(XmlLocationData.LOCATION_DATA_KEY,
locatonData, dataHandler);
}
}
}
}
}

View File

@ -0,0 +1,49 @@
package org.hl7.fhir.dstu2016may.utils;
public class XmlLocationData {
public static final String LOCATION_DATA_KEY = "locationDataKey";
private final String systemId;
private final int startLine;
private final int startColumn;
private final int endLine;
private final int endColumn;
public XmlLocationData(String systemId, int startLine,
int startColumn, int endLine, int endColumn) {
super();
this.systemId = systemId;
this.startLine = startLine;
this.startColumn = startColumn;
this.endLine = endLine;
this.endColumn = endColumn;
}
public String getSystemId() {
return systemId;
}
public int getStartLine() {
return startLine;
}
public int getStartColumn() {
return startColumn;
}
public int getEndLine() {
return endLine;
}
public int getEndColumn() {
return endColumn;
}
@Override
public String toString() {
return getSystemId() + "[line " + startLine + ":"
+ startColumn + " to line " + endLine + ":"
+ endColumn + "]";
}
}

View File

@ -0,0 +1,436 @@
package org.hl7.fhir.dstu2016may.validation;
/*
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 java.text.MessageFormat;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType;
import org.hl7.fhir.dstu2016may.validation.ValidationMessage.Source;
public class BaseValidator {
protected Source source;
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean fail(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.FATAL));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
if (!thePass) {
String path = toPath(pathParts);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String path = toPath(pathParts);
errors.add(new ValidationMessage(source, type, -1, -1, path, formatMessage(theMessage, theMessageArguments), IssueSeverity.FATAL));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean fail(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL));
}
return thePass;
}
private String formatMessage(String theMessage, Object... theMessageArguments) {
String message;
if (theMessageArguments != null && theMessageArguments.length > 0) {
message = MessageFormat.format(theMessage, theMessageArguments);
} else {
message = theMessage;
}
return message;
}
protected boolean grammarWord(String w) {
return w.equals("and") || w.equals("or") || w.equals("a") || w.equals("the") || w.equals("for") || w.equals("this") || w.equals("that") || w.equals("of");
}
/**
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean rule(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.ERROR));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
if (!thePass) {
String path = toPath(pathParts);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.ERROR));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR));
}
return thePass;
}
static public boolean rule(List<ValidationMessage> errors, Source source, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.ERROR));
}
return thePass;
}
protected String splitByCamelCase(String s) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isUpperCase(c) && !(i == 0 || Character.isUpperCase(s.charAt(i-1))))
b.append(' ');
b.append(c);
}
return b.toString();
}
protected String stripPunctuation(String s, boolean numbers) {
StringBuilder b = new StringBuilder();
for (char c : s.toCharArray()) {
int t = Character.getType(c);
if (t == Character.UPPERCASE_LETTER || t == Character.LOWERCASE_LETTER || t == Character.TITLECASE_LETTER || t == Character.MODIFIER_LETTER || t == Character.OTHER_LETTER || (t == Character.DECIMAL_DIGIT_NUMBER && numbers) || (t == Character.LETTER_NUMBER && numbers) || c == ' ')
b.append(c);
}
return b.toString();
}
private String toPath(List<String> pathParts) {
if (pathParts == null || pathParts.isEmpty()) {
return "";
}
return "//" + StringUtils.join(pathParts, '/');
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) {
msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.WARNING));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.WARNING));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.WARNING));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass) {
msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING));
}
return thePass;
}
//---------
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) {
msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
String path = toPath(pathParts);
String message = formatMessage(theMessage, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) {
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION));
}
return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass) {
msg = formatMessage(msg, theMessageArguments);
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION));
}
return thePass;
}
}

View File

@ -0,0 +1,94 @@
package org.hl7.fhir.dstu2016may.validation;
import java.io.InputStream;
import java.util.List;
import org.hl7.fhir.dstu2016may.metamodel.Manager.FhirFormat;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import com.google.gson.JsonObject;
/**
* Interface to the instance validator. This takes a resource, in one of many forms, and
* checks whether it is valid
*
* @author Grahame Grieve
*
*/
public interface IResourceValidator {
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}
enum IdStatus {
OPTIONAL, REQUIRED, PROHIBITED
}
/**
* how much to check displays for coded elements
* @return
*/
CheckDisplayOption getCheckDisplay();
void setCheckDisplay(CheckDisplayOption checkDisplay);
/**
* whether the resource must have an id or not (depends on context)
*
* @return
*/
IdStatus getResourceIdRule();
void setResourceIdRule(IdStatus resourceIdRule);
/**
* whether the validator should enforce best practice guidelines
* as defined by various HL7 committees
*
*/
BestPracticeWarningLevel getBasePracticeWarningLevel();
void setBestPracticeWarningLevel(BestPracticeWarningLevel value);
/**
* Validate suite
*
* you can validate one of the following representations of resources:
*
* stream - provide a format
* a native resource
* a metamodel resource
* a DOM element or Document
* a Json Object
*
*/
void validate(List<ValidationMessage> errors, InputStream stream, FhirFormat format) throws Exception;
void validate(List<ValidationMessage> errors, InputStream stream, FhirFormat format, String profile) throws Exception;
void validate(List<ValidationMessage> errors, InputStream stream, FhirFormat format, StructureDefinition profile) throws Exception;
void validate(List<ValidationMessage> errors, org.hl7.fhir.dstu2016may.model.Resource resource) throws Exception;
void validate(List<ValidationMessage> errors, org.hl7.fhir.dstu2016may.model.Resource resource, String profile) throws Exception;
void validate(List<ValidationMessage> errors, org.hl7.fhir.dstu2016may.model.Resource resource, StructureDefinition profile) throws Exception;
void validate(List<ValidationMessage> errors, org.hl7.fhir.dstu2016may.metamodel.Element element) throws Exception;
void validate(List<ValidationMessage> errors, org.hl7.fhir.dstu2016may.metamodel.Element element, String profile) throws Exception;
void validate(List<ValidationMessage> errors, org.hl7.fhir.dstu2016may.metamodel.Element element, StructureDefinition profile) throws Exception;
void validate(List<ValidationMessage> errors, org.w3c.dom.Element element) throws Exception;
void validate(List<ValidationMessage> errors, org.w3c.dom.Element element, String profile) throws Exception;
void validate(List<ValidationMessage> errors, org.w3c.dom.Element element, StructureDefinition profile) throws Exception;
void validate(List<ValidationMessage> errors, org.w3c.dom.Document document) throws Exception;
void validate(List<ValidationMessage> errors, org.w3c.dom.Document document, String profile) throws Exception;
void validate(List<ValidationMessage> errors, org.w3c.dom.Document document, StructureDefinition profile) throws Exception;
void validate(List<ValidationMessage> errors, JsonObject object) throws Exception;
void validate(List<ValidationMessage> errors, JsonObject object, String profile) throws Exception;
void validate(List<ValidationMessage> errors, JsonObject object, StructureDefinition profile) throws Exception;
}

View File

@ -0,0 +1,289 @@
package org.hl7.fhir.dstu2016may.validation;
/*
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.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.dstu2016may.model.OperationOutcome;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.dstu2016may.model.StringType;
import org.hl7.fhir.dstu2016may.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
public class ValidationMessage
{
public enum Source {
ExampleValidator,
ProfileValidator,
ResourceValidator,
InstanceValidator,
Schema,
Schematron,
Publisher,
Ontology,
ProfileComparer,
QuestionnaireResponseValidator
}
private Source source;
private int line;
private int col;
private String location;
private String message;
private OperationOutcome.IssueType type;
private IssueSeverity level;
private String html;
/**
* Constructor
*/
public ValidationMessage() {
// nothing
}
public ValidationMessage(Source source, OperationOutcome.IssueType type, String path, String message, IssueSeverity level) {
super();
this.line = -1;
this.col = -1;
this.location = path;
this.message = message;
this.html = Utilities.escapeXml(message);
this.level = level;
this.source = source;
this.type = type;
if (level == IssueSeverity.NULL)
determineLevel(path);
if (type == null)
throw new Error("A type must be provided");
}
public ValidationMessage(Source source, OperationOutcome.IssueType type, int line, int col, String path, String message, IssueSeverity level) {
super();
this.line = line;
this.col = col;
this.location = path;
this.message = message;
this.html = Utilities.escapeXml(message);
this.level = level;
this.source = source;
this.type = type;
if (level == IssueSeverity.NULL)
determineLevel(path);
if (type == null)
throw new Error("A type must be provided");
}
public ValidationMessage(Source source, OperationOutcome.IssueType type, String path, String message, String html, IssueSeverity level) {
super();
this.line = -1;
this.col = -1;
this.location = path;
this.message = message;
this.html = html;
this.level = level;
this.source = source;
this.type = type;
if (level == IssueSeverity.NULL)
determineLevel(path);
if (type == null)
throw new Error("A type must be provided");
}
public ValidationMessage(Source source, OperationOutcome.IssueType type, int line, int col, String path, String message, String html, IssueSeverity level) {
super();
this.line = line;
this.col = col;
this.location = path;
this.message = message;
this.html = html;
this.level = level;
this.source = source;
this.type = type;
if (level == IssueSeverity.NULL)
determineLevel(path);
if (type == null)
throw new Error("A type must be provided");
}
public ValidationMessage(Source source, OperationOutcome.IssueType type, String message, IssueSeverity level) {
super();
this.line = -1;
this.col = -1;
this.message = message;
this.level = level;
this.source = source;
this.type = type;
if (type == null)
throw new Error("A type must be provided");
}
private IssueSeverity determineLevel(String path) {
if (isGrandfathered(path))
return IssueSeverity.WARNING;
else
return IssueSeverity.ERROR;
}
private boolean isGrandfathered(String path) {
if (path.startsWith("xds-documentmanifest."))
return true;
if (path.startsWith("observation-device-metric-devicemetricobservation."))
return true;
if (path.startsWith("medicationadministration-immunization-vaccine."))
return true;
if (path.startsWith("elementdefinition-de-dataelement."))
return true;
if (path.startsWith("dataelement-sdc-sdcelement."))
return true;
if (path.startsWith("questionnaireresponse-sdc-structureddatacaptureanswers."))
return true;
if (path.startsWith("valueset-sdc-structureddatacapturevalueset."))
return true;
if (path.startsWith("dataelement-sdc-de-sdcelement."))
return true;
if (path.startsWith("do-uslab-uslabdo."))
return true;
if (path.startsWith("."))
return true;
if (path.startsWith("."))
return true;
if (path.startsWith("."))
return true;
if (path.startsWith("."))
return true;
return false;
}
public String getMessage() {
return message;
}
public ValidationMessage setMessage(String message) {
this.message = message;
return this;
}
public IssueSeverity getLevel() {
return level;
}
public ValidationMessage setLevel(IssueSeverity level) {
this.level = level;
return this;
}
public Source getSource() {
return source;
}
public ValidationMessage setSource(Source source) {
this.source = source;
return this;
}
public int getLine() {
return line;
}
public void setLine(int theLine) {
line = theLine;
}
public int getCol() {
return col;
}
public void setCol(int theCol) {
col = theCol;
}
public String getLocation() {
return location;
}
public ValidationMessage setLocation(String location) {
this.location = location;
return this;
}
public OperationOutcome.IssueType getType() {
return type;
}
public ValidationMessage setType(OperationOutcome.IssueType type) {
this.type = type;
return this;
}
public String summary() {
return level.toString()+" @ "+location+(line>= 0 && col >= 0 ? " (line "+Integer.toString(line)+", col"+Integer.toString(col)+") " : " ") +message +(source != null ? " (src = "+source+")" : "");
}
public OperationOutcomeIssueComponent asIssue(OperationOutcome op) {
OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent();
issue.setCode(type);
if (location != null) {
StringType s = new StringType();
s.setValue(location+(line>= 0 && col >= 0 ? " (line "+Integer.toString(line)+", col"+Integer.toString(col)+")" : "") );
issue.getLocation().add(s);
}
issue.setSeverity(level);
issue.getDetails().setText(message);
if (source != null) {
issue.getExtension().add(ToolingExtensions.makeIssueSource(source));
}
return issue;
}
public String toXML() {
return "<message source=\"" + source + "\" line=\"" + line + "\" col=\"" + col + "\" location=\"" + Utilities.escapeXml(location) + "\" type=\"" + type + "\" level=\"" + level + "\"><plain>" + Utilities.escapeXml(message) + "</plain><html>" + html + "</html></message>";
}
public String getHtml() {
return html == null ? Utilities.escapeXml(message) : html;
}
/**
* Returns a representation of this ValidationMessage suitable for logging. The values of
* most of the internal fields are included, so this may not be suitable for display to
* an end user.
*/
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("level", level);
b.append("type", type);
b.append("location", location);
b.append("message", message);
return b.build();
}
}

View File

@ -17,11 +17,11 @@ import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
public class IdTypeDstu21Test {
public class IdTypeDstu2_1Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeDstu21Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeDstu2_1Test.class);
@AfterClass
public static void afterClassClearContext() {

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.TestUtil;
public class ModelDstu21Test {
public class ModelDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();
@ -75,7 +75,7 @@ public class ModelDstu21Test {
}
// private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelDstu21Test.class);
// private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelDstu2_1Test.class);
/**

View File

@ -18,7 +18,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.util.TestUtil;
public class ModelSerializationDstu21Test {
public class ModelSerializationDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();

View File

@ -34,10 +34,10 @@ import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.TestUtil;
public class CustomTypeDstu21Test {
public class CustomTypeDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu21Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu2_1Test.class);
@Before
public void before() {

View File

@ -95,7 +95,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
import ca.uhn.fhir.parser.PatientWithExtendedContactDstu3.CustomContactComponent;
import ca.uhn.fhir.parser.XmlParserDstu3Test.TestPatientFor327;
import ca.uhn.fhir.parser.XmlParserDstu2_1Test.TestPatientFor327;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
@ -105,9 +105,9 @@ import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import net.sf.json.JsonConfig;
public class JsonParserDstu21Test {
public class JsonParserDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu21Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu2_1Test.class);
@After
public void after() {
@ -1235,7 +1235,7 @@ public class JsonParserDstu21Test {
@Test
@Ignore
public void testParseAndEncodeBundle() throws Exception {
String content = IOUtils.toString(JsonParserDstu21Test.class.getResourceAsStream("/bundle-example.json"), StandardCharsets.UTF_8);
String content = IOUtils.toString(JsonParserDstu2_1Test.class.getResourceAsStream("/bundle-example.json"), StandardCharsets.UTF_8);
Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, content);
assertEquals("Bundle/example/_history/1", parsed.getIdElement().getValue());
@ -1284,7 +1284,7 @@ public class JsonParserDstu21Test {
@Test
@Ignore
public void testParseAndEncodeBundleFromXmlToJson() throws Exception {
String content = IOUtils.toString(JsonParserDstu21Test.class.getResourceAsStream("/bundle-example2.xml"), StandardCharsets.UTF_8);
String content = IOUtils.toString(JsonParserDstu2_1Test.class.getResourceAsStream("/bundle-example2.xml"), StandardCharsets.UTF_8);
Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, content);
@ -1309,7 +1309,7 @@ public class JsonParserDstu21Test {
@Test
@Ignore
public void testParseAndEncodeBundleNewStyle() throws Exception {
String content = IOUtils.toString(JsonParserDstu21Test.class.getResourceAsStream("/bundle-example.json"), StandardCharsets.UTF_8);
String content = IOUtils.toString(JsonParserDstu2_1Test.class.getResourceAsStream("/bundle-example.json"), StandardCharsets.UTF_8);
Bundle parsed = ourCtx.newJsonParser().parseResource(Bundle.class, content);
assertEquals("Bundle/example/_history/1", parsed.getIdElement().getValue());
@ -1847,7 +1847,7 @@ public class JsonParserDstu21Test {
@Test
public void testReportSerialize() {
ReportObservationDstu3 obsv = new ReportObservationDstu3();
ReportObservationDstu2_1 obsv = new ReportObservationDstu2_1();
obsv.getCode().addCoding().setCode("name");
obsv.setValue(new StringType("value test"));
obsv.setStatus(ObservationStatus.FINAL);
@ -1869,7 +1869,7 @@ public class JsonParserDstu21Test {
@Test
public void testReportSerializeWithMatchingId() {
ReportObservationDstu3 obsv = new ReportObservationDstu3();
ReportObservationDstu2_1 obsv = new ReportObservationDstu2_1();
obsv.getCode().addCoding().setCode("name");
obsv.setValue(new StringType("value test"));
obsv.setStatus(ObservationStatus.FINAL);

View File

@ -11,7 +11,7 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name = "Observation", id = "reportobservation")
public class ReportObservationDstu3 extends Observation {
public class ReportObservationDstu2_1 extends Observation {
/**
* Each extension is defined in a field. Any valid HAPI Data Type can be used for the field type. Note that the
@ -62,7 +62,7 @@ public class ReportObservationDstu3 extends Observation {
}
/** Setter for mandatory */
public ReportObservationDstu3 setMandatory(Boolean isMandatory) {
public ReportObservationDstu2_1 setMandatory(Boolean isMandatory) {
mandatory = new BooleanType(isMandatory);
return this;
}
@ -76,7 +76,7 @@ public class ReportObservationDstu3 extends Observation {
}
/** Setter for readOnly */
public ReportObservationDstu3 setReadOnly(Boolean isReadOnly) {
public ReportObservationDstu2_1 setReadOnly(Boolean isReadOnly) {
readOnly = new BooleanType(isReadOnly);
return this;
}
@ -90,7 +90,7 @@ public class ReportObservationDstu3 extends Observation {
}
/** Setter for defaultCursor */
public ReportObservationDstu3 setDefaultCursor(Boolean isDefaultCursor) {
public ReportObservationDstu2_1 setDefaultCursor(Boolean isDefaultCursor) {
defaultCursor = new BooleanType(isDefaultCursor);
return this;
}
@ -104,7 +104,7 @@ public class ReportObservationDstu3 extends Observation {
}
/** Setter for sectionContentId */
public ReportObservationDstu3 setSectionContentId(String sectionContentId) {
public ReportObservationDstu2_1 setSectionContentId(String sectionContentId) {
this.sectionContentId = new StringType(sectionContentId);
return this;
}

View File

@ -24,8 +24,8 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.XmlUtil;
public class RoundTripDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RoundTripDstu3Test.class);
public class RoundTripDstu2_1Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RoundTripDstu2_1Test.class);
private static FhirContext ourCtx = FhirContext.forDstu2_1();
@Test

View File

@ -117,9 +117,9 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
import net.sf.saxon.style.DataElement;
public class XmlParserDstu3Test {
public class XmlParserDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserDstu2_1Test.class);
@After
public void after() {
@ -1802,7 +1802,7 @@ public class XmlParserDstu3Test {
@Test
@Ignore
public void testParseAndEncodeBundle() throws Exception {
String content = IOUtils.toString(XmlParserDstu3Test.class.getResourceAsStream("/bundle-example.xml"), StandardCharsets.UTF_8);
String content = IOUtils.toString(XmlParserDstu2_1Test.class.getResourceAsStream("/bundle-example.xml"), StandardCharsets.UTF_8);
Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, content);
assertEquals("Bundle/example/_history/1", parsed.getIdElement().getValue());
@ -1838,7 +1838,7 @@ public class XmlParserDstu3Test {
@Test
@Ignore
public void testParseAndEncodeBundleNewStyle() throws Exception {
String content = IOUtils.toString(XmlParserDstu3Test.class.getResourceAsStream("/bundle-example.xml"), StandardCharsets.UTF_8);
String content = IOUtils.toString(XmlParserDstu2_1Test.class.getResourceAsStream("/bundle-example.xml"), StandardCharsets.UTF_8);
IParser newXmlParser = ourCtx.newXmlParser();
Bundle parsed = newXmlParser.parseResource(Bundle.class, content);

View File

@ -31,9 +31,9 @@ import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
public class ClientWithCustomTypeDstu3Test {
public class ClientWithCustomTypeDstu2_1Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientWithCustomTypeDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientWithCustomTypeDstu2_1Test.class);
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;

View File

@ -66,8 +66,8 @@ import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.CustomTypeDstu21Test;
import ca.uhn.fhir.parser.CustomTypeDstu21Test.MyCustomPatient;
import ca.uhn.fhir.parser.CustomTypeDstu2_1Test;
import ca.uhn.fhir.parser.CustomTypeDstu2_1Test.MyCustomPatient;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
@ -84,9 +84,9 @@ import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.util.VersionUtil;
public class GenericClientDstu3Test {
public class GenericClientDstu2_1Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu2_1Test.class);
private int myAnswerCount;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@ -529,7 +529,7 @@ public class GenericClientDstu3Test {
@Test
public void testCookieInterceptor() throws Exception {
final String respString = CustomTypeDstu21Test.createBundle(CustomTypeDstu21Test.createResource(false));
final String respString = CustomTypeDstu2_1Test.createBundle(CustomTypeDstu2_1Test.createResource(false));
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
@ -651,7 +651,7 @@ public class GenericClientDstu3Test {
@Test
public void testExplicitCustomTypeHistoryType() throws Exception {
final String respString = CustomTypeDstu21Test.createBundle(CustomTypeDstu21Test.createResource(false));
final String respString = CustomTypeDstu2_1Test.createBundle(CustomTypeDstu2_1Test.createResource(false));
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
@ -668,19 +668,19 @@ public class GenericClientDstu3Test {
//@formatter:off
Bundle resp = client
.history()
.onType(CustomTypeDstu21Test.MyCustomPatient.class)
.onType(CustomTypeDstu2_1Test.MyCustomPatient.class)
.andReturnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu21Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals(CustomTypeDstu2_1Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient/_history", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeLoadPage() throws Exception {
final String respString = CustomTypeDstu21Test.createBundle(CustomTypeDstu21Test.createResource(false));
final String respString = CustomTypeDstu2_1Test.createBundle(CustomTypeDstu2_1Test.createResource(false));
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
@ -705,7 +705,7 @@ public class GenericClientDstu3Test {
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu21Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals(CustomTypeDstu2_1Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://foo/next", capt.getAllValues().get(0).getURI().toASCIIString());
//@formatter:off
@ -717,7 +717,7 @@ public class GenericClientDstu3Test {
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu21Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals(CustomTypeDstu2_1Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://foo/next", capt.getAllValues().get(0).getURI().toASCIIString());
}
@ -754,7 +754,7 @@ public class GenericClientDstu3Test {
//@formatter:on
assertEquals(1, resp.getParameter().size());
assertEquals(CustomTypeDstu21Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals(CustomTypeDstu2_1Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/$foo", capt.getAllValues().get(0).getURI().toASCIIString());
//@formatter:off
@ -767,13 +767,13 @@ public class GenericClientDstu3Test {
//@formatter:on
assertEquals(1, resp.getParameter().size());
assertEquals(CustomTypeDstu21Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals(CustomTypeDstu2_1Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient/$foo", capt.getAllValues().get(1).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeSearch() throws Exception {
final String respString = CustomTypeDstu21Test.createBundle(CustomTypeDstu21Test.createResource(false));
final String respString = CustomTypeDstu2_1Test.createBundle(CustomTypeDstu2_1Test.createResource(false));
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
@ -790,13 +790,13 @@ public class GenericClientDstu3Test {
//@formatter:off
Bundle resp = client
.search()
.forResource(CustomTypeDstu21Test.MyCustomPatient.class)
.forResource(CustomTypeDstu2_1Test.MyCustomPatient.class)
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu21Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals(CustomTypeDstu2_1Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
}
@ -1950,7 +1950,7 @@ public class GenericClientDstu3Test {
@Test
public void testUserInfoInterceptor() throws Exception {
final String respString = CustomTypeDstu21Test.createBundle(CustomTypeDstu21Test.createResource(false));
final String respString = CustomTypeDstu2_1Test.createBundle(CustomTypeDstu2_1Test.createResource(false));
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));

View File

@ -45,7 +45,7 @@ import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
public class NonGenericClientDstu3Test {
public class NonGenericClientDstu2_1Test {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;

View File

@ -42,9 +42,9 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
public class OperationClientDstu3Test {
public class OperationClientDstu2_1Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationClientDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationClientDstu2_1Test.class);
private FhirContext ourCtx;
private HttpClient ourHttpClient;

View File

@ -42,7 +42,7 @@ import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
public class PatchClientDstu3Test {
public class PatchClientDstu2_1Test {
public interface IClientType extends IRestfulClient {
@Patch(type=Patient.class)

View File

@ -48,9 +48,9 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
public class SearchClientDstu3Test {
public class SearchClientDstu2_1Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchClientDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchClientDstu2_1Test.class);
private FhirContext ourCtx;
private HttpClient ourHttpClient;
private HttpResponse ourHttpResponse;

View File

@ -33,7 +33,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class CreateBinaryDstu3Test {
public class CreateBinaryDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static Binary ourLastBinary;

View File

@ -46,11 +46,11 @@ import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class CreateDstu3Test {
public class CreateDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
public static IBaseOperationOutcome ourReturnOo;

View File

@ -39,7 +39,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class CustomTypeServerDstu3 {
public class CustomTypeServerDstu2_1 {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
@ -47,7 +47,7 @@ public class CustomTypeServerDstu3 {
private static IdType ourLastId;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasSearch;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu3.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu2_1.class);
private static int ourPort;
private static Server ourServer;

View File

@ -33,7 +33,7 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class DateRangeParamSearchDstu3Test {
public class DateRangeParamSearchDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();

View File

@ -29,14 +29,14 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class DeleteConditionalDstu3Test {
public class DeleteConditionalDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static IGenericClient ourHapiClient;
private static String ourLastConditionalUrl;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasDelete;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;

View File

@ -26,13 +26,13 @@ import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class FormatParameterDstu3Test {
public class FormatParameterDstu2_1Test {
private static final String VALUE_XML = "<Patient xmlns=\"http://hl7.org/fhir\"><id value=\"p1ReadId\"/><meta><profile value=\"http://foo_profile\"/></meta><identifier><value value=\"p1ReadValue\"/></identifier></Patient>";
private static final String VALUE_JSON = "{\"resourceType\":\"Patient\",\"id\":\"p1ReadId\",\"meta\":{\"profile\":[\"http://foo_profile\"]},\"identifier\":[{\"value\":\"p1ReadValue\"}]}";
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FormatParameterDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FormatParameterDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static RestfulServer ourServlet;

View File

@ -48,7 +48,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class InterceptorDstu3Test {
public class InterceptorDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();

View File

@ -40,14 +40,14 @@ import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.VersionUtil;
@SuppressWarnings("deprecation")
public class MetadataConformanceDstu3Test {
public class MetadataConformanceDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static int ourPort;
private static Server ourServer;
private static RestfulServer ourServlet;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu2_1Test.class);
@Test

View File

@ -55,7 +55,7 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class OperationServerDstu3Test {
public class OperationServerDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx;
@ -66,7 +66,7 @@ public class OperationServerDstu3Test {
private static List<StringType> ourLastParam3;
private static Money ourLastParamMoney1;
private static UnsignedIntType ourLastParamUnsignedInt1;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private IGenericClient myFhirClient;

View File

@ -53,14 +53,14 @@ import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
public class OperationServerWithSearchParamTypesDstu3Test {
public class OperationServerWithSearchParamTypesDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx;
private static String ourLastMethod;
private static List<StringOrListParam> ourLastParamValStr;
private static List<TokenOrListParam> ourLastParamValTok;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
@Before

View File

@ -33,11 +33,11 @@ import ca.uhn.fhir.rest.api.PatchTypeEnum;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class PatchDstu3Test {
public class PatchDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatchDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatchDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;

View File

@ -30,11 +30,11 @@ import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class ReadDstu3Test {
public class ReadDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;

View File

@ -33,11 +33,11 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchCountParamDstu3Test {
public class SearchCountParamDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;

View File

@ -32,11 +32,11 @@ import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchDstu3Test {
public class SearchDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;

View File

@ -32,11 +32,11 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchHasParamDstu3Test {
public class SearchHasParamDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;

View File

@ -43,7 +43,7 @@ import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchPostDstu3Test {
public class SearchPostDstu2_1Test {
public class ParamLoggingInterceptor extends InterceptorAdapter {
@ -58,7 +58,7 @@ public class SearchPostDstu3Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchPostDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchPostDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;

View File

@ -31,11 +31,11 @@ import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchSortDstu3Test {
public class SearchSortDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;

View File

@ -32,11 +32,11 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchWithGenericListDstu3Test {
public class SearchWithGenericListDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;

View File

@ -33,11 +33,11 @@ import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchWithIncludesDstu3Test {
public class SearchWithIncludesDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithIncludesDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithIncludesDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;

View File

@ -29,11 +29,11 @@ import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchWithServerAddressStrategyDstu3Test {
public class SearchWithServerAddressStrategyDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithServerAddressStrategyDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithServerAddressStrategyDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
private static RestfulServer ourServlet;

View File

@ -33,11 +33,11 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class ServerExceptionDstu3Test {
public class ServerExceptionDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerExceptionDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerExceptionDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
public static BaseServerResponseException ourException;

View File

@ -49,11 +49,11 @@ import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class ServerMimetypeDstu21Test {
public class ServerMimetypeDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeDstu21Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;

View File

@ -20,7 +20,7 @@ import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.util.TestUtil;
public class ServerUsingOldTypesDstu3Test {
public class ServerUsingOldTypesDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();
@AfterClass

View File

@ -31,11 +31,11 @@ import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class UnclassifiedServerExceptionDstu3Test {
public class UnclassifiedServerExceptionDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UnclassifiedServerExceptionDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UnclassifiedServerExceptionDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;
public static UnclassifiedServerFailureException ourException;

View File

@ -37,12 +37,12 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class UpdateDstu3Test {
public class UpdateDstu2_1Test {
private static CloseableHttpClient ourClient;
private static String ourConditionalUrl;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static IdType ourId;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu2_1Test.class);
private static int ourPort;
private static Server ourServer;

View File

@ -39,7 +39,7 @@ import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class ValidateDstu3Test {
public class ValidateDstu2_1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static EncodingEnum ourLastEncoding;
@ -48,7 +48,7 @@ public class ValidateDstu3Test {
public static Patient ourLastPatient;
private static String ourLastProfile;
private static String ourLastResourceBody;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDstu2_1Test.class);
private static OperationOutcome ourOutcomeToReturn;
private static int ourPort;

View File

@ -0,0 +1,73 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import org.hl7.fhir.dstu2016may.model.CodeableConcept;
import org.hl7.fhir.dstu2016may.model.Patient;
import org.hl7.fhir.dstu2016may.model.Reference;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name="Patient", profile = "http://hl7.org/fhir/StructureDefinition/Patient")
public class PatientProfileDstu2_1 extends Patient {
private static final long serialVersionUID = 1L;
@Child(name="owner", min=0, max=1)
@Extension(url="http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#owningOrganization", definedLocally=false, isModifier=false)
@Description(shortDefinition="The organization that owns this animal")
private Reference owningOrganization;
public Reference getOwningOrganization() {
if (owningOrganization == null) {
owningOrganization = new Reference();
}
return owningOrganization;
}
public PatientProfileDstu2_1 setOwningOrganization(Reference owningOrganization) {
this.owningOrganization = owningOrganization;
return this;
}
@Child(name="colorPrimary", min=0, max=1)
@Extension(url="http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#animal-colorPrimary", definedLocally=false, isModifier=false)
@Description(shortDefinition="The animals primary color")
private CodeableConcept colorPrimary;
@Child(name="colorSecondary", min=0, max=1)
@Extension(url="http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#animal-colorSecondary", definedLocally=false, isModifier=false)
@Description(shortDefinition="The animals secondary color")
private CodeableConcept colorSecondary;
public CodeableConcept getColorPrimary() {
if (this.colorPrimary == null) {
return new CodeableConcept();
}
return colorPrimary;
}
public void setColorPrimary(CodeableConcept colorPrimary) {
this.colorPrimary = colorPrimary;
}
public CodeableConcept getColorSecondary() {
if (this.colorSecondary == null) {
return new CodeableConcept();
}
return colorSecondary;
}
public void setColorSecondary(CodeableConcept colorSecondary) {
this.colorSecondary = colorSecondary;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(owningOrganization) && ElementUtil.isEmpty(colorPrimary)
&& ElementUtil.isEmpty(colorSecondary) ;
}
}

View File

@ -0,0 +1,252 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.dstu2016may.model.CodeableConcept;
import org.hl7.fhir.dstu2016may.model.Coding;
import org.hl7.fhir.dstu2016may.model.Condition;
import org.hl7.fhir.dstu2016may.model.Condition.ConditionVerificationStatus;
import org.hl7.fhir.dstu2016may.model.DateType;
import org.hl7.fhir.dstu2016may.model.Narrative.NarrativeStatus;
import org.hl7.fhir.dstu2016may.model.OperationOutcome;
import org.hl7.fhir.dstu2016may.model.Patient;
import org.hl7.fhir.dstu2016may.model.Reference;
import org.hl7.fhir.dstu2016may.model.StringType;
import org.junit.AfterClass;
import org.junit.Ignore;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.parser.XmlParserDstu2_1Test;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.SchemaBaseValidator;
import ca.uhn.fhir.validation.ValidationResult;
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
public class ResourceValidatorDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceValidatorDstu2_1Test.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
/**
* See issue #50
*/
@Test()
public void testOutOfBoundsDate() {
Patient p = new Patient();
p.setBirthDateElement(new DateType("2000-12-31"));
// Put in an invalid date
IParser parser = ourCtx.newXmlParser();
parser.setParserErrorHandler(new StrictErrorHandler());
String encoded = parser.setPrettyPrint(true).encodeResourceToString(p).replace("2000-12-31", "2000-15-31");
ourLog.info(encoded);
assertThat(encoded, StringContains.containsString("2000-15-31"));
ValidationResult result = ourCtx.newValidator().validateWithResult(encoded);
String resultString = parser.setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
ourLog.info(resultString);
assertEquals(2, ((OperationOutcome)result.toOperationOutcome()).getIssue().size());
assertThat(resultString, StringContains.containsString("cvc-pattern-valid"));
try {
parser.parseResource(encoded);
fail();
} catch (DataFormatException e) {
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
}
}
/**
* Make sure that the elements that appear in all resources (meta, language, extension, etc) all appear in the correct order
*/
@Test
public void testValidateResourceWithResourceElements() {
XmlParserDstu2_1Test.TestPatientFor327 patient = new XmlParserDstu2_1Test.TestPatientFor327();
patient.setBirthDate(new Date());
patient.setId("123");
patient.getText().setDivAsString("<div>FOO</div>");
patient.getText().setStatus(NarrativeStatus.GENERATED);
patient.getLanguageElement().setValue("en");
patient.addExtension().setUrl("http://foo").setValue(new StringType("MOD"));
patient.getMeta().setLastUpdated(new Date());
List<Reference> conditions = new ArrayList<Reference>();
Condition condition = new Condition();
condition.getPatient().setReference("Patient/123");
condition.addBodySite().setText("BODY SITE");
condition.getCode().setText("CODE");
condition.setClinicalStatus("active");
condition.setVerificationStatus(ConditionVerificationStatus.CONFIRMED);
conditions.add(new Reference(condition));
patient.setCondition(conditions);
patient.addIdentifier().setSystem("http://foo").setValue("123");
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
ourLog.info(encoded);
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(encoded);
OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
String ooencoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
ourLog.info(ooencoded);
assertTrue(result.isSuccessful());
assertThat(ooencoded, containsString("No issues detected"));
}
/**
* See https://groups.google.com/d/msgid/hapi-fhir/a266083f-6454-4cf0-a431-c6500f052bea%40googlegroups.com?utm_medium= email&utm_source=footer
*/
@Test
public void testValidateWithExtensionsXml() {
PatientProfileDstu2_1 myPatient = new PatientProfileDstu2_1();
myPatient.setId("1");
myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey")));
myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setSystem("furry-white")));
myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393"));
myPatient.addName().addFamily("FamilyName");
myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(myPatient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"meta",
"Organization/2.25.79433498044103547197447759549862032393",
"furry-grey",
"furry-white",
"String Extension",
"FamilyName"
));
assertThat(messageString, not(stringContainsInOrder(
"extension",
"meta"
)));
assertThat(messageString, containsString("url=\"http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#animal-colorSecondary\""));
assertThat(messageString, containsString("url=\"http://foo.com/example\""));
//@formatter:on
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(messageString);
OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
ourLog.info(encoded);
assertTrue(result.isSuccessful());
assertThat(messageString, containsString("valueReference"));
assertThat(messageString, not(containsString("valueResource")));
}
/**
* Per email from Jon Zammit
*/
@Test
@Ignore
public void testValidateQuestionnaire() throws IOException {
String input = IOUtils.toString(getClass().getResourceAsStream("/questionnaire_jon_z_20160506.xml"));
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(input);
OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
String ooencoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
ourLog.info(ooencoded);
assertTrue(result.isSuccessful());
}
/**
* See https://groups.google.com/d/msgid/hapi-fhir/a266083f-6454-4cf0-a431-c6500f052bea%40googlegroups.com?utm_medium= email&utm_source=footer
*/
@Test
public void testValidateWithExtensionsJson() {
PatientProfileDstu2_1 myPatient = new PatientProfileDstu2_1();
myPatient.setId("1");
myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey")));
myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setSystem("furry-white")));
myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393"));
myPatient.addName().addFamily("FamilyName");
myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));
IParser p = ourCtx.newJsonParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(myPatient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"meta",
"String Extension",
"Organization/2.25.79433498044103547197447759549862032393",
"furry-grey",
"furry-white",
"FamilyName"
));
assertThat(messageString, not(stringContainsInOrder(
"extension",
"meta"
)));
//@formatter:on
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(messageString);
OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
ourLog.info(encoded);
assertTrue(result.isSuccessful());
assertThat(messageString, containsString("valueReference"));
assertThat(messageString, not(containsString("valueResource")));
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<attributes>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="build/classes"/>
</classpath>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>hapi-fhir-validation-resources-dstu2.1</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@ -0,0 +1 @@
/classes/

View File

@ -0,0 +1,25 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>2.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-validation-resources-dstu2.1</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR - Validation Resources DSTU2.1 (2016May)</name>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<sch:ns prefix="f" uri="http://hl7.org/fhir"/>
<sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>
<!--
This file contains just the constraints for the resource Account
It is provided for documentation purposes. When actually validating,
always use fhir-invariants.sch (because of the way containment works)
Alternatively you can use this file to build a smaller version of
fhir-invariants.sch (the contents are identical; only include those
resources relevant to your implementation).
-->
<sch:pattern>
<sch:title>Global</sch:title>
<sch:rule context="//f:*">
<sch:assert test="@value|f:*|h:div">global-1: All FHIR elements must have a @value or children</sch:assert>
</sch:rule>
</sch:pattern>
<sch:pattern>
<sch:title>Account</sch:title>
<sch:rule context="//f:Account">
<sch:assert test="not(exists(f:contained/*/f:meta/f:versionId)) and not(exists(f:contained/*/f:meta/f:lastUpdated))">dom-4: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated</sch:assert>
<sch:assert test="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))">dom-3: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource</sch:assert>
<sch:assert test="not(parent::f:contained and f:contained)">dom-2: If the resource is contained in another resource, it SHALL NOT contain nested Resources</sch:assert>
<sch:assert test="not(parent::f:contained and f:text)">dom-1: If the resource is contained in another resource, it SHALL NOT contain any narrative</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:text/h:div">
<sch:assert test="descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]">txt-2: The narrative SHALL have some non-whitespace content</sch:assert>
<sch:assert test="not(descendant-or-self::*[not(local-name(.)=('a', 'abbr', 'acronym', 'b', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'))])">txt-1: The narrative SHALL contain only the basic html formatting elements described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
<sch:assert test="not(descendant-or-self::*/@*[not(name(.)=('abbr', 'accesskey', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellhalign', 'cellpadding', 'cellspacing', 'cellvalign', 'char', 'charoff', 'charset', 'cite', 'class', 'colspan', 'compact', 'coords', 'dir', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id', 'lang', 'longdesc', 'name', 'nowrap', 'rel', 'rev', 'rowspan', 'rules', 'scope', 'shape', 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'title', 'type', 'valign', 'value', 'vspace', 'width'))])">txt-3: The narrative SHALL contain only the basic html formatting attributes described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:identifier/f:period">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:identifier/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:activePeriod">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:balance">
<sch:assert test="not(exists(f:code)) or exists(f:system)">qty-3: If a code for the unit is present, the system SHALL also be present</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:coveragePeriod">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:subject">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:Account/f:owner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
</sch:pattern>
</sch:schema>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<sch:ns prefix="f" uri="http://hl7.org/fhir"/>
<sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>
<!--
This file contains just the constraints for the resource AllergyIntolerance
It is provided for documentation purposes. When actually validating,
always use fhir-invariants.sch (because of the way containment works)
Alternatively you can use this file to build a smaller version of
fhir-invariants.sch (the contents are identical; only include those
resources relevant to your implementation).
-->
<sch:pattern>
<sch:title>Global</sch:title>
<sch:rule context="//f:*">
<sch:assert test="@value|f:*|h:div">global-1: All FHIR elements must have a @value or children</sch:assert>
</sch:rule>
</sch:pattern>
<sch:pattern>
<sch:title>AllergyIntolerance</sch:title>
<sch:rule context="//f:AllergyIntolerance">
<sch:assert test="not(exists(f:contained/*/f:meta/f:versionId)) and not(exists(f:contained/*/f:meta/f:lastUpdated))">dom-4: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated</sch:assert>
<sch:assert test="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))">dom-3: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource</sch:assert>
<sch:assert test="not(parent::f:contained and f:contained)">dom-2: If the resource is contained in another resource, it SHALL NOT contain nested Resources</sch:assert>
<sch:assert test="not(parent::f:contained and f:text)">dom-1: If the resource is contained in another resource, it SHALL NOT contain any narrative</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:text/h:div">
<sch:assert test="descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]">txt-2: The narrative SHALL have some non-whitespace content</sch:assert>
<sch:assert test="not(descendant-or-self::*[not(local-name(.)=('a', 'abbr', 'acronym', 'b', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'))])">txt-1: The narrative SHALL contain only the basic html formatting elements described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
<sch:assert test="not(descendant-or-self::*/@*[not(name(.)=('abbr', 'accesskey', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellhalign', 'cellpadding', 'cellspacing', 'cellvalign', 'char', 'charoff', 'charset', 'cite', 'class', 'colspan', 'compact', 'coords', 'dir', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id', 'lang', 'longdesc', 'name', 'nowrap', 'rel', 'rev', 'rowspan', 'rules', 'scope', 'shape', 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'title', 'type', 'valign', 'value', 'vspace', 'width'))])">txt-3: The narrative SHALL contain only the basic html formatting attributes described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:identifier/f:period">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:identifier/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:patient">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:recorder">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:reporter">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:note/f:authorReference">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AllergyIntolerance/f:reaction/f:note/f:authorReference">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
</sch:pattern>
</sch:schema>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<sch:ns prefix="f" uri="http://hl7.org/fhir"/>
<sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>
<!--
This file contains just the constraints for the resource Appointment
It is provided for documentation purposes. When actually validating,
always use fhir-invariants.sch (because of the way containment works)
Alternatively you can use this file to build a smaller version of
fhir-invariants.sch (the contents are identical; only include those
resources relevant to your implementation).
-->
<sch:pattern>
<sch:title>Global</sch:title>
<sch:rule context="//f:*">
<sch:assert test="@value|f:*|h:div">global-1: All FHIR elements must have a @value or children</sch:assert>
</sch:rule>
</sch:pattern>
<sch:pattern>
<sch:title>Appointment</sch:title>
<sch:rule context="//f:Appointment">
<sch:assert test="not(exists(f:contained/*/f:meta/f:versionId)) and not(exists(f:contained/*/f:meta/f:lastUpdated))">dom-4: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated</sch:assert>
<sch:assert test="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))">dom-3: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource</sch:assert>
<sch:assert test="not(parent::f:contained and f:contained)">dom-2: If the resource is contained in another resource, it SHALL NOT contain nested Resources</sch:assert>
<sch:assert test="not(parent::f:contained and f:text)">dom-1: If the resource is contained in another resource, it SHALL NOT contain any narrative</sch:assert>
<sch:assert test="((exists(f:start) and exists(f:end)) or (f:status/@value='proposed') or (f:status/@value='cancelled'))">app-3: Only proposed or cancelled appointments can be missing start/end dates</sch:assert>
<sch:assert test="((exists(f:start) and exists(f:end)) or (not(exists(f:start)) and not(exists(f:end))))">app-2: Either start and end are specified, or neither</sch:assert>
</sch:rule>
<sch:rule context="//f:Appointment/f:text/h:div">
<sch:assert test="descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]">txt-2: The narrative SHALL have some non-whitespace content</sch:assert>
<sch:assert test="not(descendant-or-self::*[not(local-name(.)=('a', 'abbr', 'acronym', 'b', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'))])">txt-1: The narrative SHALL contain only the basic html formatting elements described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
<sch:assert test="not(descendant-or-self::*/@*[not(name(.)=('abbr', 'accesskey', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellhalign', 'cellpadding', 'cellspacing', 'cellvalign', 'char', 'charoff', 'charset', 'cite', 'class', 'colspan', 'compact', 'coords', 'dir', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id', 'lang', 'longdesc', 'name', 'nowrap', 'rel', 'rev', 'rowspan', 'rules', 'scope', 'shape', 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'title', 'type', 'valign', 'value', 'vspace', 'width'))])">txt-3: The narrative SHALL contain only the basic html formatting attributes described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
</sch:rule>
<sch:rule context="//f:Appointment/f:identifier/f:period">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:Appointment/f:identifier/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:Appointment/f:slot">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:Appointment/f:participant">
<sch:assert test="(exists(f:type) or exists(f:actor))">app-1: Either the type or actor on the participant MUST be specified</sch:assert>
</sch:rule>
<sch:rule context="//f:Appointment/f:participant/f:actor">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
</sch:pattern>
</sch:schema>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<sch:ns prefix="f" uri="http://hl7.org/fhir"/>
<sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>
<!--
This file contains just the constraints for the resource AppointmentResponse
It is provided for documentation purposes. When actually validating,
always use fhir-invariants.sch (because of the way containment works)
Alternatively you can use this file to build a smaller version of
fhir-invariants.sch (the contents are identical; only include those
resources relevant to your implementation).
-->
<sch:pattern>
<sch:title>Global</sch:title>
<sch:rule context="//f:*">
<sch:assert test="@value|f:*|h:div">global-1: All FHIR elements must have a @value or children</sch:assert>
</sch:rule>
</sch:pattern>
<sch:pattern>
<sch:title>AppointmentResponse</sch:title>
<sch:rule context="//f:AppointmentResponse">
<sch:assert test="not(exists(f:contained/*/f:meta/f:versionId)) and not(exists(f:contained/*/f:meta/f:lastUpdated))">dom-4: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated</sch:assert>
<sch:assert test="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))">dom-3: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource</sch:assert>
<sch:assert test="not(parent::f:contained and f:contained)">dom-2: If the resource is contained in another resource, it SHALL NOT contain nested Resources</sch:assert>
<sch:assert test="not(parent::f:contained and f:text)">dom-1: If the resource is contained in another resource, it SHALL NOT contain any narrative</sch:assert>
<sch:assert test="(exists(f:participantType) or exists(f:actor))">apr-1: Either the participantType or actor must be specified</sch:assert>
</sch:rule>
<sch:rule context="//f:AppointmentResponse/f:text/h:div">
<sch:assert test="descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]">txt-2: The narrative SHALL have some non-whitespace content</sch:assert>
<sch:assert test="not(descendant-or-self::*[not(local-name(.)=('a', 'abbr', 'acronym', 'b', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'))])">txt-1: The narrative SHALL contain only the basic html formatting elements described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
<sch:assert test="not(descendant-or-self::*/@*[not(name(.)=('abbr', 'accesskey', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellhalign', 'cellpadding', 'cellspacing', 'cellvalign', 'char', 'charoff', 'charset', 'cite', 'class', 'colspan', 'compact', 'coords', 'dir', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id', 'lang', 'longdesc', 'name', 'nowrap', 'rel', 'rev', 'rowspan', 'rules', 'scope', 'shape', 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'title', 'type', 'valign', 'value', 'vspace', 'width'))])">txt-3: The narrative SHALL contain only the basic html formatting attributes described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
</sch:rule>
<sch:rule context="//f:AppointmentResponse/f:identifier/f:period">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:AppointmentResponse/f:identifier/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AppointmentResponse/f:appointment">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AppointmentResponse/f:actor">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
</sch:pattern>
</sch:schema>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<sch:ns prefix="f" uri="http://hl7.org/fhir"/>
<sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>
<!--
This file contains just the constraints for the resource AuditEvent
It is provided for documentation purposes. When actually validating,
always use fhir-invariants.sch (because of the way containment works)
Alternatively you can use this file to build a smaller version of
fhir-invariants.sch (the contents are identical; only include those
resources relevant to your implementation).
-->
<sch:pattern>
<sch:title>Global</sch:title>
<sch:rule context="//f:*">
<sch:assert test="@value|f:*|h:div">global-1: All FHIR elements must have a @value or children</sch:assert>
</sch:rule>
</sch:pattern>
<sch:pattern>
<sch:title>AuditEvent</sch:title>
<sch:rule context="//f:AuditEvent">
<sch:assert test="not(exists(f:contained/*/f:meta/f:versionId)) and not(exists(f:contained/*/f:meta/f:lastUpdated))">dom-4: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated</sch:assert>
<sch:assert test="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))">dom-3: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource</sch:assert>
<sch:assert test="not(parent::f:contained and f:contained)">dom-2: If the resource is contained in another resource, it SHALL NOT contain nested Resources</sch:assert>
<sch:assert test="not(parent::f:contained and f:text)">dom-1: If the resource is contained in another resource, it SHALL NOT contain any narrative</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:text/h:div">
<sch:assert test="descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]">txt-2: The narrative SHALL have some non-whitespace content</sch:assert>
<sch:assert test="not(descendant-or-self::*[not(local-name(.)=('a', 'abbr', 'acronym', 'b', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'))])">txt-1: The narrative SHALL contain only the basic html formatting elements described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
<sch:assert test="not(descendant-or-self::*/@*[not(name(.)=('abbr', 'accesskey', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellhalign', 'cellpadding', 'cellspacing', 'cellvalign', 'char', 'charoff', 'charset', 'cite', 'class', 'colspan', 'compact', 'coords', 'dir', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id', 'lang', 'longdesc', 'name', 'nowrap', 'rel', 'rev', 'rowspan', 'rules', 'scope', 'shape', 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'title', 'type', 'valign', 'value', 'vspace', 'width'))])">txt-3: The narrative SHALL contain only the basic html formatting attributes described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:agent/f:reference">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:agent/f:userId/f:period">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:agent/f:userId/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:agent/f:location">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:source/f:identifier/f:period">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:source/f:identifier/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:entity">
<sch:assert test="not(exists(f:name)) or not(exists(f:query))">sev-1: Either a name or a query (NOT both)</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:entity/f:identifier/f:period">
<sch:assert test="not(exists(f:start)) or not(exists(f:end)) or (f:start/@value &lt;= f:end/@value)">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:entity/f:identifier/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
<sch:rule context="//f:AuditEvent/f:entity/f:reference">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a local reference if the resource is provided inline</sch:assert>
</sch:rule>
</sch:pattern>
</sch:schema>

Some files were not shown because too many files have changed in this diff Show More