| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package utils |
| |
| import ( |
| "context" |
| "encoding/binary" |
| "encoding/xml" |
| "fmt" |
| "math/big" |
| "strings" |
| "unicode" |
| ) |
| |
| type WriteBufferXmlBased interface { |
| WriteBuffer |
| GetXmlString() string |
| } |
| |
| // NewXmlWriteBuffer returns a WriteBufferXmlBased which renders all information into xml |
| func NewXmlWriteBuffer() WriteBufferXmlBased { |
| var xmlString strings.Builder |
| encoder := xml.NewEncoder(&xmlString) |
| encoder.Indent("", " ") |
| return &xmlWriteBuffer{ |
| xmlString: &xmlString, |
| Encoder: encoder, |
| doRenderLists: true, |
| doRenderAttr: true, |
| } |
| } |
| |
| // NewConfiguredXmlWriteBuffer returns a WriteBufferXmlBased which renders configured information into xml |
| func NewConfiguredXmlWriteBuffer(renderLists bool, renderAttr bool) WriteBufferXmlBased { |
| var xmlString strings.Builder |
| encoder := xml.NewEncoder(&xmlString) |
| encoder.Indent("", " ") |
| return &xmlWriteBuffer{ |
| xmlString: &xmlString, |
| Encoder: encoder, |
| doRenderLists: renderLists, |
| doRenderAttr: renderAttr, |
| } |
| } |
| |
| /////////////////////////////////////// |
| /////////////////////////////////////// |
| // |
| // Internal section |
| // |
| |
| type xmlWriteBuffer struct { |
| BufferCommons |
| xmlString *strings.Builder |
| *xml.Encoder |
| doRenderLists bool |
| doRenderAttr bool |
| pos uint |
| } |
| |
| var _ WriteBuffer = (*xmlWriteBuffer)(nil) |
| |
| // |
| // Internal section |
| // |
| /////////////////////////////////////// |
| /////////////////////////////////////// |
| |
| func (*xmlWriteBuffer) GetByteOrder() binary.ByteOrder { |
| return binary.BigEndian |
| } |
| |
| func (*xmlWriteBuffer) SetByteOrder(_ binary.ByteOrder) { |
| } |
| |
| func (x *xmlWriteBuffer) PushContext(logicalName string, writerArgs ...WithWriterArgs) error { |
| // Pre-emptive flush to avoid overflow when for a long time no context gets popped |
| if err := x.Flush(); err != nil { |
| return err |
| } |
| attrs := make([]xml.Attr, 0) |
| attrs = x.markAsListIfRequired(writerArgs, attrs) |
| return x.EncodeToken(xml.StartElement{Name: xml.Name{Local: x.SanitizeLogicalName(logicalName)}, Attr: attrs}) |
| } |
| |
| func (x *xmlWriteBuffer) GetPos() uint16 { |
| return uint16(x.pos * 8) |
| } |
| |
| func (x *xmlWriteBuffer) WriteBit(logicalName string, value bool, writerArgs ...WithWriterArgs) error { |
| x.move(1) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwBitKey, 1, writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteByte(logicalName string, value byte, writerArgs ...WithWriterArgs) error { |
| x.move(8) |
| return x.encodeElement(logicalName, fmt.Sprintf("%#02x", value), x.generateAttr(rwByteKey, 8, writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteByteArray(logicalName string, data []byte, writerArgs ...WithWriterArgs) error { |
| hexString := fmt.Sprintf("%#02x", data) |
| if hexString == "00" { |
| // golang does mess up the formatting on empty arrays |
| hexString = "0x" |
| } |
| x.move(uint(len(data) * 8)) |
| return x.encodeElement(logicalName, hexString, x.generateAttr(rwByteKey, uint(len(data)*8), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteUint8(logicalName string, bitLength uint8, value uint8, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteUint16(logicalName string, bitLength uint8, value uint16, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteUint32(logicalName string, bitLength uint8, value uint32, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteUint64(logicalName string, bitLength uint8, value uint64, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteInt8(logicalName string, bitLength uint8, value int8, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteInt16(logicalName string, bitLength uint8, value int16, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteInt32(logicalName string, bitLength uint8, value int32, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteInt64(logicalName string, bitLength uint8, value int64, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteBigInt(logicalName string, bitLength uint8, value *big.Int, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteFloat32(logicalName string, bitLength uint8, value float32, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, fmt.Sprintf("%16.16f", value), x.generateAttr(rwFloatKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteFloat64(logicalName string, bitLength uint8, value float64, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, fmt.Sprintf("%32.32f", value), x.generateAttr(rwFloatKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteBigFloat(logicalName string, bitLength uint8, value *big.Float, writerArgs ...WithWriterArgs) error { |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, value, x.generateAttr(rwFloatKey, uint(bitLength), writerArgs...), writerArgs...) |
| } |
| |
| // [^\u0009\r\n\u0020-\uD7FF\uE000-\uFFFD\ud800\udc00-\udbff\udfff] |
| var printableRange = &unicode.RangeTable{ |
| R16: []unicode.Range16{ |
| {0x0009, 0x0009, 1}, |
| {0x000D, 0x000D, 1}, |
| {0x000A, 0x000A, 1}, |
| {0x0020, 0xD7FF, 1}, |
| {0xE000, 0xFFFD, 1}, |
| {0xD800, 0xD800, 1}, |
| {0xDC00, 0xDBFF, 1}, |
| {0xDFFF, 0xDFFF, 1}, |
| }, |
| } |
| |
| func (x *xmlWriteBuffer) WriteString(logicalName string, bitLength uint32, value string, writerArgs ...WithWriterArgs) error { |
| attr := x.generateAttr(rwStringKey, uint(bitLength), writerArgs...) |
| attr = append(attr, xml.Attr{Name: xml.Name{Local: rwEncodingKey}, Value: x.ExtractEncoding(UpcastWriterArgs(writerArgs...)...)}) |
| cleanedUpString := strings.TrimFunc(value, func(r rune) bool { |
| return !unicode.In(r, printableRange) |
| }) |
| x.move(uint(bitLength)) |
| return x.encodeElement(logicalName, cleanedUpString, attr, writerArgs...) |
| } |
| |
| func (x *xmlWriteBuffer) WriteVirtual(_ context.Context, _ string, _ any, _ ...WithWriterArgs) error { |
| // NO-OP |
| return nil |
| } |
| |
| func (x *xmlWriteBuffer) WriteSerializable(ctx context.Context, serializable Serializable) error { |
| if serializable == nil { |
| return nil |
| } |
| return serializable.SerializeWithWriteBuffer(ctx, x) |
| } |
| |
| func (x *xmlWriteBuffer) PopContext(logicalName string, _ ...WithWriterArgs) error { |
| if err := x.Encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: x.SanitizeLogicalName(logicalName)}}); err != nil { |
| return err |
| } |
| return x.Encoder.Flush() |
| } |
| |
| func (x *xmlWriteBuffer) GetXmlString() string { |
| return x.xmlString.String() |
| } |
| |
| func (x *xmlWriteBuffer) encodeElement(logicalName string, value any, attr []xml.Attr, _ ...WithWriterArgs) error { |
| return x.EncodeElement(value, xml.StartElement{ |
| Name: xml.Name{Local: x.SanitizeLogicalName(logicalName)}, |
| Attr: attr, |
| }) |
| } |
| |
| func (x *xmlWriteBuffer) generateAttr(dataType string, bitLength uint, writerArgs ...WithWriterArgs) []xml.Attr { |
| attrs := make([]xml.Attr, 2) |
| if !x.doRenderAttr { |
| return attrs |
| } |
| attrs[0] = xml.Attr{ |
| Name: xml.Name{Local: rwDataTypeKey}, |
| Value: dataType, |
| } |
| attrs[1] = xml.Attr{ |
| Name: xml.Name{Local: rwBitLengthKey}, |
| Value: fmt.Sprintf("%d", bitLength), |
| } |
| for _, arg := range writerArgs { |
| if !arg.isWriterArgs() { |
| panic("not a writer arg") |
| } |
| switch arg.(type) { |
| case withAdditionalStringRepresentation: |
| attrs = append(attrs, xml.Attr{ |
| Name: xml.Name{Local: rwStringRepresentationKey}, |
| Value: arg.(withAdditionalStringRepresentation).stringRepresentation, |
| }) |
| } |
| } |
| return attrs |
| } |
| |
| func (x *xmlWriteBuffer) markAsListIfRequired(writerArgs []WithWriterArgs, attrs []xml.Attr) []xml.Attr { |
| if !x.doRenderLists { |
| return attrs |
| } |
| if x.IsToBeRenderedAsList(UpcastWriterArgs(writerArgs...)...) { |
| attrs = append(attrs, xml.Attr{ |
| Name: xml.Name{Local: rwIsListKey}, |
| Value: "true", |
| }) |
| } |
| return attrs |
| } |
| |
| func (x *xmlWriteBuffer) move(bits uint) { |
| x.pos += bits |
| } |