Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
myd7349 committed Aug 4, 2024
1 parent 6220866 commit 0e2366c
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 29 deletions.
28 changes: 15 additions & 13 deletions Source/SharpLSL/Interop/XML.Interop.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
using System;
using System.Runtime.InteropServices;

using lsl_xml_ptr = System.IntPtr;

namespace SharpLSL.Interop
{
// TODO: Encoding
public static unsafe partial class LSL
{
[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_child([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name);
public static extern lsl_xml_ptr lsl_child(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_next_sibling_n([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name);
public static extern lsl_xml_ptr lsl_next_sibling_n(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_previous_sibling_n([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name);
public static extern lsl_xml_ptr lsl_previous_sibling_n(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_child_value_n([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name);
public static extern lsl_xml_ptr lsl_child_value_n(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_append_child_value([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name, string value);
public static extern lsl_xml_ptr lsl_append_child_value(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_prepend_child_value([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name, string value);
public static extern lsl_xml_ptr lsl_prepend_child_value(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern int lsl_set_child_value([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name, string value);
public static extern int lsl_set_child_value(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern int lsl_set_name([NativeTypeName("lsl_xml_ptr")] IntPtr e, string rhs);
public static extern int lsl_set_name(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string rhs);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern int lsl_set_value([NativeTypeName("lsl_xml_ptr")] IntPtr e, string rhs);
public static extern int lsl_set_value(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string rhs);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_append_child([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name);
public static extern lsl_xml_ptr lsl_append_child(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern IntPtr lsl_prepend_child([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name);
public static extern lsl_xml_ptr lsl_prepend_child(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);

[DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void lsl_remove_child_n([NativeTypeName("lsl_xml_ptr")] IntPtr e, string name);
public static extern void lsl_remove_child_n(lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);

/*
Expand Down
189 changes: 175 additions & 14 deletions Source/SharpLSL/XMLElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,166 @@

namespace SharpLSL
{
/// <summary>
/// Represents a lightweight XML element tree, modeling the <see cref="StreamInfo.Description"/>
/// field of <see cref="StreamInfo"/>.
/// </summary>
/// <remarks>
/// Each element has a name and can contain multiple named child elements or
/// have text content as its value; attributes are omitted. The interface is
/// modeled after a subset of pugixml's node type and is compatible with it.
/// See https://pugixml.org/docs/manual.html#access for more details.
/// </remarks>
public class XMLElement
{
public XMLElement(IntPtr handle)
/// <summary>
/// Constructs a new instance of the <see cref="XMLElement"/> class which
/// wraps a pre-existing native XML node handle.
/// </summary>
/// <param name="handle">
/// Specifies the handle to be wrapped.
/// </param>
internal XMLElement(IntPtr handle)
{
if (handle == IntPtr.Zero)
throw new ArgumentException("Invalid Xml node pointer.");

handle_ = handle;
}

public XMLElement FirstChild() => new XMLElement(lsl_first_child(handle_));
/// <summary>
/// Gets a value indicating whether the element is null.
/// </summary>
/// <seealso cref="Null"/>
public bool IsNull => handle_ == IntPtr.Zero;

public XMLElement LastChild() => new XMLElement(lsl_last_child(handle_));
/// <summary>
/// Gets a value indicating whether the element is empty.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown when the handle is invalid.
/// </exception>
public bool IsEmpty
{
get
{
ThrowIfInvalid();
return lsl_empty(handle_) != 0;
}
}

public XMLElement Child(string name) => new XMLElement(lsl_child(handle_, name));
/// <summary>
/// Gets the parent node of the element.
/// </summary>
/// <returns>
/// The parent node of the element, or <see cref="Null"/> if the parent node
/// doesn't exist.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if current element is invalid.
/// </exception>
public XMLElement Parent()
{
ThrowIfInvalid();

public XMLElement NextSibling() => new XMLElement(lsl_next_sibling(handle_));
var parent = lsl_parent(handle_);
if (parent != IntPtr.Zero)
return new XMLElement(parent);

public XMLElement NextSibling(string name) => new XMLElement(lsl_next_sibling_n(handle_, name));
return Null;
}

public XMLElement PreviousSibling() => new XMLElement(lsl_previous_sibling(handle_));
/// <summary>
/// Gets the first child of the element.
/// </summary>
/// <returns>
/// The first child of the element, or <see cref="Null"/> if the element has
/// no children.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if current element is invalid.
/// </exception>
public XMLElement FirstChild()
{
ThrowIfInvalid();

var node = lsl_first_child(handle_);
if (node != IntPtr.Zero)
return new XMLElement(node);

public XMLElement PreviousSibling(string name) => new XMLElement(lsl_previous_sibling_n(handle_, name));
return Null;
}

/// <summary>
/// Gets the last child of the element.
/// </summary>
/// <returns>
/// The last child of the element, or <see cref="Null"/> if the element has
/// no children.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if current element is invalid.
/// </exception>
public XMLElement LastChild()
{
ThrowIfInvalid();

var node = lsl_last_child(handle_);
if (node != IntPtr.Zero)
return new XMLElement(node);

return Null;
}

/// <summary>
/// Gets the next sibling of the element.
/// </summary>
/// <returns>
/// The next sibling of the element, or <see cref="Null"/> if the element
/// is the last node in the list.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if current element is invalid.
/// </exception>
public XMLElement NextSibling()
{
ThrowIfInvalid();

var node = lsl_next_sibling(handle_);
if (node != IntPtr.Zero)
return new XMLElement(node);

return Null;
}

/// <summary>
/// Gets the previous sibling of the element.
/// </summary>
/// <returns>
/// The previous sibling of the element, or <see cref="Null"/> if the element
/// is the first node in the list.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if current element is invalid.
/// </exception>
public XMLElement PreviousSibling()
{
ThrowIfInvalid();

public XMLElement Parent() => new XMLElement(lsl_parent(handle_));
var node = lsl_previous_sibling(handle_);
if (node != IntPtr.Zero)
return new XMLElement(node);

return Null;
}

public bool IsEmpty => lsl_empty(handle_) != 0;

public XMLElement Child(string name)
{

return new XMLElement(lsl_child(handle_, name));
}

public XMLElement NextSibling(string name) => new XMLElement(lsl_next_sibling_n(handle_, name));

public XMLElement PreviousSibling(string name) => new XMLElement(lsl_previous_sibling_n(handle_, name));

public bool IsText => lsl_is_text(handle_) != 0;

Expand Down Expand Up @@ -98,6 +231,34 @@ public void RemoveChild(XMLElement element)
lsl_remove_child(handle_, element.handle_);
}

private IntPtr handle_;
/// <summary>
/// Represents a null XML element.
/// </summary>
/// <remarks>
/// The traversal functions provided by <see cref="XMLElement"/> may return
/// this value if no element is found.
/// </remarks>
/// <seealso cref="IsNull"/>
public static readonly XMLElement Null = new XMLElement(IntPtr.Zero);

/// <summary>
/// Throws an <see cref="InvalidOperationException"/> if the handle is invalid.
/// </summary>
/// <exception cref="InvalidOperationException">
/// Thrown when the handle is invalid.
/// </exception>
protected void ThrowIfInvalid()
{
if (handle_ == IntPtr.Zero)
throw new InvalidOperationException("The XML node handle is invalid.");
}

private readonly IntPtr handle_;
}
}


// References:
// https://github.com/labstreaminglayer/liblsl-Csharp/blob/master/LSL.cs
// https://pugixml.org/docs/manual.html#access
// https://github.com/zeux/pugixml/blob/master/src/pugixml.hpp
9 changes: 7 additions & 2 deletions Tests/SharpLSL.Test/CommonTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ namespace SharpLSL.Test;
public class CommonTest
{
[Fact]
public void Given_When_Then()
public void TestVersion()
{
Assert.Equal(4, sizeof(ChannelFormat));
Assert.Equal(114, LIBLSL_COMPILE_HEADER_VERSION);
}

[Fact]
public void TestChannelFormat()
{
Assert.Equal(4, sizeof(ChannelFormat));
}
}

0 comments on commit 0e2366c

Please sign in to comment.