001package org.hl7.fhir.r4.utils; 002 003/*- 004 * #%L 005 * org.hl7.fhir.r4 006 * %% 007 * Copyright (C) 2014 - 2019 Health Level 7 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023 024import java.io.OutputStreamWriter; 025import java.util.ArrayList; 026import java.util.List; 027 028import org.hl7.fhir.exceptions.FHIRException; 029import org.hl7.fhir.r4.context.IWorkerContext; 030import org.hl7.fhir.r4.model.ElementDefinition; 031import org.hl7.fhir.r4.model.StructureDefinition; 032import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; 033import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; 034import org.hl7.fhir.utilities.Utilities; 035 036public class ProtoBufGenerator { 037 038 private IWorkerContext context; 039 private StructureDefinition definition; 040 private OutputStreamWriter destination; 041 private int cursor; 042 private Message message; 043 044 private class Field { 045 private String name; 046 private boolean required; 047 private boolean repeating; 048 private String type; 049 } 050 051 private class Message { 052 private String name; 053 private List<Field> fields = new ArrayList<Field>(); 054 private List<Message> messages = new ArrayList<Message>(); 055 public Message(String name) { 056 super(); 057 this.name = name; 058 } 059 060 061 } 062 063 public ProtoBufGenerator(IWorkerContext context) { 064 super(); 065 this.context = context; 066 } 067 068 public ProtoBufGenerator(IWorkerContext context, StructureDefinition definition, OutputStreamWriter destination) { 069 super(); 070 this.context = context; 071 this.definition = definition; 072 this.destination = destination; 073 } 074 075 public IWorkerContext getContext() { 076 return context; 077 } 078 079 public StructureDefinition getDefinition() { 080 return definition; 081 } 082 083 public void setDefinition(StructureDefinition definition) { 084 this.definition = definition; 085 } 086 087 public OutputStreamWriter getDestination() { 088 return destination; 089 } 090 091 public void setDestination(OutputStreamWriter destination) { 092 this.destination = destination; 093 } 094 095 096 public void build() throws FHIRException { 097 if (definition == null) 098 throw new FHIRException("A definition must be provided"); 099 if (destination == null) 100 throw new FHIRException("A destination must be provided"); 101 102 if (definition.getDerivation() == TypeDerivationRule.CONSTRAINT) 103 throw new FHIRException("derivation = constraint is not supported yet"); 104 105 message = new Message(definition.getSnapshot().getElement().get(0).getPath()); 106 cursor = 1; 107 while (cursor < definition.getSnapshot().getElement().size()) { 108 ElementDefinition ed = definition.getSnapshot().getElement().get(0); 109 Field fld = new Field(); 110 fld.name = tail(ed.getPath()); 111 fld.required = (ed.getMin() == 1); 112 fld.repeating = (!ed.getMax().equals("1")); 113 message.fields.add(fld); 114 if (ed.getType().size() != 1) 115 fld.type = "Unknown"; 116 else { 117 StructureDefinition td = context.fetchTypeDefinition(ed.getTypeFirstRep().getWorkingCode()); 118 if (td == null) 119 fld.type = "Unresolved"; 120 else if (td.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { 121 fld.type = protoTypeForFhirType(ed.getTypeFirstRep().getWorkingCode()); 122 fld = new Field(); 123 fld.name = tail(ed.getPath())+"Extra"; 124 fld.repeating = (!ed.getMax().equals("1")); 125 fld.type = "Primitive"; 126 message.fields.add(fld); 127 } else 128 fld.type = ed.getTypeFirstRep().getWorkingCode(); 129 } 130 } 131 } 132 133 private String protoTypeForFhirType(String code) { 134 if (Utilities.existsInList(code, "integer", "unsignedInt", "positiveInt")) 135 return "int23"; 136 else if (code.equals("boolean")) 137 return "bool"; 138 else 139 return "string"; 140 } 141 142 private String tail(String path) { 143 return path.substring(path.lastIndexOf(".")+1); 144 } 145 146 147}