/* * Copyright (C) 2007-2024 C# Port * Original Copyright (C) 2007-2018 Siemens AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ using V2GDecoderNetFx.EXI; using System.Xml; namespace V2GDecoderNetFx.V2G { /// /// EXI Encoder for converting XML to EXI binary data /// public class EXIEncoder { private readonly EXIConfig _config; public EXIEncoder(EXIConfig config = null) { _config = config ?? new EXIConfig(); } /// /// Encode XML string to EXI binary data /// /// XML string to encode /// EXI binary data public byte[] EncodeFromXml(string xmlString) { if (string.IsNullOrEmpty(xmlString)) throw new ArgumentException("XML string cannot be null or empty", nameof(xmlString)); var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xmlString); return EncodeFromXmlDocument(xmlDoc); } /// /// Encode XmlDocument to EXI binary data /// /// XmlDocument to encode /// EXI binary data public byte[] EncodeFromXmlDocument(XmlDocument xmlDoc) { if (xmlDoc == null) throw new ArgumentNullException(nameof(xmlDoc)); var outputStream = new BitOutputStream(); try { // Write EXI header WriteHeader(outputStream); // Encode document EncodeDocument(xmlDoc, outputStream); return outputStream.ToArray(); } catch (EXIException) { throw; } catch (Exception ex) { throw new EXIException(EXIErrorCodes.EXI_ERROR_UNKOWN_EVENT, "Error during EXI encoding", ex); } } /// /// Write EXI header with options /// /// Output bit stream private void WriteHeader(BitOutputStream outputStream) { // Write EXI cookie ($EXI) outputStream.WriteBytes(new byte[] { (byte)'$', (byte)'E', (byte)'X', (byte)'I' }); // Format version (4 bits) - currently 0 outputStream.WriteBits(0, 4); // Options presence flag (1 bit) - false for simplicity outputStream.WriteBit(0); // Align to byte boundary outputStream.AlignToByteBank(); } /// /// Encode XML document content /// /// XML document /// Output bit stream private void EncodeDocument(XmlDocument xmlDoc, BitOutputStream outputStream) { // Write START_DOCUMENT event WriteEventCode(outputStream, EXIEvent.START_DOCUMENT); // Encode root element and its children if (xmlDoc.DocumentElement != null) { EncodeElement(xmlDoc.DocumentElement, outputStream); } // Write END_DOCUMENT event WriteEventCode(outputStream, EXIEvent.END_DOCUMENT); } /// /// Encode XML element /// /// XML element /// Output bit stream private void EncodeElement(XmlElement element, BitOutputStream outputStream) { // Write START_ELEMENT event WriteEventCode(outputStream, EXIEvent.START_ELEMENT); // Write element name (simplified - in real implementation would use string tables) WriteElementName(outputStream, element.Name); // Encode attributes EncodeAttributes(element, outputStream); // Encode child nodes foreach (XmlNode child in element.ChildNodes) { switch (child.NodeType) { case XmlNodeType.Element: EncodeElement((XmlElement)child, outputStream); break; case XmlNodeType.Text: case XmlNodeType.CDATA: EncodeTextContent(child.Value ?? string.Empty, outputStream); break; case XmlNodeType.Comment: if (_config != null) // Preserve comments if configured { // Skip for simplicity } break; } } // Write END_ELEMENT event WriteEventCode(outputStream, EXIEvent.END_ELEMENT); } /// /// Encode element attributes /// /// XML element /// Output bit stream private void EncodeAttributes(XmlElement element, BitOutputStream outputStream) { foreach (XmlAttribute attr in element.Attributes) { // Write ATTRIBUTE event WriteEventCode(outputStream, EXIEvent.ATTRIBUTE); // Write attribute name and value (simplified) WriteAttributeName(outputStream, attr.Name); WriteAttributeValue(outputStream, attr.Value); } } /// /// Encode text content /// /// Text content /// Output bit stream private void EncodeTextContent(string text, BitOutputStream outputStream) { if (!string.IsNullOrEmpty(text)) { // Write CHARACTERS event WriteEventCode(outputStream, EXIEvent.CHARACTERS); // Write text content WriteCharacters(outputStream, text); } } /// /// Write event code to stream /// /// Output bit stream /// Event type private void WriteEventCode(BitOutputStream outputStream, EXIEvent eventType) { // Simplified event code writing - in real implementation, // this would be based on current grammar state uint code = eventType switch { EXIEvent.START_DOCUMENT => 0, EXIEvent.START_ELEMENT => 0, EXIEvent.END_ELEMENT => 1, EXIEvent.CHARACTERS => 2, EXIEvent.ATTRIBUTE => 3, EXIEvent.END_DOCUMENT => 3, _ => 0 }; outputStream.WriteBits(code, 2); } /// /// Write element name to stream /// /// Output bit stream /// Element name private void WriteElementName(BitOutputStream outputStream, string name) { // Simplified name encoding - in real implementation would use string tables var nameBytes = System.Text.Encoding.UTF8.GetBytes(name); outputStream.WriteUnsignedInteger((uint)nameBytes.Length); outputStream.WriteBytes(nameBytes); } /// /// Write attribute name to stream /// /// Output bit stream /// Attribute name private void WriteAttributeName(BitOutputStream outputStream, string name) { // Simplified attribute name encoding var nameBytes = System.Text.Encoding.UTF8.GetBytes(name); outputStream.WriteUnsignedInteger((uint)nameBytes.Length); outputStream.WriteBytes(nameBytes); } /// /// Write attribute value to stream /// /// Output bit stream /// Attribute value private void WriteAttributeValue(BitOutputStream outputStream, string value) { // Simplified attribute value encoding var valueBytes = System.Text.Encoding.UTF8.GetBytes(value ?? string.Empty); outputStream.WriteUnsignedInteger((uint)valueBytes.Length); outputStream.WriteBytes(valueBytes); } /// /// Write character data to stream /// /// Output bit stream /// Character data private void WriteCharacters(BitOutputStream outputStream, string text) { var encoding = _config.Strings switch { EXIConfig.StringRepresentation.ASCII => System.Text.Encoding.ASCII, EXIConfig.StringRepresentation.UCS => System.Text.Encoding.UTF8, _ => System.Text.Encoding.UTF8 }; var textBytes = encoding.GetBytes(text); outputStream.WriteUnsignedInteger((uint)textBytes.Length); outputStream.WriteBytes(textBytes); } } }