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}