finish R5 code generator

This commit is contained in:
Grahame Grieve 2019-12-29 09:44:23 +11:00
parent 1131ac236e
commit f262236b87
7 changed files with 168 additions and 44 deletions

View File

@ -364,45 +364,45 @@ public class XmlParser extends XmlParserBase {
throw new IOException("prefix == null!");
} else if (xpp == null) {
throw new IOException("xpp == null!");
} else if (xpp.getName().equals(prefix+"date")) {
} else if (xpp.getName().equals(prefix+"Date")) {
return parseDate(xpp);
} else if (xpp.getName().equals(prefix+"dateTime")) {
} else if (xpp.getName().equals(prefix+"DateTime")) {
return parseDateTime(xpp);
} else if (xpp.getName().equals(prefix+"code")) {
} else if (xpp.getName().equals(prefix+"Code")) {
return parseCode(xpp);
} else if (xpp.getName().equals(prefix+"string")) {
} else if (xpp.getName().equals(prefix+"String")) {
return parseString(xpp);
} else if (xpp.getName().equals(prefix+"integer")) {
} else if (xpp.getName().equals(prefix+"Integer")) {
return parseInteger(xpp);
} else if (xpp.getName().equals(prefix+"integer64")) {
} else if (xpp.getName().equals(prefix+"Integer64")) {
return parseInteger64(xpp);
} else if (xpp.getName().equals(prefix+"oid")) {
} else if (xpp.getName().equals(prefix+"Oid")) {
return parseOid(xpp);
} else if (xpp.getName().equals(prefix+"canonical")) {
} else if (xpp.getName().equals(prefix+"Canonical")) {
return parseCanonical(xpp);
} else if (xpp.getName().equals(prefix+"uri")) {
} else if (xpp.getName().equals(prefix+"Uri")) {
return parseUri(xpp);
} else if (xpp.getName().equals(prefix+"uuid")) {
} else if (xpp.getName().equals(prefix+"Uuid")) {
return parseUuid(xpp);
} else if (xpp.getName().equals(prefix+"url")) {
} else if (xpp.getName().equals(prefix+"Url")) {
return parseUrl(xpp);
} else if (xpp.getName().equals(prefix+"instant")) {
} else if (xpp.getName().equals(prefix+"Instant")) {
return parseInstant(xpp);
} else if (xpp.getName().equals(prefix+"boolean")) {
} else if (xpp.getName().equals(prefix+"Boolean")) {
return parseBoolean(xpp);
} else if (xpp.getName().equals(prefix+"base64Binary")) {
} else if (xpp.getName().equals(prefix+"Base64Binary")) {
return parseBase64Binary(xpp);
} else if (xpp.getName().equals(prefix+"unsignedInt")) {
} else if (xpp.getName().equals(prefix+"UnsignedInt")) {
return parseUnsignedInt(xpp);
} else if (xpp.getName().equals(prefix+"markdown")) {
} else if (xpp.getName().equals(prefix+"Markdown")) {
return parseMarkdown(xpp);
} else if (xpp.getName().equals(prefix+"time")) {
} else if (xpp.getName().equals(prefix+"Time")) {
return parseTime(xpp);
} else if (xpp.getName().equals(prefix+"id")) {
} else if (xpp.getName().equals(prefix+"Id")) {
return parseId(xpp);
} else if (xpp.getName().equals(prefix+"positiveInt")) {
} else if (xpp.getName().equals(prefix+"PositiveInt")) {
return parsePositiveInt(xpp);
} else if (xpp.getName().equals(prefix+"decimal")) {
} else if (xpp.getName().equals(prefix+"Decimal")) {
return parseDecimal(xpp);
{{parse-type-prefix}}
} else {

View File

@ -14,9 +14,10 @@ To test the generation:
* make sure that project is not in the build path for the generator itself, which depends on the production R5 code
* run the generation
* refresh etc and make sure that the compiler is happy
* copy the JUnit tests ...
* copy the JUnit tests RoundTripTests into the copy project, update the constants, and execute it
* check all the tests pass, and inspect a sampling of the results for consistency
Configuring the Generation
Configuring the Generation Output
The most common reason to alter the generation is to add additional utility routines/enhanceements to the generated classes.
To do this, edit on the one of the templates in the configuration directory - xx.java, where xx is the class name (may include

View File

@ -43,6 +43,8 @@ import org.hl7.fhir.core.generator.analysis.TypeInfo;
import org.hl7.fhir.core.generator.engine.Definitions;
import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Enumerations;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
@ -167,9 +169,10 @@ public class JavaParserJsonGenerator extends JavaBaseGenerator {
parser.append(" protected void parse"+upFirst(tn).replace(".", "")+"Properties(JsonObject json, "+tn+" res) throws IOException, FHIRFormatError {\r\n");
parser.append(" parse"+analysis.getAncestor().getName()+"Properties(json, res);\r\n");
for (ElementDefinition e : analysis.getRootType().getChildren()) {
genElementParser(analysis, analysis.getRootType(), e, bUseOwner, null);
if (!analysis.isInterface()) {
for (ElementDefinition e : analysis.getRootType().getChildren()) {
genElementParser(analysis, analysis.getRootType(), e, bUseOwner, null);
}
}
parser.append(" }\r\n\r\n");
}
@ -187,10 +190,11 @@ public class JavaParserJsonGenerator extends JavaBaseGenerator {
String prsr = null;
String aprsr = null;
String anprsr = null;
EnumInfo ei = null;
String en = null;
if (ed.hasUserData("java.enum")) {
EnumInfo ei = (EnumInfo) ed.getUserData("java.enum"); // getCodeListType(cd.getBinding());
ei = (EnumInfo) ed.getUserData("java.enum"); // getCodeListType(cd.getBinding());
ValueSet vs = ei.getValueSet();
String en;
if (vs.hasUserData("shared")) {
en = "Enumerations."+ei.getName();
} else {
@ -238,7 +242,16 @@ public class JavaParserJsonGenerator extends JavaBaseGenerator {
parser.append(" if (json.has(\""+name+"\")) {\r\n");
parser.append(" JsonArray array = json.getAsJsonArray(\""+name+"\");\r\n");
parser.append(" for (int i = 0; i < array.size(); i++) {\r\n");
parser.append(" res.get"+upFirst(name)+"().add("+aprsr+");\r\n");
parser.append(" if (array.get(i).isJsonNull()) {\r\n");
if (en == null) {
parser.append(" res.get"+upFirst(name)+"().add(new "+tn+"Type());\r\n");
} else {
parser.append(" res.get"+upFirst(name)+"().add(new Enumeration<"+en+">(new "+en+"EnumFactory(), "+en+".NULL));\r\n");
}
parser.append(" } else {;\r\n");
parser.append(" res.get"+upFirst(name)+"().add("+aprsr+");\r\n");
parser.append(" }\r\n");
parser.append(" }\r\n");
parser.append(" };\r\n");
parser.append(" if (json.has(\"_"+name+"\")) {\r\n");
@ -300,8 +313,10 @@ public class JavaParserJsonGenerator extends JavaBaseGenerator {
composer.append(" protected void compose"+tn+"Properties("+tn+" element) throws IOException {\r\n");
composer.append(" compose"+analysis.getAncestor().getName()+"Properties(element);\r\n");
for (ElementDefinition e : analysis.getRootType().getChildren()) {
genElementComposer(analysis, analysis.getRootType(), e, null);
if (!analysis.isInterface()) {
for (ElementDefinition e : analysis.getRootType().getChildren()) {
genElementComposer(analysis, analysis.getRootType(), e, null);
}
}
composer.append(" }\r\n\r\n");
}

View File

@ -175,10 +175,12 @@ public class JavaParserXmlGenerator extends JavaBaseGenerator {
String tn = ti.getName();
parser.append(" protected boolean parse"+upFirst(tn).replace(".", "")+"Content(int eventType, XmlPullParser xpp, "+tn+" res) throws XmlPullParserException, IOException, FHIRFormatError {\r\n");
boolean first = true;
for (ElementDefinition ed : ti.getChildren()) {
if (!ed.hasRepresentation(PropertyRepresentation.XMLATTR)) {
genElement(analysis, ti, ed, null, first);
first = false;
if (!analysis.isInterface()) {
for (ElementDefinition ed : ti.getChildren()) {
if (!ed.hasRepresentation(PropertyRepresentation.XMLATTR)) {
genElement(analysis, ti, ed, null, first);
first = false;
}
}
}
if (!first)
@ -279,6 +281,13 @@ public class JavaParserXmlGenerator extends JavaBaseGenerator {
} else {
composer.append(" composeElementAttributes(element);\r\n");
}
for (ElementDefinition ed : ti.getChildren()) {
if (ed.hasRepresentation(PropertyRepresentation.XMLATTR)) {
composer.append(" if (element.has"+upFirst(getElementName(ed.getName(), true))+"Element())\r\n");
composer.append(" xml.attribute(\""+ed.getName()+"\", element.get"+upFirst(getElementName(ed.getName(), true))+"Element().getValue());\r\n");
}
}
composer.append(" xml.enter(FHIR_NS, name);\r\n");
composer.append(" compose"+pfx+tn+"Elements(element);\r\n");
composer.append(" composeElementClose(element);\r\n");
@ -302,10 +311,12 @@ public class JavaParserXmlGenerator extends JavaBaseGenerator {
composer.append(" protected void compose"+tn+"Elements("+tn+" element) throws IOException {\r\n");
composer.append(" compose"+ti.getAncestorName()+"Elements(element);\r\n");
for (ElementDefinition ed : ti.getChildren()) {
if (!ed.hasRepresentation(PropertyRepresentation.XMLATTR)) {
genElementCompose(analysis, ti, ed, null);
if (!analysis.isInterface()) {
for (ElementDefinition ed : ti.getChildren()) {
if (!ed.hasRepresentation(PropertyRepresentation.XMLATTR)) {
genElementCompose(analysis, ti, ed, null);
}
}
}
composer.append(" }\r\n\r\n");

View File

@ -1858,16 +1858,24 @@ private void generatePropertyMaker(Analysis analysis, TypeInfo ti, String indent
* getXXXFirstRep() for repeatable element
*/
if (!"DomainResource".equals(className)) {
jdoc(indent, "@return The first repetition of repeating field {@link #"+getElementName(e.getName(), true)+"}, creating it if it does not already exist");
jdoc(indent, "@return The first repetition of repeating field {@link #"+getElementName(e.getName(), true)+"}, creating it if it does not already exist {3}");
write(indent+"public "+tn+" get"+getTitle(getElementName(e.getName(), false))+"FirstRep() { \r\n");
write(indent+" if (get"+getTitle(getElementName(e.getName(), false))+"().isEmpty()) {\r\n");
if (e.unbounded()) {
write(indent+" if (get"+getTitle(getElementName(e.getName(), false))+"().isEmpty()) {\r\n");
} else {
write(indent+" if ("+getElementName(e.getName(), false)+" == null) {\r\n");
}
if ((definitions.hasPrimitiveType(e.typeSummary()))) {
write(indent+" add" + getTitle(getElementName(e.getName(), false)) + "Element();\r\n");
} else {
write(indent+" add" + getTitle(getElementName(e.getName(), false)) + "();\r\n");
}
write(indent+" }\r\n");
write(indent+" return get"+getTitle(getElementName(e.getName(), false))+"().get(0);\r\n");
if (e.unbounded()) {
write(indent+" return get"+getTitle(getElementName(e.getName(), false))+"().get(0);\r\n");
} else {
write(indent+" return "+getElementName(e.getName(), false)+";\r\n");
}
write(indent+"}\r\n\r\n");
}
@ -2087,7 +2095,7 @@ private void generatePropertyMaker(Analysis analysis, TypeInfo ti, String indent
* getXXXFirstRep() for repeatable element
*/
if (!"DomainResource".equals(ti.getName())) {
jdoc(indent, "@return The first repetition of repeating field {@link #"+getElementName(e.getName(), true)+"}, creating it if it does not already exist");
jdoc(indent, "@return The first repetition of repeating field {@link #"+getElementName(e.getName(), true)+"}, creating it if it does not already exist {1}");
write(indent+"public abstract "+tn+" get"+getTitle(getElementName(e.getName(), false))+"FirstRep(); \r\n");
}
}
@ -2272,7 +2280,7 @@ private void generatePropertyMaker(Analysis analysis, TypeInfo ti, String indent
* getXXXFirstRep() for repeatable element
*/
if (!"DomainResource".equals(className)) {
jdoc(indent, "@return The first repetition of repeating field {@link #"+getElementName(e.getName(), true)+"}, creating it if it does not already exist");
jdoc(indent, "@return The first repetition of repeating field {@link #"+getElementName(e.getName(), true)+"}, creating it if it does not already exist {2}");
write(indent+"public "+tn+" get"+getTitle(getElementName(e.getName(), false))+"FirstRep() { \r\n");
write(indent+" throw new Error(\"The resource type \\\""+analysis.getName()+"\\\" does not implement the property \\\""+e.getName()+"\\\"\");\r\n");
write(indent+"}\r\n");

View File

@ -58,7 +58,7 @@ public class JavaCoreGenerator {
long start = System.currentTimeMillis();
Date date = new Date();
String ap = Utilities.path(src, "src", "main", "resources");
String ap = Utilities.path(src);
System.out.println("Load Configuration from "+ap);
Configuration config = new Configuration(ap);

View File

@ -0,0 +1,89 @@
package org.hl7.fhir.core.generator.tests;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
@RunWith(Parameterized.class)
public class RoundTripTests {
private static final String EXAMPLES_DIR = "R:\\fhir\\publish\\examples";
@Parameters(name = "{index}: file {0}")
public static Iterable<Object[]> data() throws ParserConfigurationException, SAXException, IOException {
File dir = new File(EXAMPLES_DIR);
String[] list = dir.list();
List<Object[]> objects = new ArrayList<Object[]>(list.length);
for (String s : list) {
objects.add(new Object[] { s, s });
}
return objects;
}
private String name;
public RoundTripTests(String name, String unused) {
this.name = name;
}
@Test
public void test() throws FileNotFoundException, IOException {
byte[] src = TextFile.fileToBytes(Utilities.path(EXAMPLES_DIR, name));
Resource r = new XmlParser().parse(src);
assertNotNull(r);
byte[] cnt = new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(r);
Utilities.createDirectory(output());
save(src, Utilities.path(output(), r.fhirType()+"-"+r.getId()+".src.xml"));
save(cnt, Utilities.path(output(), r.fhirType()+"-"+r.getId()+".cnt.xml"));
cnt = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(r);
save(cnt, Utilities.path(output(), r.fhirType()+"-"+r.getId()+".cnt.json"));
Resource rj = new JsonParser().parse(cnt);
assertNotNull(rj);
if (r instanceof DomainResource) {
((DomainResource) r).setText(null);
}
if (rj instanceof DomainResource) {
((DomainResource) rj).setText(null);
}
assertTrue(r.equalsDeep(rj));
}
private String output() throws IOException {
return Utilities.path("[tmp]", "round-trip");
}
private void save(byte[] src, String path) throws IOException {
File f = new File(path);
if (f.exists()) {
f.delete();
}
TextFile.bytesToFile(src, f);
}
}