Add validator for DSTU2.1
This commit is contained in:
parent
191e3b6d4d
commit
9cb014724b
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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() {
|
|
@ -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);
|
||||
|
||||
|
||||
/**
|
|
@ -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();
|
||||
|
|
@ -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() {
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
@ -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"));
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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)
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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();
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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();
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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) ;
|
||||
}
|
||||
|
||||
}
|
|
@ -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")));
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
|
@ -0,0 +1,4 @@
|
|||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
|
@ -0,0 +1 @@
|
|||
/classes/
|
|
@ -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>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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, <a> 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, <a> 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 <= 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 <= 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 <= 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>
|
|
@ -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, <a> 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, <a> 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 <= 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>
|
|
@ -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, <a> 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, <a> 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 <= 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>
|
|
@ -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, <a> 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, <a> 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 <= 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>
|
|
@ -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, <a> 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, <a> 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 <= 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 <= 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 <= 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
Loading…
Reference in New Issue