001package org.hl7.fhir.r4.profilemodel.gen;
002
003/*
004  Copyright (c) 2011+, HL7, Inc.
005  All rights reserved.
006  
007  Redistribution and use in source and binary forms, with or without modification, \
008  are permitted provided that the following conditions are met:
009  
010   * Redistributions of source code must retain the above copyright notice, this \
011     list of conditions and the following disclaimer.
012   * Redistributions in binary form must reproduce the above copyright notice, \
013     this list of conditions and the following disclaimer in the documentation \
014     and/or other materials provided with the distribution.
015   * Neither the name of HL7 nor the names of its contributors may be used to 
016     endorse or promote products derived from this software without specific 
017     prior written permission.
018  
019  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND \
020  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \
021  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. \
022  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \
023  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \
024  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR \
025  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \
026  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \
027  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \
028  POSSIBILITY OF SUCH DAMAGE.
029  */
030
031import java.io.File;
032import java.io.IOException;
033import java.text.SimpleDateFormat;
034import java.util.List;
035import java.util.Locale;
036import java.util.TimeZone;
037import java.util.ArrayList;
038import java.util.Date;
039
040import org.hl7.fhir.r4.context.IWorkerContext;
041import org.hl7.fhir.r4.model.CodeableConcept;
042import org.hl7.fhir.r4.model.Identifier;
043import org.hl7.fhir.r4.model.Observation;
044import org.hl7.fhir.r4.model.StructureDefinition;
045import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
046import org.hl7.fhir.r4.profilemodel.PEBuilder;
047import org.hl7.fhir.r4.profilemodel.PEBuilder.PEElementPropertiesPolicy;
048import org.hl7.fhir.r4.profilemodel.gen.PECodeGenerator.ExtensionPolicy;
049import org.hl7.fhir.r4.profilemodel.PEDefinition;
050import org.hl7.fhir.r4.profilemodel.PEInstance;
051import org.hl7.fhir.r4.profilemodel.PEType;
052import org.hl7.fhir.utilities.TextFile;
053import org.hl7.fhir.utilities.Utilities;
054
055
056public class PECodeGenerator {
057
058
059  public static final String DEFAULT_DATE() {
060    SimpleDateFormat sdf = new SimpleDateFormat("EEE, MMM d, yyyy HH:mmZ", new Locale("en", "US"));
061    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
062    return sdf.format(new Date());
063  }
064  
065  
066  public enum ExtensionPolicy {
067    None, Complexes, Primitives;
068  }
069  
070  private class PEGenClass {
071    private String name;
072    private String base;
073    private String doco;
074    private String url;
075    private boolean isResource;
076    private StringBuilder fields = new StringBuilder();
077    private StringBuilder load = new StringBuilder();
078    private StringBuilder save = new StringBuilder();
079    private StringBuilder clear = new StringBuilder();
080    private StringBuilder copy = new StringBuilder();
081    private StringBuilder accessors = new StringBuilder();
082    private StringBuilder hash = new StringBuilder();
083    public void genId() {
084      if (isResource) {
085        genField(true, "id", "String", "id", "", false, "");
086        genAccessors(true, false, "id", "String", "", "String", "String", "Id", "Ids", false, "", false);   
087        genLoad(true, false, "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, null);
088        genSave(true, false, "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, false, null);
089        genClear(false, "id");
090      }
091    }
092    public void write(StringBuilder b, String copyright) {
093      w(b);
094      if (copyright != null) {
095        w(b, "/*");
096        w(b, copyright);
097        w(b, " */");
098        w(b);
099      }
100      w(b, "// Generated by the HAPI Java Profile Generator, "+genDate);      
101      w(b);      
102      jdoc(b, doco, 0, true);
103      w(b, "public class "+name+" extends PEGeneratedBase {");
104      w(b);
105      if (url != null) {
106        w(b, "  private static final String CANONICAL_URL = \""+url+"\";");
107        w(b);        
108      }
109      w(b, fields.toString());
110      jdoc(b, "Parameter-less constructor. If you use this, the fixed values won't be filled out - they'll be missing. They'll be filled in if/when you call build, so they won't be missing from the resource, only from this particular object model", 2, true);
111      w(b, "  public "+name+"() {");
112      w(b, "    // todo");
113      w(b, "  }");
114      w(b);
115      if (isResource) {
116        jdoc(b, "Construct an instance of the object, and fill out all the fixed values ", 2, true);
117        w(b, "  public "+name+"(IWorkerContext context) {");
118        w(b, "    workerContext = context;");
119        w(b, "    PEBuilder builder = new PEBuilder(context, PEElementPropertiesPolicy.EXTENSION, true);");
120        w(b, "    PEInstance src = builder.buildPEInstance(CANONICAL_URL, builder.createResource(CANONICAL_URL, false));");
121        w(b, "    load(src);");
122        w(b, "  }");
123        w(b);
124        jdoc(b, "Populate an instance of the object based on this source object ", 2, true);
125        w(b, "  public static "+name+" fromSource(IWorkerContext context, "+base+" source) {");
126        w(b, "    "+name+" theThing = new "+name+"();");
127        w(b, "    theThing.workerContext = context;");
128        w(b, "    PEBuilder builder = new PEBuilder(context, PEElementPropertiesPolicy.EXTENSION, true);");
129        w(b, "    PEInstance src = builder.buildPEInstance(CANONICAL_URL, source);");
130        w(b, "    theThing.load(src);");
131        w(b, "    return theThing;");
132        w(b, "  }");  
133        w(b);
134      } else {
135        jdoc(b, "Used when loading other models ", 2, true);
136        w(b, "  public static "+name+" fromSource(PEInstance source) {");
137        w(b, "    "+name+" theThing = new "+name+"();");
138        w(b, "    theThing.workerContext = source.getContext();");
139        w(b, "    theThing.load(source);");
140        w(b, "    return theThing;");
141        w(b, "  }");  
142      }
143      w(b);
144      w(b, "  public void load(PEInstance src) {");
145      w(b, "    clear();");
146      w(b, load.toString());
147      w(b, "  }");  
148      w(b);
149
150      if (isResource) {
151        jdoc(b, "Build an instance of the object based on this source object ", 2, true);
152        w(b, "  public "+base+" build(IWorkerContext context) {");
153        w(b, "    workerContext = context;");
154        w(b, "    "+base+" theThing = new "+base+"();");
155        w(b, "    PEBuilder builder = new PEBuilder(context, PEElementPropertiesPolicy.EXTENSION, true);");
156        w(b, "    PEInstance tgt = builder.buildPEInstance(CANONICAL_URL, theThing);");      
157        w(b, "    save(tgt, false);");
158        w(b, "    return theThing;");
159        w(b, "  }");
160        w(b);
161        jdoc(b, "Save this profile class into an existing resource (overwriting anything that exists in the profile) ", 2, true);
162        w(b, "  public void save(IWorkerContext context, "+base+" dest, boolean nulls) {");
163        w(b, "    workerContext = context;");
164        w(b, "    PEBuilder builder = new PEBuilder(context, PEElementPropertiesPolicy.EXTENSION, true);");
165        w(b, "    PEInstance tgt = builder.buildPEInstance(CANONICAL_URL, dest);");
166        w(b, "    save(tgt, nulls);");
167        w(b, "  }");
168        w(b);
169      }
170      w(b, "  public void save(PEInstance tgt, boolean nulls) {");
171      w(b, save.toString());
172      w(b, "  }");  
173      w(b);
174      w(b, accessors.toString());
175      w(b);
176      w(b, "  public void clear() {");
177      w(b, clear.toString());
178      w(b, "  }");  
179      w(b);
180      w(b, "}");  
181    }
182
183    private void defineField(PEDefinition source, PEDefinition field) {
184      if (field.types().size() == 1) {
185        StructureDefinition sd = workerContext.fetchTypeDefinition(field.types().get(0).getUrl());
186        if (sd != null) {
187          boolean isPrim = sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
188          boolean isAbstract = sd.getAbstract();
189          String name = field.name().replace("[x]", "");
190          String sname = name;
191          String type = null;
192          String init = "";
193          String ptype = type;
194          if (isPrim) {
195            // todo: are we extension-less?
196            type = Utilities.capitalize(field.types().get(0).getName()+"Type");
197            ptype = getPrimitiveType(sd);
198          } else {
199            type = field.types().get(0).getName();
200          }
201          String ltype = type;
202          if (field.isList()) {
203            ltype = "List<"+type+">";
204            init = "new ArrayList<>()";
205            if (!Utilities.existsInList(name, "contained")) {
206              name = Utilities.pluralize(name, 2);
207            }
208          }
209          String cname = Utilities.capitalize(name);
210          String csname = Utilities.capitalize(sname);
211          String nn = field.min() == 1 ? "// @NotNull" : "";
212          boolean isExtension = field.isExtension();
213          genField(isPrim, name, ptype, ltype, nn, field.isList(), field.shortDocumentation());
214          genAccessors(isPrim, isAbstract, name, type, init, ptype, ltype, cname, csname, field.isList(), field.documentation(), field.fixedValue());   
215          genLoad(isPrim, isAbstract, name, sname, type, init, ptype, ltype, cname, csname, field.isList(), field.fixedValue(), field.types().get(0)); 
216          genSave(isPrim, isAbstract, name, sname, type, init, ptype, ltype, cname, csname, field.isList(), field.fixedValue(), isExtension, field.types().get(0));
217          genClear(field.isList(), name);
218        }
219      } else {
220        // ignoring polymorphics for now
221      }
222
223    }
224
225    private void genClear(boolean list, String name) {
226      if (list) {
227        w(clear, "    "+name+".clear();");        
228      } else {
229        w(clear, "    "+name+" = null;");
230      }
231    }
232    
233    private void genLoad(boolean isPrim, boolean isAbstract, String name, String sname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, PEType typeInfo) {
234      if (isList) {
235        w(load, "    for (PEInstance item : src.children(\""+sname+"\")) {");
236        w(load, "      "+name+".add(("+type+") item.asDataType());");
237        w(load, "    }");
238      } else if (isPrim) {
239        w(load, "    if (src.hasChild(\""+name+"\")) {");
240        if ("CodeType".equals(type)) {
241          // might be code or enum 
242          w(load, "      "+name+" = src.child(\""+name+"\").asDataType().primitiveValue();");
243        } else {
244          w(load, "      "+name+" = (("+type+") src.child(\""+name+"\").asDataType()).getValue();");
245        }
246        w(load, "    }");      
247      } else if (typeInfo != null && typeInfo.getUrl() != null && !typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) {
248        w(load, "    if (src.hasChild(\""+name+"\")) {");
249        w(load, "      "+name+" = "+type+".fromSource(src.child(\""+name+"\"));");
250        w(load, "    }");
251      } else {
252        w(load, "    if (src.hasChild(\""+name+"\")) {");
253        w(load, "      "+name+" = ("+type+") src.child(\""+name+"\").asDataType();");
254        w(load, "    }");
255      }
256    }
257
258    private void genSave(boolean isPrim, boolean isAbstract, String name, String sname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, boolean isExtension, PEType typeInfo) {
259      w(save, "    tgt.clear(\""+sname+"\");");
260      if (isList) {
261        w(save, "    for ("+type+" item : "+name+") {");
262        if (isExtension) {
263          w(save, "      tgt.makeChild(\""+sname+"\").data().setProperty(\"value[x]\", item);");          
264        } else {
265          w(save, "      tgt.addChild(\""+sname+"\", item);");
266        }
267        w(save, "    }");
268      } else if (isPrim) {
269        w(save, "    if ("+name+" != null) {");
270        if (isExtension) {
271          w(save, "      tgt.makeChild(\""+sname+"\").data().setProperty(\"value[x]\", new "+type+"("+name+"));");
272        } else if (Utilities.existsInList(type, "DateType", "InstantType", "DateTimeType")) {
273          w(save, "      tgt.addChild(\""+sname+"\", new "+type+"("+name+"));");          
274        } else {
275          w(save, "      tgt.makeChild(\""+sname+"\").data().setProperty(\"value\", new "+type+"("+name+"));");
276        }
277        w(save, "    }");      
278      } else if (typeInfo != null && typeInfo.getUrl() != null && !typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) {
279        w(save, "    if ("+name+" != null) {");
280        w(save, "      "+name+".save(tgt.makeChild(\""+sname+"\"), nulls);");
281        w(save, "    }");
282      } else if (isExtension) {
283        w(save, "    if ("+name+" != null) {");
284        w(save, "      tgt.makeChild(\""+sname+"\").data().setProperty(\"value[x]\", "+name+");");
285        w(save, "    }");
286      } else {
287        w(save, "    if ("+name+" != null) {");
288        w(save, "      tgt.addChild(\""+sname+"\", "+name+");");
289        w(save, "    }");
290      }
291    }
292
293    private void genAccessors(boolean isPrim, boolean isAbstract, String name, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, String shortDoco, boolean isFixed) {
294      jdoc(accessors, doco, 2, true);
295      if (isPrim && extensionPolicy != ExtensionPolicy.Primitives && !isList) {
296        w(accessors, "  public "+ptype+" get"+cname+"() {");
297        w(accessors, "    return "+name+";");
298        w(accessors, "  }");
299        w(accessors);
300        w(accessors, "  public "+this.name+" set"+cname+"("+ptype+" value) {");
301        w(accessors, "    this."+name+" = value;");
302        w(accessors, "    return this;");
303        w(accessors, "  }");
304        w(accessors);
305        w(accessors, "  public boolean has"+cname+"() {");
306        w(accessors, "    return "+name+" != null;");
307        w(accessors, "  }");
308      } else {
309        if (isPrim && !isList) {
310          w(accessors, "  public "+ptype+" get"+cname+"() {");
311          w(accessors, "    if ("+name+" == null) { "+name+" = new "+type+"(); }");
312          w(accessors, "    return "+name+".getValue();");
313          w(accessors, "  }");
314          w(accessors, "  public "+ltype+" get"+cname+"Element() {");
315        } else if (isAbstract && !isList) {
316          w(accessors, "  public @Nullable "+ltype+" get"+cname+"() { // "+ltype+" is abstract ");
317        } else {
318          w(accessors, "  public "+ltype+" get"+cname+"() {");
319        }
320        if (isList) {
321          w(accessors, "    if ("+name+" == null) { "+name+" = "+init+"; }");
322        } else if (!isAbstract) {
323          w(accessors, "    if ("+name+" == null) { "+name+" = new "+type+"(); }");
324        }
325        w(accessors, "    return "+name+";");
326        w(accessors, "  }");
327        w(accessors);
328        if (isList) {
329          w(accessors, "  public boolean has"+cname+"() {");
330          w(accessors, "    return "+name+" != null && !"+name+".isEmpty();");
331          w(accessors, "  }");
332          w(accessors);
333          if (!isAbstract) {
334            w(accessors, "  public "+type+" add"+csname+"() {");
335            w(accessors, "    "+type+" theThing = new "+type+"();");
336            w(accessors, "    get"+cname+"().add(theThing);");
337            w(accessors, "    return theThing;");
338            w(accessors, "  }");
339            w(accessors); 
340          }
341          w(accessors, "  public boolean has"+csname+"("+type+" item) {");
342          w(accessors, "    return has"+cname+"() && "+name+".contains(item);");
343          w(accessors, "  }");
344          w(accessors);        
345          w(accessors, "  public void remove"+csname+"("+type+" item) {");
346          w(accessors, "    if (has"+csname+"(item)) {");
347          w(accessors, "      "+name+".remove(item);");
348          w(accessors, "    }");
349          w(accessors, "  }");
350          w(accessors);        
351        } else if (isPrim) {
352          if (!isFixed) {
353            w(accessors, "  public "+this.name+" set"+cname+"("+ptype+" value) {");
354            w(accessors, "    if ("+name+" == null) { "+name+" = new "+type+"(); }");
355            w(accessors, "    "+name+".setValue(value);");
356            w(accessors, "    return this;");
357            w(accessors, "  }");
358            w(accessors, "  public "+this.name+" set"+cname+"Element("+type+" value) {");
359            w(accessors, "    this."+name+" = value;");
360            w(accessors, "    return this;");
361            w(accessors, "  }");
362          }
363          w(accessors, "  public boolean has"+cname+"() {");
364          w(accessors, "    return "+name+" != null && "+name+".hasValue();");
365          w(accessors, "  }");
366          w(accessors); 
367        } else {
368          if (!isFixed) {
369            w(accessors, "  public "+this.name+" set"+cname+"("+type+" value) {");
370            w(accessors, "    this."+name+" = value;");
371            w(accessors, "    return this;");
372            w(accessors, "  }");
373          }
374          w(accessors, "  public boolean has"+cname+"() {");
375          w(accessors, "    return "+name+" != null;");
376          w(accessors, "  }");
377        }
378      }
379      w(accessors);
380    }
381
382    private void genField(boolean isPrim, String name, String ptype, String ltype, String nn, boolean isList, String shortDoco) {
383      // jdoc(fields, field.documentation(), 2, true);
384      if (isPrim && extensionPolicy != ExtensionPolicy.Primitives && !isList) {
385        w(fields, "  private "+ptype+" "+name+";"+nn+"  // "+shortDoco);
386      } else if (isList) {
387        w(fields, "  private "+ltype+" "+name+" = new ArrayList<>();"+nn+"  // "+shortDoco);
388      } else {
389        w(fields, "  private "+ltype+" "+name+";"+nn+"  // "+shortDoco);             
390      }
391    }
392  }
393
394  private String folder;
395  private IWorkerContext workerContext;
396  private String canonical;
397  private String pkgName;
398
399  // options:
400  private ExtensionPolicy extensionPolicy;
401  private boolean narrative;
402  private boolean contained;
403  private boolean meta;
404  private String language;
405  private boolean keyElementsOnly;
406  private String genDate = DEFAULT_DATE();
407
408
409  public PECodeGenerator(IWorkerContext workerContext) {
410    super();
411    this.workerContext = workerContext;
412  }
413
414  public String getFolder() {
415    return folder;
416  }
417
418
419  public void setFolder(String folder) {
420    this.folder = folder;
421  }
422
423
424  public String getCanonical() {
425    return canonical;
426  }
427
428  public void setCanonical(String canonical) {
429    this.canonical = canonical;
430  }
431
432
433  public String getPkgName() {
434    return pkgName;
435  }
436
437  public void setPkgName(String pkgName) {
438    this.pkgName = pkgName;
439  }
440
441  public ExtensionPolicy getExtensionPolicy() {
442    return extensionPolicy;
443  }
444
445  public void setExtensionPolicy(ExtensionPolicy extensionPolicy) {
446    this.extensionPolicy = extensionPolicy;
447  }
448
449  public boolean isNarrative() {
450    return narrative;
451  }
452
453  public void setNarrative(boolean narrative) {
454    this.narrative = narrative;
455  }
456
457  public boolean isMeta() {
458    return meta;
459  }
460
461  public void setMeta(boolean meta) {
462    this.meta = meta;
463  }
464
465  public String getLanguage() {
466    return language;
467  }
468
469  public void setLanguage(String language) {
470    this.language = language;
471  }
472
473  public boolean isKeyElementsOnly() {
474    return keyElementsOnly;
475  }
476
477  public void setKeyElementsOnly(boolean keyElementsOnly) {
478    this.keyElementsOnly = keyElementsOnly;
479  }
480
481  public boolean isContained() {
482    return contained;
483  }
484
485  public void setContained(boolean contained) {
486    this.contained = contained;
487  }
488
489  public String getGenDate() {
490    return genDate;
491  }
492
493  public void setGenDate(String genDate) {
494    this.genDate = genDate;
495  }
496
497  private StringBuilder imports = new StringBuilder();
498
499  /**
500   * @throws IOException 
501   * 
502   */
503  public void execute() throws IOException {
504    PEDefinition source = new PEBuilder(workerContext, PEElementPropertiesPolicy.EXTENSION, true).buildPEDefinition(canonical);
505    w(imports, "import java.util.List;");
506    w(imports, "import java.util.ArrayList;");
507    w(imports, "import javax.annotation.Nullable;");
508    w(imports, "import java.util.Date;\r\n");
509    w(imports);
510    w(imports, "import org.hl7.fhir.r5.context.IWorkerContext;");
511    w(imports, "import org.hl7.fhir.r5.model.*;");
512    w(imports, "import org.hl7.fhir.r5.profilemodel.PEBuilder;");
513    w(imports, "import org.hl7.fhir.r5.profilemodel.PEInstance;");
514    w(imports, "import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy;");
515    w(imports, "import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase;");
516    PEGenClass cls = genClass(source);
517    StringBuilder b = new StringBuilder();
518    w(b, "package "+pkgName+";");
519    w(b);
520    if (source.getProfile().hasCopyright()) {
521      jdoc(b, source.getProfile().getCopyright(), 0, false);
522    }
523    w(b, imports.toString());
524    cls.write(b, source.getProfile().getCopyright());
525    TextFile.stringToFile(b.toString(), Utilities.path(folder, cls.name+".java"));
526  }
527
528  public void jdoc(StringBuilder b, String doco, int indent, boolean jdoc) {
529    if (!Utilities.noString(doco)) {
530      String pfx = Utilities.padLeft("", ' ', indent);
531      w(b, pfx+"/*"+(jdoc ? "*" : ""));
532      for (String line : doco.split("\\R")) {
533        for (String nl : naturalLines(line))
534          w(b, pfx+" * "+nl);
535        w(b, pfx+" *");
536      }
537      w(b, pfx+" */");
538    }    
539  }
540
541  private List<String> naturalLines(String line) {
542    List<String> lines = new ArrayList<>();
543    while (line.length() > 80) {
544      int cutpoint = 80;
545      while (cutpoint > 0 && line.charAt(cutpoint) != ' ') {
546        cutpoint--;
547      }
548      if (cutpoint == 0) {
549        cutpoint = 80;
550      } else {
551        cutpoint++;
552      }
553      lines.add(line.substring(0, cutpoint));
554      line = line.substring(cutpoint);
555    }
556    lines.add(line);
557    return lines;
558  }
559
560  private void w(StringBuilder b) {
561    b.append("\r\n");
562
563  }
564
565  private void w(StringBuilder b, String line) {
566    b.append(line);
567    w(b);    
568  }
569
570  private PEGenClass genClass(PEDefinition source) {
571    PEGenClass cls = new PEGenClass();
572    cls.name = source.getProfile().getName();
573    cls.base = source.getProfile().getType();
574    cls.doco = source.documentation();
575    cls.url = source.getProfile().getVersionedUrl();
576    cls.isResource = source.getProfile().getKind() == StructureDefinitionKind.RESOURCE;
577    cls.genId();
578    for (PEDefinition child : source.children()) {
579      if (genForField(source, child)) {
580        cls.defineField(source, child);
581      }
582    }    
583    return cls;
584  }
585
586  private boolean genForField(PEDefinition source, PEDefinition child) {
587    if (child.definition().getBase().getPath().equals("Resource.meta")) {
588      return meta;
589    }
590    if (child.definition().getBase().getPath().equals("DomainResource.text")) {
591      return narrative;
592    }
593    if (child.definition().getBase().getPath().equals("Resource.language")) {
594      return language == null;
595    }
596    if (child.definition().getBase().getPath().endsWith(".extension") || child.definition().getBase().getPath().endsWith(".modifierExtension")) {
597      return extensionPolicy == ExtensionPolicy.Complexes;
598    }
599    if (child.definition().getBase().getPath().equals("DomainResource.contained")) {
600      return contained;
601    }
602    return !keyElementsOnly || (child.isKeyElement());
603  }
604
605
606  private String getPrimitiveType(StructureDefinition sd) {
607
608    if (sd.getType().equals("string"))
609      return "String";
610    if (sd.getType().equals("code"))
611      return "String";
612    if (sd.getType().equals("markdown"))
613      return "String";
614    if (sd.getType().equals("base64Binary"))
615      return "byte[]";
616    if (sd.getType().equals("uri"))
617      return "String";
618    if (sd.getType().equals("url"))
619      return "String";
620    if (sd.getType().equals("canonical"))
621      return "String";
622    if (sd.getType().equals("oid"))
623      return "String";
624    if (sd.getType().equals("integer"))
625      return "int";
626    if (sd.getType().equals("integer64"))
627      return "long";
628    if (sd.getType().equals("unsignedInt"))
629      return "int";
630    if (sd.getType().equals("positiveInt"))
631      return "int";
632    if (sd.getType().equals("boolean"))
633      return "boolean";
634    if (sd.getType().equals("decimal"))
635      return "BigDecimal";
636    if (sd.getType().equals("dateTime"))
637      return "Date";
638    if (sd.getType().equals("date"))
639      return "Date";
640    if (sd.getType().equals("id"))
641      return "String";
642    if (sd.getType().equals("instant"))
643      return "Date";
644    if (sd.getType().equals("time"))
645      return "String";
646
647    return "??";
648  }
649
650}