diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/renderers/CodeSystemRenderer.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/renderers/CodeSystemRenderer.java index 6ec470092..241da9890 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/renderers/CodeSystemRenderer.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/renderers/CodeSystemRenderer.java @@ -95,9 +95,11 @@ public class CodeSystemRenderer extends TerminologyRenderer { if (cs.hasProperty()) { boolean hasRendered = false; boolean hasURI = false; + boolean hasDescription = false; for (PropertyComponent p : cs.getProperty()) { hasRendered = hasRendered || !p.getCode().equals(ToolingExtensions.getPresentation(p, p.getCodeElement())); hasURI = hasURI || p.hasUri(); + hasDescription = hasDescription || p.hasDescription(); } x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Properties", getContext().getLang())); @@ -111,7 +113,9 @@ public class CodeSystemRenderer extends TerminologyRenderer { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "URL", getContext().getLang())); } tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Type", getContext().getLang())); + if (hasDescription) { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang())); + } for (PropertyComponent p : cs.getProperty()) { tr = tbl.tr(); if (hasRendered) { @@ -122,21 +126,25 @@ public class CodeSystemRenderer extends TerminologyRenderer { tr.td().tx(p.getUri()); } tr.td().tx(p.hasType() ? p.getType().toCode() : ""); + if (hasDescription) { tr.td().tx(p.getDescription()); } } } + } private boolean generateCodeSystemContent(XhtmlNode x, CodeSystem cs, boolean hasExtensions, List maps) throws FHIRFormatError, DefinitionException, IOException { XhtmlNode p = x.para(); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system ")); + p.code().tx(cs.getUrl()); if (cs.getContent() == CodeSystemContentMode.COMPLETE) - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines the following codes", cs.getUrl())+":"); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines the following codes")+":"); else if (cs.getContent() == CodeSystemContentMode.EXAMPLE) - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines some example codes", cs.getUrl())+":"); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines some example codes")+":"); else if (cs.getContent() == CodeSystemContentMode.FRAGMENT ) - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, of which the following are a subset", cs.getUrl())+":"); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines many codes, of which the following are a subset")+":"); else if (cs.getContent() == CodeSystemContentMode.NOTPRESENT ) { - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, but they are not represented here", cs.getUrl())); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines many codes, but they are not represented here")); return false; } XhtmlNode t = x.table( "codes"); @@ -245,7 +253,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { if (cs == null) { return false; } - return CodeSystemUtilities.hasCode(cs, code); + return code == null ? false : CodeSystemUtilities.hasCode(cs, code); } return false; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index fa80788e9..670429e76 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -313,7 +313,7 @@ public class ProfileUtilities extends TranslatingUtilities { } } - public static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdeeee;"; + public static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdf4f4;"; private static final String ROW_COLOR_ERROR = "#ffcccc"; private static final String ROW_COLOR_FATAL = "#ff9999"; private static final String ROW_COLOR_WARNING = "#ffebcc"; @@ -3614,6 +3614,7 @@ public class ProfileUtilities extends TranslatingUtilities { private static final int AGG_IND = 1; private static final int AGG_GR = 2; private static final boolean TABLE_FORMAT_FOR_FIXED_VALUES = false; + public static final String CONSTRAINT_CHAR = "C"; private Cell genTypes(HierarchicalTableGenerator gen, Row r, ElementDefinition e, String profileBaseFileName, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean mustSupportMode) { Cell c = gen.new Cell(); @@ -4365,7 +4366,7 @@ public class ProfileUtilities extends TranslatingUtilities { checkForNoChange(element.getIsSummaryElement(), gc.addStyledText(translate("sd.table", "This element is included in summaries"), "\u03A3", null, null, null, false)); } if (element != null && (hasNonBaseConstraints(element.getConstraint()) || hasNonBaseConditions(element.getCondition()))) { - Piece p = gc.addText("I"); + Piece p = gc.addText(ProfileUtilities.CONSTRAINT_CHAR); p.setHint(translate("sd.table", "This element has or is affected by some invariants ("+listConstraintsAndConditions(element)+")")); p.addStyle(CONSTRAINT_STYLE); p.setReference(context.getSpecUrl()+"conformance-rules.html#constraints"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java index ef3309e8e..bb8d121b2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java @@ -95,9 +95,11 @@ public class CodeSystemRenderer extends TerminologyRenderer { if (cs.hasProperty()) { boolean hasRendered = false; boolean hasURI = false; + boolean hasDescription = false; for (PropertyComponent p : cs.getProperty()) { hasRendered = hasRendered || !p.getCode().equals(ToolingExtensions.getPresentation(p, p.getCodeElement())); hasURI = hasURI || p.hasUri(); + hasDescription = hasDescription || p.hasDescription(); } x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Properties", getContext().getLang())); @@ -111,7 +113,9 @@ public class CodeSystemRenderer extends TerminologyRenderer { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "URL", getContext().getLang())); } tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Type", getContext().getLang())); - tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang())); + if (hasDescription) { + tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang())); + } for (PropertyComponent p : cs.getProperty()) { tr = tbl.tr(); if (hasRendered) { @@ -122,21 +126,25 @@ public class CodeSystemRenderer extends TerminologyRenderer { tr.td().tx(p.getUri()); } tr.td().tx(p.hasType() ? p.getType().toCode() : ""); - tr.td().tx(p.getDescription()); + if (hasDescription) { + tr.td().tx(p.getDescription()); + } } } } private boolean generateCodeSystemContent(XhtmlNode x, CodeSystem cs, boolean hasExtensions, List maps) throws FHIRFormatError, DefinitionException, IOException { XhtmlNode p = x.para(); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system ")); + p.code().tx(cs.getUrl()); if (cs.getContent() == CodeSystemContentMode.COMPLETE) - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines the following codes", cs.getUrl())+":"); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines the following codes")+":"); else if (cs.getContent() == CodeSystemContentMode.EXAMPLE) - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines some example codes", cs.getUrl())+":"); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines some example codes")+":"); else if (cs.getContent() == CodeSystemContentMode.FRAGMENT ) - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, of which the following are a subset", cs.getUrl())+":"); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines many codes, of which the following are a subset")+":"); else if (cs.getContent() == CodeSystemContentMode.NOTPRESENT ) { - p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, but they are not represented here", cs.getUrl())); + p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines many codes, but they are not represented here")); return false; } XhtmlNode t = x.table( "codes"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java index 686b6a936..62c9d5412 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java @@ -77,6 +77,7 @@ import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlComposer; @@ -306,6 +307,9 @@ public class ProfileDrivenRenderer extends ResourceRenderer { return; Base e = ew.getBase(); + if (context.isShowComments()) { + x = renderCommentsSpan(x, e); + } if (e instanceof StringType) x.addText(((StringType) e).getValue()); @@ -436,6 +440,14 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } } + private XhtmlNode renderCommentsSpan(XhtmlNode x, Base e) { + if (e.hasFormatComment()) { + return x.span(null, CommaSeparatedStringBuilder.join(" ", e.getFormatCommentsPre())); + } else { + return x; + } + } + private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails) throws FHIRException, UnsupportedEncodingException, IOException { return displayLeaf(res, ew, defn, x, name, showCodeDetails, true); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java index f7800c7e6..ee4b11ada 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java @@ -27,6 +27,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlParser; import org.hl7.fhir.utilities.xml.XMLUtil; import org.hl7.fhir.utilities.xml.XmlGenerator; import org.w3c.dom.Element; +import org.w3c.dom.Node; public class DOMWrappers { @@ -58,7 +59,15 @@ public class DOMWrappers { } catch (org.hl7.fhir.exceptions.FHIRException e) { throw new FHIRException(e.getMessage(), e); } - return context.getParser().parseType(xml, type); + Node n = element.getPreviousSibling(); + Base ret = context.getParser().parseType(xml, type); + while (n != null && (n.getNodeType() == Node.COMMENT_NODE || n.getNodeType() == Node.TEXT_NODE)) { + if (n.getNodeType() == Node.COMMENT_NODE) { + ret.getFormatCommentsPre().add(0, n.getTextContent()); + } + n = n.getPreviousSibling(); + } + return ret; } @Override diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java index e81bb81d6..57bb8c20d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java @@ -122,6 +122,7 @@ public class RenderingContext { private QuestionnaireRendererMode questionnaireMode = QuestionnaireRendererMode.FORM; private boolean addGeneratedNarrativeHeader = true; + private boolean showComments = false; private FhirPublication targetVersion; private Locale locale; @@ -188,6 +189,7 @@ public class RenderingContext { res.dateYearMonthFormat = dateYearMonthFormat; res.targetVersion = targetVersion; res.locale = locale; + res.showComments = showComments; res.terminologyServiceOptions = terminologyServiceOptions.copy(); return res; @@ -453,8 +455,9 @@ public class RenderingContext { return targetVersion; } - public void setTargetVersion(FhirPublication targetVersion) { + public RenderingContext setTargetVersion(FhirPublication targetVersion) { this.targetVersion = targetVersion; + return this; } public boolean isTechnicalMode() { @@ -473,8 +476,9 @@ public class RenderingContext { } } - public void setLocale(Locale locale) { + public RenderingContext setLocale(Locale locale) { this.locale = locale; + return this; } @@ -494,8 +498,9 @@ public class RenderingContext { return timeZoneId; } - public void setTimeZoneId(ZoneId timeZoneId) { + public RenderingContext setTimeZoneId(ZoneId timeZoneId) { this.timeZoneId = timeZoneId; + return this; } @@ -509,12 +514,14 @@ public class RenderingContext { return this.dateTimeFormat; } - public void setDateTimeFormat(DateTimeFormatter dateTimeFormat) { + public RenderingContext setDateTimeFormat(DateTimeFormatter dateTimeFormat) { this.dateTimeFormat = dateTimeFormat; + return this; } - public void setDateTimeFormatString(String dateTimeFormat) { + public RenderingContext setDateTimeFormatString(String dateTimeFormat) { this.dateTimeFormat = DateTimeFormatter.ofPattern(dateTimeFormat); + return this; } /** @@ -527,52 +534,67 @@ public class RenderingContext { return this.dateFormat; } - public void setDateFormat(DateTimeFormatter dateFormat) { + public RenderingContext setDateFormat(DateTimeFormatter dateFormat) { this.dateFormat = dateFormat; + return this; } - public void setDateFormatString(String dateFormat) { + public RenderingContext setDateFormatString(String dateFormat) { this.dateFormat = DateTimeFormatter.ofPattern(dateFormat); + return this; } public DateTimeFormatter getDateYearFormat() { return dateYearFormat; } - public void setDateYearFormat(DateTimeFormatter dateYearFormat) { + public RenderingContext setDateYearFormat(DateTimeFormatter dateYearFormat) { this.dateYearFormat = dateYearFormat; + return this; } - public void setDateYearFormatString(String dateYearFormat) { + public RenderingContext setDateYearFormatString(String dateYearFormat) { this.dateYearFormat = DateTimeFormatter.ofPattern(dateYearFormat); + return this; } public DateTimeFormatter getDateYearMonthFormat() { return dateYearMonthFormat; } - public void setDateYearMonthFormat(DateTimeFormatter dateYearMonthFormat) { + public RenderingContext setDateYearMonthFormat(DateTimeFormatter dateYearMonthFormat) { this.dateYearMonthFormat = dateYearMonthFormat; + return this; } - public void setDateYearMonthFormatString(String dateYearMonthFormat) { + public RenderingContext setDateYearMonthFormatString(String dateYearMonthFormat) { this.dateYearMonthFormat = DateTimeFormatter.ofPattern(dateYearMonthFormat); + return this; } public ResourceRendererMode getMode() { return mode; } - public void setMode(ResourceRendererMode mode) { + public RenderingContext setMode(ResourceRendererMode mode) { this.mode = mode; + return this; } public boolean isContained() { return contained; } - public void setContained(boolean contained) { + public RenderingContext setContained(boolean contained) { this.contained = contained; + return this; + } + public boolean isShowComments() { + return showComments; + } + public RenderingContext setShowComments(boolean showComments) { + this.showComments = showComments; + return this; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java index 5870f52e6..523538ad4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java @@ -53,10 +53,13 @@ import org.hl7.fhir.r5.model.CodeType; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.DateTimeType; +import org.hl7.fhir.r5.model.DecimalType; import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter; import org.hl7.fhir.r5.model.Identifier; +import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.Meta; +import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.StandardsStatus; @@ -68,7 +71,7 @@ public class CodeSystemUtilities { @Override public int compare(ConceptDefinitionComponent o1, ConceptDefinitionComponent o2) { - return o1.getCode().compareTo(o2.getCode()); + return o1.getCode().compareToIgnoreCase(o2.getCode()); } } @@ -177,6 +180,58 @@ public class CodeSystemUtilities { concept.addProperty().setCode("notSelectable").setValue(new BooleanType(true)); } + public static void setProperty(CodeSystem cs, ConceptDefinitionComponent concept, String code, DataType value) throws FHIRFormatError { + defineProperty(cs, code, propertyTypeForValue(value)); + ConceptPropertyComponent p = getProperty(concept, code); + if (p != null) + p.setValue(value); + else + concept.addProperty().setCode(code).setValue(value); + } + + + private static PropertyType propertyTypeForValue(DataType value) { + if (value instanceof BooleanType) { + return PropertyType.BOOLEAN; + } + if (value instanceof CodeType) { + return PropertyType.CODE; + } + if (value instanceof Coding) { + return PropertyType.CODING; + } + if (value instanceof DateTimeType) { + return PropertyType.DATETIME; + } + if (value instanceof DecimalType) { + return PropertyType.DECIMAL; + } + if (value instanceof IntegerType) { + return PropertyType.INTEGER; + } + if (value instanceof StringType) { + return PropertyType.STRING; + } + throw new Error("Unknown property type "+value.getClass().getName()); + } + + private static void defineProperty(CodeSystem cs, String code, PropertyType pt) { + String url = "http://hl7.org/fhir/concept-properties#"+code; + for (PropertyComponent p : cs.getProperty()) { + if (p.getCode().equals(code)) { + if (!p.getUri().equals(url)) { + throw new Error("URI mismatch for code "+code+" url = "+p.getUri()+" vs "+url); + } + if (!p.getType().equals(pt)) { + throw new Error("Type mismatch for code "+code+" type = "+p.getType()+" vs "+pt); + } + return; + } + } + cs.addProperty().setCode(code).setUri(url).setType(pt).setUri(url); + + } + public static void defineNotSelectableProperty(CodeSystem cs) { defineCodeSystemProperty(cs, "notSelectable", "Indicates that the code is abstract - only intended to be used as a selector for other concepts", PropertyType.BOOLEAN); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java index ac06329ee..1fc5ca3dd 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java @@ -1,5 +1,7 @@ package org.hl7.fhir.r5.terminologies; +import java.util.Collections; +import java.util.Comparator; import java.util.List; /* @@ -47,9 +49,11 @@ import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent; +import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptStatus; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.StandardsStatus; @@ -255,4 +259,18 @@ public class ValueSetUtilities { } + public static class ConceptReferenceComponentSorter implements Comparator { + + @Override + public int compare(ConceptReferenceComponent o1, ConceptReferenceComponent o2) { + return o1.getCode().compareToIgnoreCase(o2.getCode()); + } + } + + + public static void sortInclude(ConceptSetComponent inc) { + Collections.sort(inc.getConcept(), new ConceptReferenceComponentSorter()); + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 939d93026..705a3a449 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -583,7 +583,7 @@ public class FHIRPathEngine { fmt = fmt + " "+worker.formatMessage(I18nConstants.FHIRPATH_LOCATION, location); } if (holder != null) { - return new PathEngineException(fmt, holder.getStart(), holder.toString()); + return new PathEngineException(fmt, holder.getStart(), holder.toString()); } else { return new PathEngineException(fmt); } @@ -5503,10 +5503,11 @@ public class FHIRPathEngine { if (dt == null) { throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(t.getCode(), worker.getOverrideVersionNs()), "getChildTypesByName"); } - sdl.add(dt); + addTypeAndDescendents(sdl, dt, worker.allStructures()); + // also add any descendant types } } else { - sdl.add(sd); + addTypeAndDescendents(sdl, sd, worker.allStructures()); if (type.contains("#")) { tail = type.substring(type.indexOf("#")+1); tail = tail.substring(tail.indexOf(".")); @@ -5596,6 +5597,15 @@ public class FHIRPathEngine { } } + private void addTypeAndDescendents(List sdl, StructureDefinition dt, List types) { + sdl.add(dt); + for (StructureDefinition sd : types) { + if (sd.hasBaseDefinition() && sd.getBaseDefinition().equals(dt.getUrl()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + addTypeAndDescendents(sdl, sd, types); + } + } + } + private void getClassInfoChildTypesByName(String name, TypeDetails result) { if (name.equals("namespace")) { result.addType(TypeDetails.FP_String); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java index 4248ba585..70fa5823b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java @@ -443,7 +443,7 @@ public class QuestionnaireBuilder { if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) { ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains(); cc.setCode(u.getValue().substring(40)); - cc.setSystem("http://hl7.org/fhir/resource-types"); + cc.setSystem("http://hl7.org/fhir/fhir-types"); cc.setDisplay(cc.getCode()); } } @@ -451,7 +451,7 @@ public class QuestionnaireBuilder { ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains(); cc.setCode(t.getWorkingCode()); cc.setDisplay(t.getWorkingCode()); - cc.setSystem("http://hl7.org/fhir/data-types"); + cc.setSystem("http://hl7.org/fhir/fhir-types"); } else for (UriType u : t.getProfile()) { ProfileUtilities pu = new ProfileUtilities(context, null, null); StructureDefinition ps = pu.getProfile(profile, u.getValue()); @@ -459,7 +459,7 @@ public class QuestionnaireBuilder { ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains(); cc.setCode(u.getValue()); cc.setDisplay(ps.getType()); - cc.setSystem("http://hl7.org/fhir/resource-types"); + cc.setSystem("http://hl7.org/fhir/fhir-types"); } } } @@ -489,7 +489,7 @@ public class QuestionnaireBuilder { Coding cc = new Coding(); a.setValue(cc); cc.setCode(u.getValue().substring(40)); - cc.setSystem("http://hl7.org/fhir/resource-types"); + cc.setSystem("http://hl7.org/fhir/fhir-types"); } } } else { @@ -502,10 +502,10 @@ public class QuestionnaireBuilder { if (ps != null) { cc.setCode(t.getProfile().get(0).getValue()); - cc.setSystem("http://hl7.org/fhir/resource-types"); + cc.setSystem("http://hl7.org/fhir/fhir-types"); } else { cc.setCode(t.getWorkingCode()); - cc.setSystem("http://hl7.org/fhir/data-types"); + cc.setSystem("http://hl7.org/fhir/fhir-types"); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java index c6903ad5d..c0dd89535 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java @@ -207,6 +207,9 @@ public class ToolingExtensions { public static final String EXT_EXPAND_RULES = "http://hl7.org/fhir/StructureDefinition/valueset-expand-rules"; public static final String EXT_EXPAND_GROUP = "http://hl7.org/fhir/StructureDefinition/valueset-expand-group"; public static final String EXT_BINDING_ADDITIONAL = "http://hl7.org/fhir/tools/StructureDefinition/additional-binding"; + public static final String EXT_MIN_LENGTH = "http://hl7.org/fhir/StructureDefinition/minLength"; + public static final String EXT_MAX_DECIMALS = "http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces"; + public static final String EXT_MAX_SIZE = "http://hl7.org/fhir/StructureDefinition/maxSize"; // specific extension helpers diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/CommaSeparatedStringBuilder.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/CommaSeparatedStringBuilder.java index c4c917595..2ce6c8095 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/CommaSeparatedStringBuilder.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/CommaSeparatedStringBuilder.java @@ -108,4 +108,12 @@ public class CommaSeparatedStringBuilder { } } + + public static String join(String sep, List list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(sep); + for (String s : list) { + b.append(s); + } + return b.toString(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 439d65e6a..47e2db330 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -381,8 +381,10 @@ public class I18nConstants { public static final String SD_ED_BIND_MULTIPLE_TYPES = "SD_ED_BIND_MULTIPLE_TYPES"; public static final String SD_VALUE_TYPE_IILEGAL = "SD_VALUE_TYPE_IILEGAL"; public static final String SD_VALUE_TYPE_REPEAT_HINT = "SD_VALUE_TYPE_REPEAT_HINT"; + public static final String SD_VALUE_COMPLEX_FIXED = "SD_VALUE_COMPLEX_FIXED"; public static final String SD_VALUE_TYPE_REPEAT_WARNING_DOTNET = "SD_VALUE_TYPE_REPEAT_WARNING_DOTNET"; public static final String SD_NO_TYPES_OR_CONTENTREF = "SD_NO_TYPES_OR_CONTENTREF"; + public static final String SD_ILLEGAL_CHARACTERISTICS = "SD_ILLEGAL_CHARACTERISTICS"; public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG"; public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG"; public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index fb1b82c2b..ca032b05c 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -720,3 +720,5 @@ TX_SERVER_NO_BATCH_RESPONSE = The server return null from a batch validation req BUNDLE_POSSSIBLE_MATCHES = The bundle contains no match for {1} by the rules of Bundle reference resolution, but it has multiple resources that match {0} by resource type and id BUNDLE_BUNDLE_POSSIBLE_MATCH_NO_FU = Entry {0} matches the reference {1} by type and id but it does not match the full target URL {2} by Bundle resolution rules BUNDLE_BUNDLE_POSSIBLE_MATCH_WRONG_FU = Entry {0} matches the reference {1} by type and id but it''s fullUrl {2} does not match the full target URL {3} by Bundle resolution rules +SD_ILLEGAL_CHARACTERISTICS = This element has a {0} but the types to do not make this kind of constraint relevant +SD_VALUE_COMPLEX_FIXED = For the complex type {0}, consider using a pattern rather than a fixed value to avoid over-constraining the instance diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java index ac39d405d..d4fa6951c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorCli.java @@ -255,7 +255,7 @@ public class ValidatorCli { System.out.println("Loading"); // Comment this out because definitions filename doesn't necessarily contain version (and many not even be 14 characters long). // Version gets spit out a couple of lines later after we've loaded the context - String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); + String definitions = "dev".equals(cliContext.getSv()) ? "hl7.fhir.r5.core#current" : VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); ValidationEngine validator = validationService.initializeValidator(cliContext, definitions, tt); tts.end(); switch (cliContext.getMode()) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 4188ebdd7..903f6b154 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -950,7 +950,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat done = true; } } - hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, done, I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system); + if (!isAllowExamples() || !Utilities.startsWithInList(system, "http://example.org", "https://example.org")) { + hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, done, I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system); + } return true; } catch (Exception e) { return true; @@ -3205,6 +3207,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (pol == ReferenceValidationPolicy.CHECK_VALID) { // todo.... } + + // todo: if the content is a resource, check that Reference.type is describing a resource } private boolean isSuspiciousReference(String url) { @@ -4994,11 +4998,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat type = checkDefn.getType().get(0).getWorkingCode(); String stype = ei.getElement().fhirType(); if (checkDefn.isChoice() && !stype.equals(type)) { - if ("Extension".equals(profile.getType())) { - // error will be raised elsewhere - } else { + if (extensionUrl != null && !isAbsolute(extensionUrl)) { rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); + } else if (!isAbstractType(type) && !"Extension".equals(profile.getType())) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), stype.equals(type), I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); } + } else if (!isAbstractType(type)) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), stype.equals(type) || + (Utilities.existsInList(type, "string", "id") && Utilities.existsInList(stype, "string", "id")), // work around a r4 problem with id/string + I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); } // Excluding reference is a kludge to get around versioning issues @@ -5231,6 +5239,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } + private boolean isAbstractType(String type) { + StructureDefinition sd = context.fetchTypeDefinition(type); + return sd != null && sd.getAbstract(); + } + private boolean isResourceAndTypes(ElementDefinition ed) { if (!Utilities.existsInList(ed.getBase().getPath(), "Bundle.entry.resource", "Bundle.entry.response.outcome", "DomainResource.contained", "Parameters.parameter.resource", "Parameters.parameter.part.resource")) { return false; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index f394b17dc..7f95e80c3 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -129,6 +129,8 @@ public class StructureDefinitionValidator extends BaseValidator { boolean typeMustSupport = false; List types = element.getChildrenByName("type"); Set typeCodes = new HashSet<>(); + Set characteristics = new HashSet<>(); + for (Element type : types) { if (hasMustSupportExtension(type)) { typeMustSupport = true; @@ -143,6 +145,12 @@ public class StructureDefinitionValidator extends BaseValidator { } } typeCodes.add(tc); + Set tcharacteristics = new HashSet<>(); + addCharacteristics(tcharacteristics, tc); + characteristics.addAll(tcharacteristics); + if (type.hasChildren("targetProfile")) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), tcharacteristics.contains("has-target") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "targetProfile"); + } // check the stated profile - must be a constraint on the type if (snapshot || sd != null) { validateElementType(errors, type, stack.push(type, -1, null, null), sd, element.getChildValue("path")); @@ -156,6 +164,8 @@ public class StructureDefinitionValidator extends BaseValidator { } } if (element.hasChild("binding")) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("can-bind") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Binding"); + Element binding = element.getNamedChild("binding"); validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, element.getNamedChildValue("path")); } else { @@ -163,6 +173,25 @@ public class StructureDefinitionValidator extends BaseValidator { // String bt = boundType(typeCodes); // hint(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bt == null, I18nConstants.SD_ED_SHOULD_BIND, element.getNamedChildValue("path"), bt); } + if (element.hasChild("maxLength")) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("has-length") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "MaxLength"); + } + if (element.hasExtension(ToolingExtensions.EXT_MIN_LENGTH)) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("has-length") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "MinLength Extension"); + } + if (element.hasChild("minValue")) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("has-range") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "MinValue"); + } + if (element.hasChild("maxValue")) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("has-range") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "MaxValue"); + } + if (element.hasExtension(ToolingExtensions.EXT_MAX_DECIMALS)) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("is-continuous") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Max Decimal Places Extension"); + } + if (element.hasExtension(ToolingExtensions.EXT_MAX_SIZE)) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("has-size") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Max Size"); + } + // in a snapshot, we validate that fixedValue, pattern, and defaultValue, if present, are all of the right type if (snapshot && (element.getIdBase() != null) && (element.getIdBase().contains("."))) { if (rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), !typeCodes.isEmpty() || element.hasChild("contentReference"), I18nConstants.SD_NO_TYPES_OR_CONTENTREF, element.getIdBase())) { @@ -179,7 +208,9 @@ public class StructureDefinitionValidator extends BaseValidator { hint(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_HINT, element.getIdBase(), "fixed"); if (isPrimitiveType(v.fhirType())) { warning(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_WARNING_DOTNET, element.getIdBase(), "fixed"); - } + } else { + warning(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), false, I18nConstants.SD_VALUE_COMPLEX_FIXED, v.fhirType()); + } } v = element.getNamedChild("pattern"); if (v != null) { @@ -194,8 +225,81 @@ public class StructureDefinitionValidator extends BaseValidator { } } - + + private boolean addCharacteristics(Set set, String tc) { + switch (tc) { + case "boolean" : return addCharacteristicsForType(set); + case "integer" : return addCharacteristicsForType(set, "has-range", "has-length"); + case "integer64" : return addCharacteristicsForType(set, "has-range", "has-length"); + case "decimal" :return addCharacteristicsForType(set, "has-range", "is-continuous", "has-length"); + case "base64Binary" : return addCharacteristicsForType(set, "has-size"); + case "instant" : return addCharacteristicsForType(set, "has-range", "is-continuous", "has-length"); + case "string" : return addCharacteristicsForType(set, "has-length", "do-translations"); + case "uri" : return addCharacteristicsForType(set, "has-length", "can-bind"); + case "date" :return addCharacteristicsForType(set, "has-range", "has-length"); + case "dateTime" : return addCharacteristicsForType(set, "has-range", "is-continuous", "has-length"); + case "time" :return addCharacteristicsForType(set, "has-range", "is-continuous", "has-length"); + case "canonical" :return addCharacteristicsForType(set, "has-target", "has-length"); + case "code" :return addCharacteristicsForType(set, "has-length", "can-bind"); + case "id" :return addCharacteristicsForType(set, "has-length"); + case "markdown" :return addCharacteristicsForType(set, "do-translations"); + case "oid" :return addCharacteristicsForType(set, "has-length", "can-bind"); + case "positiveInt" :return addCharacteristicsForType(set, "has-range", "has-length"); + case "unsignedInt" :return addCharacteristicsForType(set, "has-range", "has-length"); + case "url" :return addCharacteristicsForType(set, "has-length", "can-bind"); + case "uuid" :return addCharacteristicsForType(set, "has-length", "can-bind"); + case "Address" :return addCharacteristicsForType(set, "do-translations"); + case "Age" : return addCharacteristicsForType(set, "has-range", "is-continuous"); + case "Annotation" :return addCharacteristicsForType(set); + case "Attachment" :return addCharacteristicsForType(set, "has-size", "do-translations"); + case "CodeableConcept" :return addCharacteristicsForType(set, "can-bind", "do-translations"); + case "CodeableReference" : return addCharacteristicsForType(set, "has-target", "can-bind", "do-translations"); + case "Coding" : return addCharacteristicsForType(set, "can-bind", "do-translations"); + case "ContactPoint" :return addCharacteristicsForType(set); + case "Count" :return addCharacteristicsForType(set, "has-range"); + case "Distance" :return addCharacteristicsForType(set, "has-range", "is-continuous"); + case "Duration" : return addCharacteristicsForType(set, "has-range", "is-continuous"); + case "HumanName" :return addCharacteristicsForType(set); + case "Identifier" : return addCharacteristicsForType(set); + case "Money" : return addCharacteristicsForType(set, "has-range", "is-continuous"); + case "Period" : return addCharacteristicsForType(set); + case "Quantity" :return addCharacteristicsForType(set, "has-range", "is-continuous", "can-bind", "has-units"); + case "Range" :return addCharacteristicsForType(set, "has-units"); + case "Ratio" :return addCharacteristicsForType(set, "has-units"); + case "RatioRange" : return addCharacteristicsForType(set, "has-units"); + case "Reference" : return addCharacteristicsForType(set, "has-target"); + case "SampledData" :return addCharacteristicsForType(set); + case "Signature" : return addCharacteristicsForType(set); + case "Timing" : return addCharacteristicsForType(set); + case "ContactDetail" :return addCharacteristicsForType(set); + case "Contributor" :return addCharacteristicsForType(set); + case "DataRequirement" :return addCharacteristicsForType(set); + case "Expression" : return addCharacteristicsForType(set); + case "ParameterDefinition" : return addCharacteristicsForType(set); + case "RelatedArtifact" :return addCharacteristicsForType(set); + case "TriggerDefinition" :return addCharacteristicsForType(set); + case "UsageContext" :return addCharacteristicsForType(set); + case "Dosage" : return addCharacteristicsForType(set); + case "Meta" :return addCharacteristicsForType(set); + case "Resource" :return addCharacteristicsForType(set); + case "Extension" :return addCharacteristicsForType(set); + case "Narrative" :return addCharacteristicsForType(set); + case "Element" :return addCharacteristicsForType(set); + case "BackboneElement" :return addCharacteristicsForType(set); + default: + throw new Error("Unhandled data type in addCharacterstics: "+tc); + } + } + + + private boolean addCharacteristicsForType(Set set, String... cl) { + for (String c : cl) { + set.add(c); + } + return true; + } + private boolean isPrimitiveType(String fhirType) { StructureDefinition sd = context.fetchTypeDefinition(fhirType); return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;