more work on PEDefinition

This commit is contained in:
Grahame Grieve 2022-12-26 13:28:22 +13:00
parent 900f471dda
commit 9959183017
21 changed files with 947 additions and 601 deletions

View File

@ -76,6 +76,7 @@ public class FHIRLexer {
private SourceLocation currentStartLocation;
private int id;
private String name;
private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host
public FHIRLexer(String source, String name) throws FHIRLexerException {
this.source = source == null ? "" : source;
@ -201,7 +202,7 @@ public class FHIRLexer {
cursor++;
if (cursor < source.length() && (source.charAt(cursor) == '/')) {
// this is en error - should already have been skipped
error("This shoudn't happen?");
error("This shouldn't happen?");
}
current = source.substring(currentStart, cursor);
} else if (ch == '$') {
@ -274,6 +275,12 @@ public class FHIRLexer {
throw error("Unterminated string");
cursor++;
current = "`"+source.substring(currentStart+1, cursor-1)+"`";
} else if (ch == '|' && liquidMode) {
cursor++;
ch = source.charAt(cursor);
if (ch == '|')
cursor++;
current = source.substring(currentStart, cursor);
} else if (ch == '@'){
int start = cursor;
cursor++;
@ -519,5 +526,14 @@ public class FHIRLexer {
public int getCurrentStart() {
return currentStart;
}
public String getSource() {
return source;
}
public boolean isLiquidMode() {
return liquidMode;
}
public void setLiquidMode(boolean liquidMode) {
this.liquidMode = liquidMode;
}
}

View File

@ -243,6 +243,7 @@ public class FHIRPathEngine {
return false;
}
}
}
private IWorkerContext worker;
private IEvaluationContext hostServices;
@ -255,6 +256,7 @@ public class FHIRPathEngine {
private String location; // for error messages
private boolean allowPolymorphicNames;
private boolean doImplicitStringConversion;
private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host
// if the fhir path expressions are allowed to use constants beyond those defined in the specification
// the application can implement them by providing a constant resolver
@ -4754,7 +4756,11 @@ public class FHIRPathEngine {
}
}
} else if (hostServices != null) {
res = hostServices.resolveReference(context.appInfo, s, refContext);
try {
res = hostServices.resolveReference(context.appInfo, s, refContext);
} catch (Exception e) {
res = null;
}
}
if (res != null) {
result.add(res);
@ -6023,5 +6029,13 @@ public class FHIRPathEngine {
this.allowPolymorphicNames = allowPolymorphicNames;
}
public boolean isLiquidMode() {
return liquidMode;
}
public void setLiquidMode(boolean liquidMode) {
this.liquidMode = liquidMode;
}
}

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.r4b.utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -37,6 +38,7 @@ import java.util.Map;
*/
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.r4b.context.IWorkerContext;
import org.hl7.fhir.r4b.model.Base;
@ -53,6 +55,10 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class LiquidEngine implements IEvaluationContext {
public interface ILiquidRenderingSupport {
String renderForLiquid(Object appContext, Base i) throws FHIRException;
}
public interface ILiquidEngineIncludeResolver {
public String fetchInclude(LiquidEngine engine, String name);
}
@ -60,6 +66,7 @@ public class LiquidEngine implements IEvaluationContext {
private IEvaluationContext externalHostServices;
private FHIRPathEngine engine;
private ILiquidEngineIncludeResolver includeResolver;
private ILiquidRenderingSupport renderingSupport;
private class LiquidEngineContext {
private Object externalContext;
@ -82,6 +89,7 @@ public class LiquidEngine implements IEvaluationContext {
this.externalHostServices = hostServices;
engine = new FHIRPathEngine(context);
engine.setHostServices(this);
engine.setLiquidMode(true);
}
public ILiquidEngineIncludeResolver getIncludeResolver() {
@ -92,6 +100,14 @@ public class LiquidEngine implements IEvaluationContext {
this.includeResolver = includeResolver;
}
public ILiquidRenderingSupport getRenderingSupport() {
return renderingSupport;
}
public void setRenderingSupport(ILiquidRenderingSupport renderingSupport) {
this.renderingSupport = renderingSupport;
}
public LiquidDocument parse(String source, String sourceName) throws FHIRException {
return new LiquidParser(source).parse(sourceName);
}
@ -130,17 +146,84 @@ public class LiquidEngine implements IEvaluationContext {
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) {
b.append(constant);
}
}
private enum LiquidFilter {
PREPEND;
public static LiquidFilter fromCode(String code) {
if ("prepend".equals(code)) {
return PREPEND;
}
return null;
}
}
private class LiquidExpressionNode {
private LiquidFilter filter; // null at root
private ExpressionNode expression; // null for some filters
public LiquidExpressionNode(LiquidFilter filter, ExpressionNode expression) {
super();
this.filter = filter;
this.expression = expression;
}
}
private class LiquidStatement extends LiquidNode {
private String statement;
private ExpressionNode compiled;
private List<LiquidExpressionNode> compiled = new ArrayList<>();
@Override
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
if (compiled == null)
compiled = engine.parse(statement);
b.append(engine.evaluateToString(ctxt, resource, resource, resource, compiled));
if (compiled.size() == 0) {
FHIRLexer lexer = new FHIRLexer(statement, "liquid statement");
lexer.setLiquidMode(true);
compiled.add(new LiquidExpressionNode(null, engine.parse(lexer)));
while (!lexer.done()) {
if (lexer.getCurrent().equals("||")) {
lexer.next();
String f = lexer.getCurrent();
LiquidFilter filter = LiquidFilter.fromCode(f);
if (filter == null) {
lexer.error("Unknown Liquid filter '"+f+"'");
}
lexer.next();
if (!lexer.done() && lexer.getCurrent().equals(":")) {
lexer.next();
compiled.add(new LiquidExpressionNode(filter, engine.parse(lexer)));
} else {
compiled.add(new LiquidExpressionNode(filter, null));
}
} else {
lexer.error("Unexpected syntax parsing liquid statement");
}
}
}
String t = null;
for (LiquidExpressionNode i : compiled) {
if (i.filter == null) { // first
t = stmtToString(ctxt, engine.evaluate(ctxt, resource, resource, resource, i.expression));
} else switch (i.filter) {
case PREPEND:
t = stmtToString(ctxt, engine.evaluate(ctxt, resource, resource, resource, i.expression)) + t;
break;
}
}
b.append(t);
}
private String stmtToString(LiquidEngineContext ctxt, List<Base> items) {
StringBuilder b = new StringBuilder();
boolean first = true;
for (Base i : items) {
if (first) first = false; else b.append(", ");
String s = renderingSupport != null ? renderingSupport.renderForLiquid(ctxt.externalContext, i) : null;
b.append(s != null ? s : engine.convertToString(i));
}
return b.toString();
}
}

View File

@ -104,8 +104,8 @@ import org.hl7.fhir.r5.model.Bundle.BundleType;
import org.hl7.fhir.r5.model.Bundle.HTTPVerb;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.profilemodel.ProfiledElement;
import org.hl7.fhir.r5.profilemodel.ProfiledElementBuilder;
import org.hl7.fhir.r5.profilemodel.PEDefinition;
import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.renderers.OperationOutcomeRenderer;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
@ -2391,8 +2391,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
@Override
public ProfiledElementBuilder getProfiledElementBuilder() {
public PEBuilder getProfiledElementBuilder(boolean elementProps) {
// TODO Auto-generated method stub
return new ProfiledElementBuilder(this);
return new PEBuilder(this, elementProps);
}
}

View File

@ -65,8 +65,8 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.profilemodel.ProfiledElement;
import org.hl7.fhir.r5.profilemodel.ProfiledElementBuilder;
import org.hl7.fhir.r5.profilemodel.PEDefinition;
import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
@ -803,6 +803,6 @@ public interface IWorkerContext {
public String getSpecUrl();
public ProfiledElementBuilder getProfiledElementBuilder();
public PEBuilder getProfiledElementBuilder(boolean elementProps);
}

View File

@ -61,8 +61,8 @@ import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode;
import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
import org.hl7.fhir.r5.profilemodel.ProfiledElement;
import org.hl7.fhir.r5.profilemodel.ProfiledElementBuilder;
import org.hl7.fhir.r5.profilemodel.PEDefinition;
import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.r5.terminologies.TerminologyClient;

View File

@ -0,0 +1,295 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ResourceFactory;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.utilities.Utilities;
public class PEBuilder {
private IWorkerContext context;
private ProfileUtilities pu;
private boolean elementProps;
public PEBuilder(IWorkerContext context, boolean elementProps) {
super();
this.context = context;
this.elementProps = elementProps;
pu = new ProfileUtilities(context, null, null);
}
/**
* Given a profile, return a tree of the elements defined in the profile model. This builds the profile model
* for the latest version of the nominated profile
*
* The tree of elements in the profile model is different to those defined in the base resource:
* - some elements are removed (max = 0)
* - extensions are turned into named elements
* - slices are turned into named elements
* - element properties - doco, cardinality, binding etc is updated for what the profile says
*
* Warning: profiles and resources are recursive; you can't iterate this tree until it you get
* to the leaves because there are nodes that don't terminate (extensions have extensions)
*
*/
public PEDefinition buildPEDefinition(String url) {
StructureDefinition profile = getProfile(url);
if (profile == null) {
throw new DefinitionException("Unable to find profile for URL '"+url+"'");
}
StructureDefinition base = context.fetchTypeDefinition(profile.getType());
if (base == null) {
throw new DefinitionException("Unable to find base type '"+profile.getType()+"' for URL '"+url+"'");
}
return new PEDefinitionResource(this, base, profile);
}
/**
* Given a profile, return a tree of the elements defined in the profile model. This builds the profile model
* for the nominated version of the nominated profile
*
* The tree of elements in the profile model is different to the the base resource:
* - some elements are removed (max = 0)
* - extensions are turned into named elements
* - slices are turned into named elements
* - element properties - doco, cardinality, binding etc is updated for what the profile says
*
* Warning: profiles and resources can be recursive; you can't iterate this tree until it you get
* to the leaves because you will never get to a child that doesn't have children
*
*/
public PEDefinition buildPEDefinition(String url, String version) {
StructureDefinition profile = getProfile(url, version);
if (profile == null) {
throw new DefinitionException("Unable to find profile for URL '"+url+"'");
}
StructureDefinition base = context.fetchTypeDefinition(profile.getType());
if (base == null) {
throw new DefinitionException("Unable to find base type '"+profile.getType()+"' for URL '"+url+"'");
}
return new PEDefinitionResource(this, base, profile);
}
/**
* Given a resource and a profile, return a tree of instance data as defined by the profile model
* using the latest version of the profile
*
* The tree is a facade to the underlying resource - all actual data is stored against the resource,
* and retrieved on the fly from the resource, so that applications can work at either level, as
* convenient.
*
* Note that there's a risk that deleting something through the resource while holding
* a handle to a PEInstance that is a facade on what is deleted leaves an orphan facade
* that will continue to function, but is making changes to resource content that is no
* longer part of the resource
*
*/
public PEInstance buildPEInstance(String url, Resource resource) {
throw new NotImplementedException("NOt done yet");
}
/**
* Given a resource and a profile, return a tree of instance data as defined by the profile model
* using the nominated version of the profile
*
* The tree is a facade to the underlying resource - all actual data is stored against the resource,
* and retrieved on the fly from the resource, so that applications can work at either level, as
* convenient.
*
* Note that there's a risk that deleting something through the resource while holding
* a handle to a PEInstance that is a facade on what is deleted leaves an orphan facade
* that will continue to function, but is making changes to resource content that is no
* longer part of the resource
*/
public PEInstance buildPEInstance(String url, String version, Resource resource) {
throw new NotImplementedException("Not done yet");
}
/**
* Given a profile, construct an empty resource of the type being profiled (to use as input
* to the buildPEInstance method
*
* No version, because the version doesn't change the type of the resource
*/
public Resource makeProfileBase(String url) {
StructureDefinition profile = getProfile(url);
return ResourceFactory.createResource(profile.getType());
}
// -- methods below here are only used internally to the package
private StructureDefinition getProfile(String url) {
return context.fetchResource(StructureDefinition.class, url);
}
private StructureDefinition getProfile(String url, String version) {
return context.fetchResource(StructureDefinition.class, url, version);
}
protected List<PEDefinition> listChildren(StructureDefinition baseStructure, ElementDefinition baseDefinition,
StructureDefinition profileStructure, ElementDefinition profiledDefinition, TypeRefComponent t, CanonicalType u) {
// TODO Auto-generated method stub
return null;
}
protected List<PEDefinition> listChildren(StructureDefinition baseStructure, ElementDefinition baseDefinition, StructureDefinition profileStructure, ElementDefinition profileDefinition, String url) {
StructureDefinition profile = profileStructure;
List<ElementDefinition> list = pu.getChildList(profile, profileDefinition);
if (profileDefinition.getType().size() == 1 || (!profileDefinition.getPath().contains(".")) || list.isEmpty()) {
assert url == null || checkType(profileDefinition, url);
List<PEDefinition> res = new ArrayList<>();
if (list.size() == 0) {
profile = context.fetchResource(StructureDefinition.class, url);
list = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep());
}
if (list.size() > 0) {
StructureDefinition base = baseStructure;
List<ElementDefinition> blist = pu.getChildList(baseStructure, baseDefinition);
if (blist.size() == 0) {
base = context.fetchTypeDefinition(url);
blist = pu.getChildList(base, base.getSnapshot().getElementFirstRep());
}
int i = 0;
while (i < list.size()) {
ElementDefinition defn = list.get(i);
ElementDefinition bdefn = getByName(blist, defn);
if (bdefn == null) {
throw new Error("no base definition for "+defn.getId());
}
if (elementProps || (!Utilities.existsInList(bdefn.getBase().getPath(), "Element.id", "Element.extension"))) {
PEDefinitionElement pe = new PEDefinitionElement(this, base, bdefn, profileStructure, defn);
pe.setRecursing(profileDefinition == defn || (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION && profile.getType().equals("Extension")));
if (defn.hasSlicing()) {
if (defn.getSlicing().getRules() != SlicingRules.CLOSED) {
res.add(pe);
}
i++;
while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) {
StructureDefinition ext = getExtensionDefinition(list.get(i));
if (ext != null) {
res.add(new PEDefinitionExtension(this, list.get(i).getSliceName(), baseStructure, getByName(blist, defn), profileStructure, list.get(i), defn, ext));
} else {
res.add(new PEDefinitionSlice(this, list.get(i).getSliceName(), baseStructure, getByName(blist, defn), profileStructure, list.get(i), defn));
}
i++;
}
} else {
res.add(pe);
i++;
}
} else {
i++;
}
}
}
return res;
} else if (list.isEmpty()) {
throw new DefinitionException("not done yet!");
} else {
throw new DefinitionException("not done yet");
}
}
protected List<PEDefinition> listSlices(StructureDefinition baseStructure, ElementDefinition baseDefinition, StructureDefinition profileStructure, ElementDefinition profileDefinition) {
List<ElementDefinition> list = pu.getSliceList(profileStructure, profileDefinition);
List<PEDefinition> res = new ArrayList<>();
for (ElementDefinition ed : list) {
if (profileStructure.getDerivation() == TypeDerivationRule.CONSTRAINT && profileStructure.getType().equals("Extension")) {
res.add(new PEDefinitionSubExtension(this, baseStructure, baseDefinition, profileStructure, ed));
} else {
PEDefinitionElement pe = new PEDefinitionElement(this, baseStructure, baseDefinition, profileStructure, ed);
pe.setRecursing(profileDefinition == ed || (profileStructure.getDerivation() == TypeDerivationRule.SPECIALIZATION && profileStructure.getType().equals("Extension")));
res.add(pe);
}
}
return res;
}
private boolean checkType(ElementDefinition defn, String url) {
for (TypeRefComponent t : defn.getType()) {
if (("http://hl7.org/fhir/StructureDefinition/"+t.getWorkingCode()).equals(url)) {
return true;
}
for (CanonicalType u : t.getProfile()) {
if (url.equals(u.getValue())) {
return true;
}
}
}
return false;
}
private StructureDefinition getExtensionDefinition(ElementDefinition ed) {
if ("Extension".equals(ed.getTypeFirstRep().getWorkingCode()) && ed.getTypeFirstRep().getProfile().size() == 1) {
return context.fetchResource(StructureDefinition.class, ed.getTypeFirstRep().getProfile().get(0).asStringValue());
} else {
return null;
}
}
private ElementDefinition getByName(List<ElementDefinition> blist, ElementDefinition defn) {
for (ElementDefinition ed : blist) {
if (ed.getName().equals(defn.getName())) {
return ed;
}
}
return null;
}
public PEType makeType(TypeRefComponent t) {
if (t.hasProfile()) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, t.getProfile().get(0).getValue());
if (sd == null) {
return new PEType(tail(t.getProfile().get(0).getValue()), t.getWorkingCode(), t.getProfile().get(0).getValue());
} else {
return new PEType(sd.getName(), t.getWorkingCode(), t.getProfile().get(0).getValue());
}
} else {
return makeType(t.getWorkingCode());
}
}
public PEType makeType(TypeRefComponent t, CanonicalType u) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue());
if (sd == null) {
return new PEType(tail(u.getValue()), t.getWorkingCode(), u.getValue());
} else {
return new PEType(sd.getName(), t.getWorkingCode(), u.getValue());
}
}
public PEType makeType(String tn) {
return new PEType(tn, tn, "http://hl7.org/fhir/StructureDefinition/"+ tn);
}
private String tail(String value) {
return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value;
}
public List<ElementDefinition> getChildren(StructureDefinition profileStructure, ElementDefinition profiledDefinition) {
return pu.getChildList(profileStructure, profiledDefinition);
}
}

View File

@ -0,0 +1,173 @@
package org.hl7.fhir.r5.profilemodel;
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.r5.model.Base;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
public abstract class PEDefinition {
protected PEBuilder builder;
protected String name;
protected StructureDefinition baseStructure;
protected ElementDefinition baseDefinition;
protected StructureDefinition profileStructure;
protected ElementDefinition profiledDefinition;
protected List<PEType> types;
protected Map<String, List<PEDefinition>> children = new HashMap<>();
private boolean recursing;
/**
* Don't create one of these directly - always use the public methods on ProfiledElementBuilder
*
* @param builder
* @param baseElement
* @param profiledElement
* @param data
*/
protected PEDefinition(PEBuilder builder, String name, ElementDefinition baseDefinition,
ElementDefinition profiledDefinition, Base data) {
super();
this.builder = builder;
this.name = name;
this.baseDefinition = baseDefinition;
this.profiledDefinition = profiledDefinition;
// this.data = data;
}
protected PEDefinition(PEBuilder builder, String name, StructureDefinition base, ElementDefinition baseDefinition,
StructureDefinition profile, ElementDefinition profiledDefinition) {
this.builder = builder;
this.name = name;
this.baseStructure = base;
this.baseDefinition = baseDefinition;
this.profileStructure = profile;
this.profiledDefinition = profiledDefinition;
}
/**
* @return The name of the element or slice in the profile (always unique amongst children)
*/
public String name() {
return name;
}
/**
* @return The name of the element in the resource (may be different to the slice name)
*/
public String schemaName() {
return baseDefinition.getName();
}
/**
* @return a list of types. There is usually at least one type; it might be Element, Type, BackboneElement or BackboneType
*
* The following elements don't have types (true primitives): Element.id. Extension.url, PrimitiveType.value
*/
public List<PEType> types() {
if (types == null) {
List<PEType> ltypes = new ArrayList<>();
listTypes(ltypes);
types = ltypes;
}
return types;
}
protected abstract void listTypes(List<PEType> types);
/**
* @return The minimum number of repeats allowed
*/
public int min() {
return profiledDefinition.getMin();
}
/**
* @return the maximum number of repeats allowed
*/
public int max() {
return "*".equals(profiledDefinition.getMax()) ? Integer.MAX_VALUE : Integer.parseInt(profiledDefinition.getMax());
}
/**
* @return the definition of the element in the profile (fully populated)
*
* Note that the profile definition might be the same as a base definition, when the tree runs off the end of what's profiled
*/
public ElementDefinition definition() {
return profiledDefinition;
}
/**
* @return the definition of the element in the base specification
*
* Note that the profile definition might be the same as a base definition, when the tree runs off the end of what's profiled
*/
public ElementDefinition baseDefinition() {
return baseDefinition;
}
/**
* @return the short documentation of the definition (shown in the profile table view)
*/
public String shortDocumentation() {
return profiledDefinition.getShort();
}
/**
* @return the full definition of the element (markdown syntax)
*/
public String documentation() {
return profiledDefinition.getDefinition();
}
// /**
// * @return if the profiled definition has a value set
// */
// public ValueSet expansion() {
// throw new NotImplementedException("Not done yet");
// }
//
/**
* @param typeUrl - the url of one of the types listed in types()
* @return - the list of children for the nominated type
*
* Warning: profiles and resources can be recursive; you can't iterate this tree until you get
* to the leaves because you will never get to a child that doesn't have children (extensions have extensions etc)
*
*/
public List<PEDefinition> children(String typeUrl) {
if (children.containsKey(typeUrl)) {
return children.get(typeUrl);
}
List<PEDefinition> res = new ArrayList<>();
makeChildren(typeUrl, res);
children.put(typeUrl, res);
return res;
}
protected abstract void makeChildren(String typeUrl, List<PEDefinition> children);
@Override
public String toString() {
return name+"("+schemaName()+"):"+types().toString()+" ["+min()+":"+(max() == Integer.MAX_VALUE ? "*" : max() )+"] \""+shortDocumentation()+"\"";
}
public boolean isRecursing() {
return recursing;
}
public void setRecursing(boolean recursing) {
this.recursing = recursing;
}
}

View File

@ -0,0 +1,36 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.List;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PEDefinitionElement extends PEDefinition {
public PEDefinitionElement(PEBuilder builder,
StructureDefinition baseStructure, ElementDefinition baseDefinition,
StructureDefinition profileStructure, ElementDefinition profileDefinition) {
super(builder, baseDefinition.getName(), baseStructure, baseDefinition, profileStructure, profileDefinition);
}
@Override
public void listTypes(List<PEType> types) {
for (TypeRefComponent t : profiledDefinition.getType()) {
if (t.hasProfile()) {
for (CanonicalType u : t.getProfile()) {
types.add(builder.makeType(t, u));
}
} else if (!t.getCode().startsWith("http://hl7.org/fhirpath/")) {
types.add(new PEType(t.getWorkingCode(), t.getWorkingCode(), "http://hl7.org/fhir/StructureDefinition/"+t.getWorkingCode()));
}
}
}
@Override
protected void makeChildren(String typeUrl, List<PEDefinition> children) {
children.addAll(builder.listChildren(baseStructure, baseDefinition, profileStructure, profiledDefinition, typeUrl));
}
}

View File

@ -1,50 +1,54 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PEExtension extends ProfiledElement {
public class PEDefinitionExtension extends PEDefinition {
private StructureDefinition extension;
private ElementDefinition sliceDefinition;
public PEExtension(ProfiledElementBuilder builder, String name,
public PEDefinitionExtension(PEBuilder builder, String name,
StructureDefinition baseStructure, ElementDefinition baseDefinition,
StructureDefinition profileStructure, ElementDefinition profileDefinition,
ElementDefinition sliceDefinition, StructureDefinition extension) {
super(builder, name, baseStructure, baseDefinition, profileStructure, profileDefinition, sliceDefinition);
super(builder, name, baseStructure, baseDefinition, profileStructure, profileDefinition);
this.sliceDefinition = sliceDefinition;
this.extension= extension;
}
@Override
public List<String> types() {
List<String> res = new ArrayList<>();
public void listTypes(List<PEType> types) {
ElementDefinition eed = extension.getSnapshot().getElementByPath("Extension.extension");
ElementDefinition ved = extension.getSnapshot().getElementByPath("Extension.value[x]");
if (ved.isRequired() || eed.isProhibited()) {
for (TypeRefComponent t : ved.getType()) {
if (t.hasProfile()) {
for (CanonicalType u : t.getProfile()) {
res.add(t.getWorkingCode()+"["+u.getValue()+"]");
types.add(builder.makeType(t, u));
}
} else {
res.add(t.getWorkingCode());
types.add(builder.makeType(t.getWorkingCode()));
}
}
} else {
res.add("Extension");
types.add(builder.makeType("Extension"));
}
return res;
}
@Override
public List<ProfiledElement> children(String type) {
throw new Error("Not done yet");
protected void makeChildren(String typeUrl, List<PEDefinition> children) {
ElementDefinition eed = extension.getSnapshot().getElementByPath("Extension.extension");
ElementDefinition ved = extension.getSnapshot().getElementByPath("Extension.value[x]");
if (ved.isRequired() || eed.isProhibited()) {
children.addAll(builder.listChildren(extension, ved, extension, ved, typeUrl));
} else {
children.addAll(builder.listSlices(extension, eed, extension, eed));
}
}
}

View File

@ -0,0 +1,23 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.List;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PEDefinitionResource extends PEDefinition {
public PEDefinitionResource(PEBuilder builder, StructureDefinition base, StructureDefinition profile) {
super(builder, profile.getName(), base, base.getSnapshot().getElementFirstRep(), profile, profile.getSnapshot().getElementFirstRep());
}
@Override
public void listTypes(List<PEType> types) {
types.add(new PEType(profileStructure.getName(), profileStructure.getType(), profileStructure.getUrl()));
}
@Override
protected void makeChildren(String typeUrl, List<PEDefinition> children) {
children.addAll(builder.listChildren(baseStructure, baseDefinition, profileStructure, profiledDefinition, null));
}
}

View File

@ -0,0 +1,30 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.List;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PEDefinitionSlice extends PEDefinition {
protected ElementDefinition sliceDefinition;
public PEDefinitionSlice(PEBuilder builder, String name, StructureDefinition baseStructure,
ElementDefinition baseDefinition, StructureDefinition profileStructure, ElementDefinition profileDefinition,
ElementDefinition sliceDefinition) {
super(builder, name, baseStructure, baseDefinition, profileStructure, profileDefinition);
this.sliceDefinition = sliceDefinition;
}
@Override
public void listTypes(List<PEType> types) {
throw new Error("Not done yet");
}
@Override
protected void makeChildren(String typeUrl, List<PEDefinition> children) {
throw new Error("Not done yet");
}
}

View File

@ -0,0 +1,57 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.List;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PEDefinitionSubExtension extends PEDefinition {
public PEDefinitionSubExtension(PEBuilder builder, StructureDefinition baseStructure, ElementDefinition baseDefinition, StructureDefinition profileStructure, ElementDefinition profileDefinition) {
super(builder, profileDefinition.getSliceName(), baseStructure, baseDefinition, profileStructure, profileDefinition);
}
@Override
public void listTypes(List<PEType> types) {
List<ElementDefinition> childDefs = builder.getChildren(profileStructure, profiledDefinition);
ElementDefinition eed = getElementByName(childDefs, "extension");
ElementDefinition ved = getElementByName(childDefs, "value[x]");
if (ved.isRequired() || eed.isProhibited()) {
for (TypeRefComponent t : ved.getType()) {
if (t.hasProfile()) {
for (CanonicalType u : t.getProfile()) {
types.add(builder.makeType(t, u));
}
} else {
types.add(builder.makeType(t.getWorkingCode()));
}
}
} else {
types.add(builder.makeType("Extension"));
}
}
private ElementDefinition getElementByName(List<ElementDefinition> children, String name) {
for (ElementDefinition ed : children) {
if (name.equals(ed.getName())) {
return ed;
}
}
return null;
}
@Override
protected void makeChildren(String typeUrl, List<PEDefinition> children) {
List<ElementDefinition> childDefs = builder.getChildren(profileStructure, profiledDefinition);
ElementDefinition eed = getElementByName(childDefs, "extension");
ElementDefinition ved = getElementByName(childDefs, "value[x]");
if (ved.isRequired() || eed.isProhibited()) {
children.addAll(builder.listChildren(baseStructure, baseDefinition, profileStructure, ved, typeUrl));
} else {
children.addAll(builder.listSlices(baseStructure, baseDefinition, profileStructure, eed));
}
}
}

View File

@ -1,58 +0,0 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PEElement extends ProfiledElement {
public PEElement(ProfiledElementBuilder builder,
StructureDefinition baseStructure, ElementDefinition baseDefinition,
StructureDefinition profileStructure, ElementDefinition profileDefinition) {
super(builder, baseDefinition.getName(), baseStructure, baseDefinition, profileStructure, profileDefinition);
}
@Override
public List<String> types() {
List<String> res = new ArrayList<>();
for (TypeRefComponent t : profiledDefinition.getType()) {
if (t.hasProfile()) {
for (CanonicalType u : t.getProfile()) {
res.add(t.getWorkingCode()+"["+u.getValue()+"]");
}
} else if (!t.getCode().startsWith("http://hl7.org/fhirpath/")) {
res.add(t.getWorkingCode());
}
}
return res;
}
@Override
public List<ProfiledElement> children(String type) {
if (children == null) {
for (TypeRefComponent t : profiledDefinition.getType()) {
if (t.hasProfile()) {
for (CanonicalType u : t.getProfile()) {
if ((t.getWorkingCode()+"["+u.getValue()+"]").equals(type)) {
children = builder.listChildren(baseStructure, baseDefinition, profileStructure, profiledDefinition, t, u);
}
}
} else {
if (t.getWorkingCode().equals(type)) {
children = builder.listChildren(baseStructure, baseDefinition, profileStructure, profiledDefinition, t);
}
}
}
}
if (children != null) {
return children;
}
throw new DefinitionException("Unable to understand type '"+type+"'");
}
}

View File

@ -0,0 +1,81 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.List;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.Resource;
public abstract class PEInstance {
private PEDefinition definition;
private Base data;
protected PEInstance(PEDefinition definition, Base data) {
super();
this.definition = definition;
this.data = data;
}
/**
* @return definition information about this instance data
*/
public abstract PEDefinition definition();
/**
* @return the type of this element
*/
public abstract PEType type();
/**
* @return all the children of this instance data
*/
public abstract List<PEInstance> children();
/**
* @return all the children of this instance data for the named property
*/
public abstract List<PEInstance> children(String name);
/**
* @return all the children of this instance data with the named property and the named type (for polymorphic
*/
public abstract List<PEInstance> children(String name, String type);
/**
* @return make a child, and append it to existing children (if they exist)
*/
public abstract PEInstance makeChild(String name);
/**
* remove the nominated child from the resource
*/
public abstract void removeChild(PEInstance child);
public enum PEInstanceDataKind {
Resource, Complex, DataType, PrimitiveValue
}
/**
* @return the kind of data behind this profiled node
*/
public abstract PEInstanceDataKind getDataKind();
/**
* @return if dataKind = Resource, get the underlying resource, otherwise an exception
*/
public abstract Resource asResource();
/**
* @return if dataKind = Datatype, get the underlying resource, otherwise an exception
*/
public abstract DataType asDataType();
/**
* @return if dataKind = PrimitiveValue, get the underlying resource, otherwise an exception
*
* Note that this is for e.g. String.value, not String itself
*/
public abstract String getPrimitiveValue();
}

View File

@ -1,29 +0,0 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PEResource extends ProfiledElement {
public PEResource(ProfiledElementBuilder builder, StructureDefinition base, StructureDefinition profile) {
super(builder, profile.getName(), base, base.getSnapshot().getElementFirstRep(), profile, profile.getSnapshot().getElementFirstRep());
}
@Override
public List<String> types() {
List<String> res = new ArrayList<>();
res.add(profileStructure.getType());
return res;
}
@Override
public List<ProfiledElement> children(String type) {
if (children == null) {
children = builder.listChildren(baseStructure, baseDefinition, profileStructure, profiledDefinition, null);
}
return children;
}
}

View File

@ -1,27 +0,0 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.List;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
public class PESlice extends ProfiledElement {
public PESlice(ProfiledElementBuilder builder, String name,
StructureDefinition baseStructure, ElementDefinition baseDefinition,
StructureDefinition profileStructure, ElementDefinition profileDefinition,
ElementDefinition sliceDefinition) {
super(builder, name, baseStructure, baseDefinition, profileStructure, profileDefinition, sliceDefinition);
}
@Override
public List<String> types() {
throw new Error("Not done yet");
}
@Override
public List<ProfiledElement> children(String type) {
throw new Error("Not done yet");
}
}

View File

@ -0,0 +1,45 @@
package org.hl7.fhir.r5.profilemodel;
public class PEType {
private String name;
private String type;
private String url;
public PEType(String name, String type, String url) {
super();
this.name = name;
this.url = url;
this.type = type;
}
/**
* Human presentable name
*
* @return
*/
public String getName() {
return name;
}
/**
* The concrete FHIR type name for the type
*
* @return
*/
public String getType() {
return type;
}
/**
* URL that identifies the type
* @return
*/
public String getUrl() {
return url;
}
@Override
public String toString() {
return (name.equals(type) ? name : name+"->"+type)+"["+url+"]";
}
}

View File

@ -1,216 +0,0 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.List;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
public abstract class ProfiledElement {
protected ProfiledElementBuilder builder;
protected String name;
protected StructureDefinition baseStructure;
protected ElementDefinition baseDefinition;
protected StructureDefinition profileStructure;
protected ElementDefinition profiledDefinition;
protected Base data; // might be null if we're not attached to an instance
protected List<ProfiledElement> children;
protected Object sliceDefinition;
/**
* Don't create one of these directly - always use the public methods on ProfiledElementBuilder
*
* @param builder
* @param baseElement
* @param profiledElement
* @param data
*/
protected ProfiledElement(ProfiledElementBuilder builder, String name, ElementDefinition baseDefinition,
ElementDefinition profiledDefinition, Base data) {
super();
this.builder = builder;
this.name = name;
this.baseDefinition = baseDefinition;
this.profiledDefinition = profiledDefinition;
this.data = data;
}
protected ProfiledElement(ProfiledElementBuilder builder, String name, StructureDefinition base, ElementDefinition baseDefinition,
StructureDefinition profile, ElementDefinition profiledDefinition) {
this.builder = builder;
this.name = name;
this.baseStructure = base;
this.baseDefinition = baseDefinition;
this.profileStructure = profile;
this.profiledDefinition = profiledDefinition;
}
public ProfiledElement(ProfiledElementBuilder builder, String name, StructureDefinition base, ElementDefinition baseDefinition,
StructureDefinition profile, ElementDefinition profiledDefinition, ElementDefinition sliceDefinition) {
this.builder = builder;
this.name = name;
this.baseStructure = base;
this.baseDefinition = baseDefinition;
this.profileStructure = profile;
this.profiledDefinition = profiledDefinition;
this.sliceDefinition = sliceDefinition;
}
/**
* @return The name of the element or slice in the profile (always unique amongst children)
*/
public String name() {
return name;
}
/**
* @return The name of the element in the resource (may be different to the slice name)
*/
public String schemaName() {
return baseDefinition.getName();
}
/**
* @return a list of types. There is usually at least one type; it might be Element, Type, BackboneElement or BackboneType
*
* The following elements don't have types (true primitives): Element.id. Extension.url, PrimitiveType.value
*/
public abstract List<String> types();
/**
* @return The minimum number of repeats allowed
*/
public int min() {
return profiledDefinition.getMin();
}
/**
* @return the maximum number of repeats allowed
*/
public int max() {
return "*".equals(profiledDefinition.getMax()) ? Integer.MAX_VALUE : Integer.parseInt(profiledDefinition.getMax());
}
/**
* @return the definition of the element in the profile (fully populated)
*/
public ElementDefinition definition() {
return profiledDefinition;
}
/**
* @return the definition of the element in the base specification
*/
public ElementDefinition baseDefinition() {
return baseDefinition;
}
/**
* @return the short documentation of the definition (shown in the profile table view)
*/
public String shortDocumentation() {
return profiledDefinition.getShort();
}
/**
* @return the full definition of the element (markdown syntax)
*/
public String documentation() {
return profiledDefinition.getDefinition();
}
/**
* @return if the base definition
*/
public ValueSet expansion() {
throw new NotImplementedException("Not done yet");
}
/**
* @param type - one of the types listed in types()
* @return - the list of children for the nominated type
*
* Note that the children returned from this instance can run off the
* end of the data provided, and then inDataMode() is false
*
* Warning: profiles and resources can be recursive; you can't iterate this tree until it you get
* to the leaves because you will never get to a child that doesn't have children
*
*/
public abstract List<ProfiledElement> children(String type);
//
// // -- instance data ---------------------------------------
//
// /**
// * true if the profiled element is part of a tree built based on
// * a resource
// */
// public void inDataMode() {
// throw new NotImplementedException("Not done yet");
// }
//
// /**
// * return a list of profiled elements that are instances of
// * of this element - these are attached to acual underlying data in the resource
// * @return
// */
// public List<ProfiledElement> instances() {
// throw new NotImplementedException("Not done yet");
// };
//
// /**
// * Create a new instance of data that conforms to this profiled element
// *
// * @return
// */
// public ProfiledElement addInstance(){
// throw new NotImplementedException("Not done yet");
// }
//
// /**
// * @return true if this element can have a primitive value
// *
// * Note that an element can have extensions as well as a value, so that doesn't mean it can't have children
// */
// public boolean canHavePrimitiveValue() {
// throw new NotImplementedException("Not done yet");
// }
//
// /**
// * @return true if this element has a primitive value
// *
// * Note that an element can have extensions as well as a value, so that doesn't mean it can't have children
// */
// public boolean hasPrimitiveValue() {
// throw new NotImplementedException("Not done yet");
// }
//
// /**
// * @return true if this element has a primitive value
// *
// * Note that an element can have extensions as well as a value, so that doesn't mean it can't have children
// */
// public String getPrimitiveValue() {
// throw new NotImplementedException("Not done yet");
// }
//
// /**
// * @return true if this element has a primitive value
// *
// * Note that an element can have extensions as well as a value, so that doesn't mean it can't have children
// */
// public String setPrimitiveValue() {
// throw new NotImplementedException("Not done yet");
// }
@Override
public String toString() {
return name+"("+schemaName()+"):"+types().toString()+" ["+min()+":"+(max() == Integer.MAX_VALUE ? "*" : max() )+"] \""+shortDocumentation()+"\"";
}
}

View File

@ -1,221 +0,0 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ResourceFactory;
import org.hl7.fhir.r5.model.StructureDefinition;
public class ProfiledElementBuilder {
private IWorkerContext context;
private ProfileUtilities pu;
public ProfiledElementBuilder(IWorkerContext context) {
super();
this.context = context;
pu = new ProfileUtilities(context, null, null);
}
/**
* Given a profile, return a tree of elements in the profile model. This builds the profile model
* for the latest version of the nominated profile
*
* THe tree of elements in the profile model is different to the the base resource:
* - some elements are removed (max = 0)
* - extensions are turned into named elements
* - slices are turned into named elements
* - element properties - doco, cardinality, binding etc is updated for what the profile says
*
* When built with this method, the profile element can't have instance data
*
* Warning: profiles and resources are recursive; you can't iterate this tree until it you get
* to the leaves because there are nodes that don't terminate (extensions have extensions)
*
*/
public ProfiledElement buildProfileElement(String url) {
StructureDefinition profile = getProfile(url);
if (profile == null) {
throw new DefinitionException("Unable to find profile for URL '"+url+"'");
}
StructureDefinition base = context.fetchTypeDefinition(profile.getType());
if (base == null) {
throw new DefinitionException("Unable to find base type '"+profile.getType()+"' for URL '"+url+"'");
}
return new PEResource(this, base, profile);
}
/**
* Given a profile, return a tree of elements in the profile model. This builds the profile model
* for the nominated version of the nominated profile
*
* THe tree of elements in the profile model is different to the the base resource:
* - some elements are removed (max = 0)
* - extensions are turned into named elements
* - slices are turned into named elements
* - element properties - doco, cardinality, binding etc is updated for what the profile says
*
* When built with this method, the profile element can't have instance data
*
* Warning: profiles and resources can be recursive; you can't iterate this tree until it you get
* to the leaves because you will never get to a child that doesn't have children
*
*/
public ProfiledElement buildProfileElement(String url, String version) {
StructureDefinition profile = getProfile(url, version);
if (profile == null) {
throw new DefinitionException("Unable to find profile for URL '"+url+"'");
}
StructureDefinition base = context.fetchTypeDefinition(profile.getType());
if (base == null) {
throw new DefinitionException("Unable to find base type '"+profile.getType()+"' for URL '"+url+"'");
}
return new PEResource(this, base, profile);
}
/**
* Given a profile, return a tree of elements in the profile model with matching instance data.
* This builds the profile model for the latest version of the nominated profile and matches
* the data in the resource against the profile. Data can be added or read from the profile element
*
* THe tree of elements in the profile model is different to the the base resource:
* - some elements are removed (max = 0)
* - extensions are turned into named elements
* - slices are turned into named elements
* - element properties - doco, cardinality, binding etc is updated for what the profile says
*
* When built with this method, the profile element can't have instance data
*
* Warning: profiles and resources can be recursive; you can't iterate this tree until it you get
* to the leaves because you will never get to a child that doesn't have children
*
*/
public ProfiledElement buildProfileElement(String url, Resource resource) {
throw new NotImplementedException("NOt done yet");
}
/**
* Given a profile, return a tree of elements in the profile model with matching instance data.
* This builds the profile model for the nominated version of the nominated profile and matches
* the data in the resource against the profile. Data can be added or read from the profile element
*
* THe tree of elements in the profile model is different to the the base resource:
* - some elements are removed (max = 0)
* - extensions are turned into named elements
* - slices are turned into named elements
* - element properties - doco, cardinality, binding etc is updated for what the profile says
*
* When built with this method, the profile element can't have instance data
*
*/
public ProfiledElement buildProfileElement(String url, String version, Resource resource) {
throw new NotImplementedException("NOt done yet");
}
/**
* Given a profile, construct an empty resource of the type being profiled (to use as input
* to the buildProfileElement method
*
* No version, because the version doesn't change the type of the resource
*/
public Resource makeProfileBase(String url) {
StructureDefinition profile = getProfile(url);
return ResourceFactory.createResource(profile.getType());
}
// -- methods below here are only used internally to the package
private StructureDefinition getProfile(String url) {
return context.fetchResource(StructureDefinition.class, url);
}
private StructureDefinition getProfile(String url, String version) {
return context.fetchResource(StructureDefinition.class, url, version);
}
protected List<ProfiledElement> listChildren(StructureDefinition baseStructure, ElementDefinition baseDefinition,
StructureDefinition profileStructure, ElementDefinition profiledDefinition, TypeRefComponent t, CanonicalType u) {
// TODO Auto-generated method stub
return null;
}
protected List<ProfiledElement> listChildren(StructureDefinition baseStructure, ElementDefinition baseDefinition, StructureDefinition profileStructure, ElementDefinition profileDefinition, TypeRefComponent t) {
if (profileDefinition.getType().size() == 1 || (!profileDefinition.getPath().contains("."))) {
assert profileDefinition.getType().size() != 1 || profileDefinition.getType().contains(t);
List<ProfiledElement> res = new ArrayList<>();
StructureDefinition profile = profileStructure;
List<ElementDefinition> list = pu.getChildList(profile, profileDefinition);
if (list.size() == 0) {
profile = t.hasProfile() ? context.fetchResource(StructureDefinition.class, t.getProfile().get(0).getValue()) : context.fetchTypeDefinition(t.getWorkingCode());
list = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep());
}
if (list.size() > 0) {
StructureDefinition base = baseStructure;
List<ElementDefinition> blist = pu.getChildList(baseStructure, baseDefinition);
if (blist.size() == 0) {
base = context.fetchTypeDefinition(t.getWorkingCode());
blist = pu.getChildList(base, base.getSnapshot().getElementFirstRep());
}
int i = 0;
while (i < list.size()) {
ElementDefinition defn = list.get(i);
if (defn.hasSlicing()) {
if (defn.getSlicing().getRules() != SlicingRules.CLOSED) {
res.add(new PEElement(this, base, getByName(blist, defn), profileStructure, defn));
}
i++;
while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) {
StructureDefinition ext = getExtensionDefinition(list.get(i));
if (ext != null) {
res.add(new PEExtension(this, list.get(i).getSliceName(), baseStructure, getByName(blist, defn), profileStructure, list.get(i), defn, ext));
} else {
res.add(new PESlice(this, list.get(i).getSliceName(), baseStructure, getByName(blist, defn), profileStructure, list.get(i), defn));
}
i++;
}
} else {
res.add(new PEElement(this, base, getByName(blist, defn), profileStructure, defn));
i++;
}
}
}
return res;
} else {
throw new DefinitionException("not done yet");
}
}
private StructureDefinition getExtensionDefinition(ElementDefinition ed) {
if ("Extension".equals(ed.getTypeFirstRep().getWorkingCode()) && ed.getTypeFirstRep().getProfile().size() == 1) {
return context.fetchResource(StructureDefinition.class, ed.getTypeFirstRep().getProfile().get(0).asStringValue());
} else {
return null;
}
}
private ElementDefinition getByName(List<ElementDefinition> blist, ElementDefinition defn) {
for (ElementDefinition ed : blist) {
if (ed.getName().equals(defn.getName())) {
return ed;
}
}
return null;
}
}

View File

@ -4,8 +4,9 @@ import java.io.IOException;
import java.util.List;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.profilemodel.ProfiledElement;
import org.hl7.fhir.r5.profilemodel.ProfiledElementBuilder;
import org.hl7.fhir.r5.profilemodel.PEDefinition;
import org.hl7.fhir.r5.profilemodel.PEType;
import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.test.utils.TestPackageLoader;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
@ -16,26 +17,37 @@ import org.junit.jupiter.api.Test;
public class ProfiledElementTests {
private IWorkerContext ctxt;
public void load() throws IOException {
if (ctxt == null) {
ctxt = TestingUtilities.getSharedWorkerContext();
FilesystemPackageCacheManager pc = new FilesystemPackageCacheManager(true);
NpmPackage npm = pc.loadPackage("hl7.fhir.us.core", "5.0.0");
ctxt.loadFromPackage(npm, new TestPackageLoader(new String[] { "StructureDefinition" }));
}
}
@Test
public void testPatientCore() throws IOException {
IWorkerContext ctxt = TestingUtilities.getSharedWorkerContext();
FilesystemPackageCacheManager pc = new FilesystemPackageCacheManager(true);
NpmPackage npm = pc.loadPackage("hl7.fhir.us.core", "5.0.0");
ctxt.loadFromPackage(npm, new TestPackageLoader(new String[] { "StructureDefinition" }));
public void testUSPatientCore() throws IOException {
load();
ProfiledElement pe = new ProfiledElementBuilder(ctxt).buildProfileElement("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient");
PEDefinition pe = new PEBuilder(ctxt, true).buildPEDefinition("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient");
Assertions.assertEquals("USCorePatientProfile", pe.name());
Assertions.assertEquals("Patient", pe.schemaName());
Assertions.assertEquals(0, pe.min());
Assertions.assertEquals(Integer.MAX_VALUE, pe.max());
Assertions.assertEquals("Patient", pe.types().get(0));
Assertions.assertEquals("Patient", pe.types().get(0).getType());
Assertions.assertEquals("USCorePatientProfile", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("Information about an individual or animal receiving health care services", pe.shortDocumentation());
Assertions.assertEquals("\\-", pe.documentation());
List<ProfiledElement> children = pe.children("Patient");
List<PEDefinition> children = pe.children("Patient");
Assertions.assertEquals(28, children.size());
pe = children.get(9);
@ -43,7 +55,7 @@ public class ProfiledElementTests {
Assertions.assertEquals("extension", pe.schemaName());
Assertions.assertEquals(0, pe.min());
Assertions.assertEquals(1, pe.max());
Assertions.assertEquals("code", pe.types().get(0));
Assertions.assertEquals("code", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("Extension", pe.shortDocumentation());
@ -55,7 +67,7 @@ public class ProfiledElementTests {
Assertions.assertEquals("extension", pe.schemaName());
Assertions.assertEquals(0, pe.min());
Assertions.assertEquals(1, pe.max());
Assertions.assertEquals("Extension", pe.types().get(0));
Assertions.assertEquals("Extension", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("US Core ethnicity Extension", pe.shortDocumentation());
@ -67,14 +79,14 @@ public class ProfiledElementTests {
Assertions.assertEquals("identifier", pe.schemaName());
Assertions.assertEquals(1, pe.min());
Assertions.assertEquals(Integer.MAX_VALUE, pe.max());
Assertions.assertEquals("Identifier", pe.types().get(0));
Assertions.assertEquals("Identifier", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("An identifier for this patient", pe.shortDocumentation());
Assertions.assertEquals("An identifier for this patient.", pe.documentation());
List<ProfiledElement> iChildren = pe.children("Identifier");
List<PEDefinition> iChildren = pe.children("http://hl7.org/fhir/StructureDefinition/Identifier");
Assertions.assertEquals(8, iChildren.size());
@ -84,13 +96,13 @@ public class ProfiledElementTests {
Assertions.assertEquals("use", pe.schemaName());
Assertions.assertEquals(0, pe.min());
Assertions.assertEquals(1, pe.max());
Assertions.assertEquals("code", pe.types().get(0));
Assertions.assertEquals("code", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("usual | official | temp | secondary | old (If known)", pe.shortDocumentation());
Assertions.assertEquals("The purpose of this identifier.", pe.documentation());
iChildren = pe.children("code");
iChildren = pe.children("http://hl7.org/fhir/StructureDefinition/code");
Assertions.assertEquals(3, iChildren.size());
pe = iChildren.get(2);
@ -121,38 +133,66 @@ public class ProfiledElementTests {
Assertions.assertEquals("extension", pe.schemaName());
Assertions.assertEquals(0, pe.min());
Assertions.assertEquals(Integer.MAX_VALUE, pe.max());
Assertions.assertEquals("Extension", pe.types().get(0));
Assertions.assertEquals("Extension", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("Additional content defined by implementations", pe.shortDocumentation());
Assertions.assertEquals("May be used to represent additional information that is not part of the basic definition of the resource. To make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", pe.documentation());
iChildren = pe.children("Extension");
iChildren = pe.children("http://hl7.org/fhir/StructureDefinition/Extension");
Assertions.assertEquals(4, iChildren.size());
pe = iChildren.get(1);
Assertions.assertEquals("extension", pe.name());
Assertions.assertEquals("extension", pe.schemaName());
Assertions.assertEquals(0, pe.min());
Assertions.assertEquals(Integer.MAX_VALUE, pe.max());
Assertions.assertEquals("Extension", pe.types().get(0));
Assertions.assertEquals("Extension", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("Additional content defined by implementations", pe.shortDocumentation());
Assertions.assertEquals("May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", pe.documentation());
iChildren = pe.children("Extension");
iChildren = pe.children("http://hl7.org/fhir/StructureDefinition/Extension");
Assertions.assertEquals(4, iChildren.size());
pe = iChildren.get(1);
Assertions.assertEquals("extension", pe.name());
Assertions.assertEquals("extension", pe.schemaName());
Assertions.assertEquals(0, pe.min());
Assertions.assertEquals(Integer.MAX_VALUE, pe.max());
Assertions.assertEquals("Extension", pe.types().get(0));
Assertions.assertEquals("Extension", pe.types().get(0).getName());
Assertions.assertNotNull(pe.definition());
Assertions.assertNotNull(pe.baseDefinition());
Assertions.assertEquals("Additional content defined by implementations", pe.shortDocumentation());
Assertions.assertEquals("May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", pe.documentation());
}
@Test
public void dumpUSPatientCore() throws IOException {
load();
PEDefinition pe = new PEBuilder(ctxt, false).buildPEDefinition("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient");
dump(pe, "");
}
private void dump(PEDefinition pe, String indent) {
System.out.println(indent+pe.toString());
if (!pe.isRecursing() && indent.length() < 20) {
if (pe.types().size() == 1) {
for (PEDefinition p : pe.children(pe.types().get(0).getUrl())) {
dump(p, indent+" ");
}
} else {
for (PEType t : pe.types()) {
System.out.println(indent+" +type:"+t.getName());
for (PEDefinition p : pe.children(t.getUrl())) {
dump(p, indent+" ");
}
}
}
}
}
}