001package org.hl7.fhir.validation.instance.type;
002
003import java.io.ByteArrayOutputStream;
004import java.io.IOException;
005import java.util.ArrayList;
006import java.util.Comparator;
007import java.util.HashSet;
008import java.util.List;
009import java.util.Set;
010
011import org.hl7.fhir.convertors.conv10_50.VersionConvertor_10_50;
012import org.hl7.fhir.convertors.conv14_50.VersionConvertor_14_50;
013import org.hl7.fhir.convertors.conv30_50.VersionConvertor_30_50;
014import org.hl7.fhir.convertors.factory.*;
015import org.hl7.fhir.exceptions.FHIRException;
016import org.hl7.fhir.r5.conformance.ProfileUtilities;
017import org.hl7.fhir.r5.context.IWorkerContext;
018import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
019import org.hl7.fhir.r5.elementmodel.Element;
020import org.hl7.fhir.r5.elementmodel.Manager;
021import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
022import org.hl7.fhir.r5.formats.IParser.OutputStyle;
023import org.hl7.fhir.r5.model.Coding;
024import org.hl7.fhir.r5.model.ElementDefinition;
025import org.hl7.fhir.r5.model.ExpressionNode;
026import org.hl7.fhir.r5.model.Resource;
027import org.hl7.fhir.r5.model.StructureDefinition;
028import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
029import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
030import org.hl7.fhir.r5.model.ValueSet;
031import org.hl7.fhir.r5.utils.FHIRPathEngine;
032import org.hl7.fhir.r5.utils.ToolingExtensions;
033import org.hl7.fhir.r5.utils.XVerExtensionManager;
034import org.hl7.fhir.utilities.Utilities;
035import org.hl7.fhir.utilities.VersionUtilities;
036import org.hl7.fhir.utilities.i18n.I18nConstants;
037import org.hl7.fhir.utilities.validation.ValidationMessage;
038import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
039import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
040import org.hl7.fhir.utilities.validation.ValidationOptions;
041import org.hl7.fhir.validation.BaseValidator;
042import org.hl7.fhir.validation.TimeTracker;
043import org.hl7.fhir.validation.instance.utils.NodeStack;
044
045public class StructureDefinitionValidator extends BaseValidator {
046
047  public class FhirPathSorter implements Comparator<ExpressionNode> {
048
049    @Override
050    public int compare(ExpressionNode arg0, ExpressionNode arg1) {
051      return arg0.toString().compareTo(arg1.toString());
052    }
053
054  }
055
056  private FHIRPathEngine fpe;
057  private boolean wantCheckSnapshotUnchanged;
058
059  public StructureDefinitionValidator(IWorkerContext context, TimeTracker timeTracker, FHIRPathEngine fpe, boolean wantCheckSnapshotUnchanged, XVerExtensionManager xverManager) {
060    super(context, xverManager);
061    source = Source.InstanceValidator;
062    this.fpe = fpe;
063    this.timeTracker = timeTracker;
064    this.wantCheckSnapshotUnchanged = wantCheckSnapshotUnchanged;
065  }
066  
067  public void validateStructureDefinition(List<ValidationMessage> errors, Element src, NodeStack stack)  {
068    StructureDefinition sd = null;
069    try {
070      sd = loadAsSD(src);
071      List<ElementDefinition> snapshot = sd.getSnapshot().getElement();
072      sd.setSnapshot(null);
073      StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
074      if (warning(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) {
075        if (rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasDerivation(), I18nConstants.SD_MUST_HAVE_DERIVATION, sd.getUrl())) {
076          if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
077            List<ValidationMessage> msgs = new ArrayList<>();
078            ProfileUtilities pu = new ProfileUtilities(context, msgs, null);
079            pu.setXver(xverManager);
080            pu.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir/R4/", sd.getName());
081            if (msgs.size() > 0) {
082              for (ValidationMessage msg : msgs) {
083                // we need to set the location for the context 
084                String loc = msg.getLocation();
085                if (loc.contains("#")) {
086                  msg.setLocation(stack.getLiteralPath()+".differential.element.where(path = '"+loc.substring(loc.indexOf("#")+1)+"')");
087                } else {
088                  msg.setLocation(stack.getLiteralPath());
089                }
090                errors.add(msg);
091              }
092            }
093            if (!snapshot.isEmpty() && wantCheckSnapshotUnchanged) {
094              int was = snapshot.size();
095              int is = sd.getSnapshot().getElement().size();
096              rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), was == is, I18nConstants.SNAPSHOT_EXISTING_PROBLEM, was, is);
097            }
098          }
099        }
100        if ("constraint".equals(src.getChildValue("derivation"))) {
101          rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base.getKindElement().primitiveValue().equals(src.getChildValue("kind")), 
102              I18nConstants.SD_DERIVATION_KIND_MISMATCH, base.getKindElement().primitiveValue(), src.getChildValue("kind"));
103        }
104      }
105    } catch (FHIRException | IOException e) {
106      rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
107    }
108    List<Element> differentials = src.getChildrenByName("differential");
109    List<Element> snapshots = src.getChildrenByName("snapshot");
110    for (Element differential : differentials) {
111      validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd);
112    }
113    for (Element snapshot : snapshots) {
114      validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true, sd);
115    }
116  }
117
118  private void validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd) {
119    List<Element> elements = elementList.getChildrenByName("element");
120    int cc = 0;
121    for (Element element : elements) {
122      validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd);
123      cc++;
124    }    
125  }
126
127  private void validateElementDefinition(List<ValidationMessage> errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd) {
128    boolean typeMustSupport = false;
129    List<Element> types = element.getChildrenByName("type");
130    Set<String> typeCodes = new HashSet<>();
131    for (Element type : types) {
132      if (hasMustSupportExtension(type)) {
133        typeMustSupport = true;
134      }
135      String tc = type.getChildValue("code");
136      if (type.hasExtension("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type")) {
137        tc = type.getExtensionValue("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type").primitiveValue();
138      }
139      if (Utilities.noString(tc) && type.hasChild("code")) {
140        if (type.getNamedChild("code").hasExtension("http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type")) {
141          tc = "*";
142        }
143      }
144      typeCodes.add(tc);
145      // check the stated profile - must be a constraint on the type 
146      if (snapshot || sd != null) {
147        validateElementType(errors, type, stack.push(type, -1, null, null), sd, element.getChildValue("path"));
148      }
149    }
150    if (typeMustSupport) {
151      if (snapshot) {
152        rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_SNAPSHOT, element.getNamedChildValue("path"));
153      } else {
154        hint(errors, IssueType.EXCEPTION, stack.getLiteralPath(), hasSnapshot || "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_DIFF, element.getNamedChildValue("path"));        
155      }
156    }
157    if (element.hasChild("binding")) {
158      Element binding = element.getNamedChild("binding");
159      validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, element.getNamedChildValue("path"));
160    } else {
161      // this is a good idea but there's plenty of cases where the rule isn't met; maybe one day it's worth investing the time to exclude these cases and bring this rule back
162//      String bt = boundType(typeCodes);
163//      hint(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bt == null, I18nConstants.SD_ED_SHOULD_BIND, element.getNamedChildValue("path"), bt);              
164    }
165    // in a snapshot, we validate that fixedValue, pattern, and defaultValue, if present, are all of the right type
166    if (snapshot && (element.getIdBase() != null) && (element.getIdBase().contains("."))) {
167      if (rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), !typeCodes.isEmpty() || element.hasChild("contentReference"), I18nConstants.SD_NO_TYPES_OR_CONTENTREF, element.getIdBase())) {     
168        Element v = element.getNamedChild("defaultValue");
169        if (v != null) {
170          rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "defaultValue", v.fhirType(), typeCodes);
171        }
172        v = element.getNamedChild("fixed");
173        if (v != null) {
174          rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "fixed", v.fhirType(), typeCodes);
175        }
176        v = element.getNamedChild("pattern");
177        if (v != null) {
178          rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "pattern", v.fhirType(), typeCodes);
179        }
180      }
181    }
182  }
183
184  private String boundType(Set<String> typeCodes) {
185    for (String tc : typeCodes) {
186      if (Utilities.existsInList(tc, "code", "Coding", "CodeableConcept", "Quantity", "CodeableReference")) {
187        return tc;
188      }
189    }
190    return null;
191  }
192
193  private String bindableType(Set<String> typeCodes) {
194    String ret = boundType(typeCodes);
195    if (ret != null) {
196      return ret;
197    }
198    for (String tc : typeCodes) {
199      if (Utilities.existsInList(tc, "string", "uri", "CodeableConcept", "Quantity", "CodeableReference")) {
200        return tc;
201      }
202      StructureDefinition sd = context.fetchTypeDefinition(tc);
203      if (sd != null) {
204        if (sd.hasExtension(ToolingExtensions.EXT_BINDING_METHOD)) {
205          return tc;          
206        }
207      }
208    }
209    return null;
210  }
211
212  private void validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path) {
213    rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bindableType(typeCodes) != null, I18nConstants.SD_ED_BIND_NO_BINDABLE, path, typeCodes.toString());
214    if (!snapshot) {
215      Set<String> bindables = getListofBindableTypes(typeCodes);    
216      hint(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), bindables.size() <= 1, I18nConstants.SD_ED_BIND_MULTIPLE_TYPES, path, typeCodes.toString());
217    }
218    
219    if (binding.hasChild("valueSet")) {
220      Element valueSet = binding.getNamedChild("valueSet");
221      String ref = valueSet.hasPrimitiveValue() ? valueSet.primitiveValue() : valueSet.getNamedChildValue("reference");
222      if (warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || ref != null, I18nConstants.SD_ED_SHOULD_BIND_WITH_VS, path)) {
223        Resource vs = context.fetchResource(Resource.class, ref);
224        
225        // just because we can't resolve it directly doesn't mean that terminology server can't. Check with it
226        
227        if (warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
228          if (vs != null) {
229            rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType());
230          }
231        }
232      }
233    } 
234  }
235
236  private Set<String> getListofBindableTypes(Set<String> types) {
237    Set<String> res = new HashSet<>();
238    for (String s : types) {
239      if (Utilities.existsInList(s, "code", "string", "url", "uri", "Coding", "CodeableConcept", "Quantity", "CodeableReference")) {
240        res.add(s);
241      }
242    }
243    return res;
244  }
245
246  private boolean serverSupportsValueSet(String ref) {
247    ValidationResult vr = context.validateCode(new ValidationOptions().checkValueSetOnly().setVsAsUrl().noClient(), new Coding("http://loinc.org", "5792-7", null), new ValueSet().setUrl(ref));
248    return vr.getErrorClass() == null;
249  }
250
251  private void validateElementType(List<ValidationMessage> errors, Element type, NodeStack stack, StructureDefinition sd, String path) {
252    String code = type.getNamedChildValue("code");
253    if (code == null && path != null) {
254      code = getTypeCodeFromSD(sd, path);
255    }
256    if (code != null) {
257      List<Element> profiles = type.getChildrenByName("profile");
258      if (VersionUtilities.isR2Ver(context.getVersion()) || VersionUtilities.isR2BVer(context.getVersion()) ) {
259        for (Element profile : profiles) {
260          validateProfileTypeOrTarget(errors, profile, code, stack.push(profile, -1, null, null), path);
261        }
262        
263      } else {
264        for (Element profile : profiles) {
265          validateTypeProfile(errors, profile, code, stack.push(profile, -1, null, null), path);
266        }
267        profiles = type.getChildrenByName("targetProfile");
268        for (Element profile : profiles) {
269          validateTargetProfile(errors, profile, code, stack.push(profile, -1, null, null), path);
270        }
271      }
272    }
273  }
274
275  private void validateProfileTypeOrTarget(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
276    String p = profile.primitiveValue();
277    StructureDefinition sd = context.fetchResource(StructureDefinition.class, p);
278    if (code.equals("Reference")) {
279      if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
280        StructureDefinition t = determineBaseType(sd);
281        if (t == null) {
282          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
283        } else {
284          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path);
285        }
286      }
287    } else {
288      if (sd == null ) {
289        sd = getXverExt(errors, stack.getLiteralPath(), profile, p);
290      }
291      if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
292        StructureDefinition t = determineBaseType(sd);
293        if (t == null) {
294          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
295        } else {
296          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path);
297        }
298      }      
299    }
300  }
301
302  private String getTypeCodeFromSD(StructureDefinition sd, String path) {
303    ElementDefinition ed = null;
304    for (ElementDefinition t : sd.getSnapshot().getElement()) {
305      if (t.hasPath() && t.getPath().equals(path)) {
306        if (ed == null) {
307          ed = t;
308        } else {
309          return null; // more than one match, we don't know which is which
310        }
311      }
312    }
313    return ed != null && ed.getType().size() == 1 ? ed.getTypeFirstRep().getCode() : null;
314  }
315
316  private void validateTypeProfile(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
317    String p = profile.primitiveValue();
318    StructureDefinition sd = context.fetchResource(StructureDefinition.class, p);
319    if (sd == null ) {
320      sd = getXverExt(errors, stack.getLiteralPath(), profile, p);
321    }
322    if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
323      StructureDefinition t = determineBaseType(sd);
324      if (t == null) {
325        rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
326      } else if (!isInstanceOf(t, code)) {
327        rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path);
328      }
329    }
330  }
331
332  private void validateTargetProfile(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
333    String p = profile.primitiveValue();
334    StructureDefinition sd = context.fetchResource(StructureDefinition.class, p);
335    if (code.equals("Reference")) {
336      if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
337        StructureDefinition t = determineBaseType(sd);
338        if (t == null) {
339          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
340        } else {
341          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Resource");
342        }
343      }
344    } else if (code.equals("canonical")) {
345      if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
346        StructureDefinition t = determineBaseType(sd);
347        if (t == null) {
348          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p);
349        } else if (!VersionUtilities.isR5Ver(context.getVersion())) {
350          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(t.getType()) || "Resource".equals(t.getType()), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Canonical Resource");
351        } else {
352          rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(t.getType()), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Canonical Resource");
353        }  
354      }
355    } else {
356      rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_NO_TARGET_PROFILE, code);
357    }
358  }
359
360  private boolean isInstanceOf(StructureDefinition sd, String code) {
361    while (sd != null) {
362      if (sd.getType().equals(code)) {
363        return true;
364      }
365      if (sd.getUrl().equals(code)) {
366        return true;
367      }
368      sd = sd.hasBaseDefinition() ? context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()) : null;
369      if (!(VersionUtilities.isR2Ver(context.getVersion()) || VersionUtilities.isR2BVer(context.getVersion())) && sd != null && !sd.getAbstract() && sd.getKind() != StructureDefinitionKind.LOGICAL) {
370        sd = null;
371      }
372    }
373    
374    return false;
375  }
376
377  private StructureDefinition determineBaseType(StructureDefinition sd) {
378    while (sd != null && sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
379      sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
380    }
381    return sd;
382  }
383
384  private boolean hasMustSupportExtension(Element type) {
385    if ("true".equals(getExtensionValue(type, ToolingExtensions.EXT_MUST_SUPPORT))) {
386      return true;
387    }
388    List<Element> profiles = type.getChildrenByName("profile");
389    for (Element profile : profiles) {
390      if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) {
391        return true;
392      }
393    }
394    profiles = type.getChildrenByName("targetProfile");
395    for (Element profile : profiles) {
396      if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) {
397        return true;
398      }
399    }
400    return false;
401  }
402
403  private String getExtensionValue(Element element, String url) {
404    List<Element> extensions = element.getChildrenByName("extension");
405    for (Element extension : extensions) {
406      if (url.equals(extension.getNamedChildValue("url"))) {
407        return extension.getNamedChildValue("value");
408      }
409    }
410    return null;
411  }
412
413  private StructureDefinition loadAsSD(Element src) throws FHIRException, IOException {
414    ByteArrayOutputStream bs = new ByteArrayOutputStream();
415    Manager.compose(context, src, bs, FhirFormat.JSON, OutputStyle.NORMAL, null);
416    if (VersionUtilities.isR2Ver(context.getVersion())) {
417      org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(bs.toByteArray());
418      return (StructureDefinition) VersionConvertorFactory_10_50.convertResource(r2);
419    }
420    if (VersionUtilities.isR2BVer(context.getVersion())) {
421      org.hl7.fhir.dstu2016may.model.Resource r2b = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(bs.toByteArray());
422      return (StructureDefinition) VersionConvertorFactory_14_50.convertResource(r2b);
423    }
424    if (VersionUtilities.isR3Ver(context.getVersion())) {
425      org.hl7.fhir.dstu3.model.Resource r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(bs.toByteArray());
426      return (StructureDefinition) VersionConvertorFactory_30_50.convertResource(r3);
427    }
428    if (VersionUtilities.isR4Ver(context.getVersion())) {
429      org.hl7.fhir.r4.model.Resource r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(bs.toByteArray());
430      return (StructureDefinition) VersionConvertorFactory_40_50.convertResource(r4);
431    }
432    return (StructureDefinition) new org.hl7.fhir.r5.formats.JsonParser().parse(bs.toByteArray());
433  }
434
435}