Files
V2GDecoderC/DotNet/V2G/EXIEncoder.cs
ChiKyun Kim c6dc6735fa feat: Complete cross-platform build system and folder reorganization
- Reorganize project structure: Port/ → DotNet/, VC/, C++/
- Add comprehensive cross-platform build automation
  - Windows: build_all.bat, build.bat files for all components
  - Linux/macOS: build_all.sh, build.sh files for all components
- Update all build scripts with correct folder paths
- Create test automation scripts (test_all.bat/sh)
- Update documentation to reflect new structure
- Maintain 100% roundtrip accuracy for test5.exi (pure EXI)
- Support both Windows MSBuild and Linux GCC compilation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 09:36:38 +09:00

275 lines
9.8 KiB
C#

/*
* 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 V2GDecoderNet.EXI;
using System.Xml;
namespace V2GDecoderNet.V2G
{
/// <summary>
/// EXI Encoder for converting XML to EXI binary data
/// </summary>
public class EXIEncoder
{
private readonly EXIConfig _config;
public EXIEncoder(EXIConfig? config = null)
{
_config = config ?? new EXIConfig();
}
/// <summary>
/// Encode XML string to EXI binary data
/// </summary>
/// <param name="xmlString">XML string to encode</param>
/// <returns>EXI binary data</returns>
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);
}
/// <summary>
/// Encode XmlDocument to EXI binary data
/// </summary>
/// <param name="xmlDoc">XmlDocument to encode</param>
/// <returns>EXI binary data</returns>
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);
}
}
/// <summary>
/// Write EXI header with options
/// </summary>
/// <param name="outputStream">Output bit stream</param>
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();
}
/// <summary>
/// Encode XML document content
/// </summary>
/// <param name="xmlDoc">XML document</param>
/// <param name="outputStream">Output bit stream</param>
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);
}
/// <summary>
/// Encode XML element
/// </summary>
/// <param name="element">XML element</param>
/// <param name="outputStream">Output bit stream</param>
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);
}
/// <summary>
/// Encode element attributes
/// </summary>
/// <param name="element">XML element</param>
/// <param name="outputStream">Output bit stream</param>
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);
}
}
/// <summary>
/// Encode text content
/// </summary>
/// <param name="text">Text content</param>
/// <param name="outputStream">Output bit stream</param>
private void EncodeTextContent(string text, BitOutputStream outputStream)
{
if (!string.IsNullOrEmpty(text))
{
// Write CHARACTERS event
WriteEventCode(outputStream, EXIEvent.CHARACTERS);
// Write text content
WriteCharacters(outputStream, text);
}
}
/// <summary>
/// Write event code to stream
/// </summary>
/// <param name="outputStream">Output bit stream</param>
/// <param name="eventType">Event type</param>
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);
}
/// <summary>
/// Write element name to stream
/// </summary>
/// <param name="outputStream">Output bit stream</param>
/// <param name="name">Element name</param>
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);
}
/// <summary>
/// Write attribute name to stream
/// </summary>
/// <param name="outputStream">Output bit stream</param>
/// <param name="name">Attribute name</param>
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);
}
/// <summary>
/// Write attribute value to stream
/// </summary>
/// <param name="outputStream">Output bit stream</param>
/// <param name="value">Attribute value</param>
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);
}
/// <summary>
/// Write character data to stream
/// </summary>
/// <param name="outputStream">Output bit stream</param>
/// <param name="text">Character data</param>
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);
}
}
}