diff --git a/Rubjerg.Graphviz.Test/TestInterop.cs b/Rubjerg.Graphviz.Test/TestInterop.cs
index 1d6541c..94f2218 100644
--- a/Rubjerg.Graphviz.Test/TestInterop.cs
+++ b/Rubjerg.Graphviz.Test/TestInterop.cs
@@ -40,11 +40,12 @@ public void TestMarshaling()
public void TestOutputEncoding()
{
var root = Utils.CreateUniqueTestGraph();
- var nodeA = root.GetOrAddNode("©");
+ var nodeA = root.GetOrAddNode("A");
+ nodeA.SetAttribute("label", "✅");
var dotStr = root.ToDotString();
- Assert.IsTrue(dotStr.Contains("©"));
- var svgStr = root.ToSvgString();
- Assert.IsTrue(svgStr.Contains("©"));
+ Assert.IsTrue(dotStr.Contains("✅"));
+ //var svgStr = root.ToSvgString();
+ //Assert.IsTrue(svgStr.Contains("©"));
// FIXNOW: test with files
}
}
diff --git a/Rubjerg.Graphviz/CGraphThing.cs b/Rubjerg.Graphviz/CGraphThing.cs
index 18fb365..9b769c5 100644
--- a/Rubjerg.Graphviz/CGraphThing.cs
+++ b/Rubjerg.Graphviz/CGraphThing.cs
@@ -20,15 +20,15 @@ public abstract class CGraphThing : GraphvizThing
/// Argument root may be null.
/// In that case, it is assumed this is a RootGraph, and MyRootGraph is set to `this`.
///
- internal CGraphThing(IntPtr ptr, RootGraph root) : base(ptr)
+ internal CGraphThing(IntPtr ptr, RootGraph? root) : base(ptr)
{
- if (root == null)
+ if (root is null)
MyRootGraph = (RootGraph)this;
else
MyRootGraph = root;
}
- protected static string NameString(string name)
+ protected static string? NameString(string? name)
{
// Because graphviz does not properly export empty strings to dot, this opens a can of worms.
// So we disallow it, and map it onto null.
@@ -40,7 +40,7 @@ protected static string NameString(string name)
/// Identifier for this object. Used to distinghuish multi edges.
/// Edges can be nameless, and in that case this method returns null.
///
- public string GetName()
+ public string? GetName()
{
return NameString(Rjagnameof(_ptr));
}
@@ -53,7 +53,7 @@ public bool HasAttribute(string name)
///
/// Set attribute, and introduce it with the given default if it is not introduced yet.
///
- public void SafeSetAttribute(string name, string value, string deflt)
+ public void SafeSetAttribute(string name, string? value, string? deflt)
{
_ = deflt ?? throw new ArgumentNullException(nameof(deflt));
Agsafeset(_ptr, name, value, deflt);
@@ -62,7 +62,7 @@ public void SafeSetAttribute(string name, string value, string deflt)
///
/// Set attribute, and introduce it with the empty string if it does not exist yet.
///
- public void SetAttribute(string name, string value)
+ public void SetAttribute(string name, string? value)
{
Agsafeset(_ptr, name, value, "");
}
@@ -71,7 +71,7 @@ public void SetAttribute(string name, string value)
/// Get the attribute value for this object, or the default value of the attribute if no explicit value was set.
/// If the attribute was not introduced, return null.
///
- public string GetAttribute(string name)
+ public string? GetAttribute(string name)
{
return Agget(_ptr, name);
}
@@ -79,10 +79,10 @@ public string GetAttribute(string name)
///
/// Get the attribute if it was introduced and contains a non-empty value, otherwise return deflt.
///
- public string SafeGetAttribute(string name, string deflt)
+ public string? SafeGetAttribute(string name, string? deflt)
{
if (HasAttribute(name))
- return GetAttribute(name);
+ return GetAttribute(name)!;
return deflt;
}
@@ -103,13 +103,13 @@ public Dictionary GetAttributes()
IntPtr sym = Agnxtattr(MyRootGraph._ptr, kind, IntPtr.Zero);
while (sym != IntPtr.Zero)
{
- string key = ImsymKey(sym);
- if (HasAttribute(key))
+ string? key = ImsymKey(sym);
+ if (key is not null && HasAttribute(key))
{
- string value = GetAttribute(key);
+ string? value = GetAttribute(key);
if (!string.IsNullOrEmpty(value))
{
- attributes[key] = value;
+ attributes[key] = value!;
}
}
sym = Agnxtattr(MyRootGraph._ptr, kind, sym);
diff --git a/Rubjerg.Graphviz/Edge.cs b/Rubjerg.Graphviz/Edge.cs
index 230445a..bb6cc76 100644
--- a/Rubjerg.Graphviz/Edge.cs
+++ b/Rubjerg.Graphviz/Edge.cs
@@ -12,7 +12,7 @@ public class Edge : CGraphThing
///
internal Edge(IntPtr ptr, RootGraph rootgraph) : base(ptr, rootgraph) { }
- internal static Edge Get(Graph graph, Node tail, Node head, string name)
+ internal static Edge? Get(Graph graph, Node tail, Node head, string? name)
{
name = NameString(name);
IntPtr ptr = Agedge(graph._ptr, tail._ptr, head._ptr, name, 0);
@@ -21,7 +21,7 @@ internal static Edge Get(Graph graph, Node tail, Node head, string name)
return new Edge(ptr, graph.MyRootGraph);
}
- internal static Edge GetOrCreate(Graph graph, Node tail, Node head, string name)
+ internal static Edge GetOrCreate(Graph graph, Node tail, Node head, string? name)
{
name = NameString(name);
IntPtr ptr = Agedge(graph._ptr, tail._ptr, head._ptr, name, 1);
@@ -92,7 +92,7 @@ public void SetLogicalTail(SubGraph ltail)
throw new InvalidOperationException("ltail must be a cluster");
if (!MyRootGraph.IsCompound())
throw new InvalidOperationException("rootgraph must be compound for lheads/ltails to be used");
- string ltailname = ltail.GetName();
+ string? ltailname = ltail.GetName();
SetAttribute("ltail", ltailname);
}
@@ -106,7 +106,7 @@ public void SetLogicalHead(SubGraph lhead)
throw new InvalidOperationException("ltail must be a cluster");
if (!MyRootGraph.IsCompound())
throw new InvalidOperationException("rootgraph must be compound for lheads/ltails to be used");
- string lheadname = lhead.GetName();
+ string? lheadname = lhead.GetName();
SetAttribute("lhead", lheadname);
}
@@ -128,7 +128,7 @@ public static string ConvertUidToPortName(string id)
// Because there are two valid pointers to each edge, we have to override the default equals behaviour
// which simply compares the wrapped pointers.
- public override bool Equals(GraphvizThing obj)
+ public override bool Equals(GraphvizThing? obj)
{
if (obj is Edge)
return Ageqedge(_ptr, obj._ptr);
diff --git a/Rubjerg.Graphviz/ForeignFunctionInterface.cs b/Rubjerg.Graphviz/ForeignFunctionInterface.cs
index 4592978..6daf130 100644
--- a/Rubjerg.Graphviz/ForeignFunctionInterface.cs
+++ b/Rubjerg.Graphviz/ForeignFunctionInterface.cs
@@ -41,14 +41,14 @@ public static int GvFreeLayout(IntPtr gvc, IntPtr graph)
return gvFreeLayout(gvc, graph);
}
}
- public static int GvRender(IntPtr gvc, IntPtr graph, string format, IntPtr @out)
+ public static int GvRender(IntPtr gvc, IntPtr graph, string? format, IntPtr @out)
{
lock (_mutex)
{
return MarshalToUtf8(format, formatPtr => gvRender(gvc, graph, formatPtr, @out));
}
}
- public static int GvRenderFilename(IntPtr gvc, IntPtr graph, string format, string filename)
+ public static int GvRenderFilename(IntPtr gvc, IntPtr graph, string? format, string? filename)
{
lock (_mutex)
{
@@ -58,7 +58,7 @@ public static int GvRenderFilename(IntPtr gvc, IntPtr graph, string format, stri
gvRenderFilename(gvc, graph, formatPtr, filenamePtr)));
}
}
- public static IntPtr Agnode(IntPtr graph, string name, int create)
+ public static IntPtr Agnode(IntPtr graph, string? name, int create)
{
lock (_mutex)
{
@@ -159,7 +159,7 @@ public static void AgsetHtml(IntPtr obj, string name, string value)
}
}
- public static void Agsafeset(IntPtr obj, string name, string val, string deflt)
+ public static void Agsafeset(IntPtr obj, string name, string? val, string? deflt)
{
lock (_mutex)
{
@@ -169,7 +169,7 @@ public static void Agsafeset(IntPtr obj, string name, string val, string deflt)
agsafeset(obj, namePtr, valPtr, defltPtr))));
}
}
- public static void AgsafesetHtml(IntPtr obj, string name, string val, string deflt)
+ public static void AgsafesetHtml(IntPtr obj, string name, string? val, string? deflt)
{
lock (_mutex)
{
@@ -224,7 +224,7 @@ public static IntPtr Aghead(IntPtr node)
return rj_aghead(node);
}
}
- public static IntPtr Agedge(IntPtr graph, IntPtr tail, IntPtr head, string name, int create)
+ public static IntPtr Agedge(IntPtr graph, IntPtr tail, IntPtr head, string? name, int create)
{
lock (_mutex)
{
@@ -287,7 +287,7 @@ public static int Agcontains(IntPtr graph, IntPtr obj)
return agcontains(graph, obj);
}
}
- public static IntPtr Agsubg(IntPtr graph, string name, int create)
+ public static IntPtr Agsubg(IntPtr graph, string? name, int create)
{
lock (_mutex)
{
@@ -350,7 +350,7 @@ public static IntPtr EdgeLabel(IntPtr node)
return edge_label(node);
}
}
- public static string Rjagmemwrite(IntPtr graph)
+ public static string? Rjagmemwrite(IntPtr graph)
{
lock (_mutex)
{
@@ -365,14 +365,14 @@ public static IntPtr GraphLabel(IntPtr node)
return graph_label(node);
}
}
- public static string Agget(IntPtr obj, string name)
+ public static string? Agget(IntPtr obj, string name)
{
lock (_mutex)
{
return MarshalToUtf8(name, namePtr => MarshalFromUtf8(agget(obj, namePtr), false));
}
}
- public static string Rjagnameof(IntPtr obj)
+ public static string? Rjagnameof(IntPtr obj)
{
lock (_mutex)
{
@@ -386,7 +386,7 @@ public static void CloneAttributeDeclarations(IntPtr graphfrom, IntPtr graphto)
clone_attribute_declarations(graphfrom, graphto);
}
}
- public static string ImsymKey(IntPtr sym)
+ public static string? ImsymKey(IntPtr sym)
{
lock (_mutex)
{
@@ -421,7 +421,7 @@ public static double LabelHeight(IntPtr label)
return label_height(label);
}
}
- public static string LabelText(IntPtr label)
+ public static string? LabelText(IntPtr label)
{
lock (_mutex)
{
@@ -435,7 +435,7 @@ public static double LabelFontsize(IntPtr label)
return label_fontsize(label);
}
}
- public static string LabelFontname(IntPtr label)
+ public static string? LabelFontname(IntPtr label)
{
lock (_mutex)
{
@@ -491,7 +491,7 @@ public static IntPtr Rjagmemread(string input)
return MarshalToUtf8(input, rj_agmemread);
}
}
- public static IntPtr Rjagopen(string name, int graphtype)
+ public static IntPtr Rjagopen(string? name, int graphtype)
{
lock (_mutex)
{
@@ -709,7 +709,7 @@ public enum TestEnum
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr return_copyright();
- public static string EchoString(string str)
+ public static string? EchoString(string? str)
{
return MarshalToUtf8(str, ptr =>
{
@@ -718,8 +718,8 @@ public static string EchoString(string str)
return MarshalFromUtf8(returnPtr, true);
});
}
- public static string ReturnEmptyString() => MarshalFromUtf8(return_empty_string(), false);
- public static string ReturnHello() => MarshalFromUtf8(return_hello(), false);
- public static string ReturnCopyRight() => MarshalFromUtf8(return_copyright(), false);
+ public static string? ReturnEmptyString() => MarshalFromUtf8(return_empty_string(), false);
+ public static string? ReturnHello() => MarshalFromUtf8(return_hello(), false);
+ public static string? ReturnCopyRight() => MarshalFromUtf8(return_copyright(), false);
#endregion
}
diff --git a/Rubjerg.Graphviz/Graph.cs b/Rubjerg.Graphviz/Graph.cs
index e0d4cda..d430570 100644
--- a/Rubjerg.Graphviz/Graph.cs
+++ b/Rubjerg.Graphviz/Graph.cs
@@ -15,7 +15,7 @@ public class Graph : CGraphThing
///
/// rootgraph may be null
///
- protected Graph(IntPtr ptr, RootGraph rootgraph) : base(ptr, rootgraph) { }
+ protected Graph(IntPtr ptr, RootGraph? rootgraph) : base(ptr, rootgraph) { }
public bool IsStrict() { return Agisstrict(_ptr) != 0; }
public bool IsDirected() { return Agisdirected(_ptr) != 0; }
public bool IsUndirected() { return Agisundirected(_ptr) != 0; }
@@ -100,7 +100,7 @@ public IEnumerable Descendants()
}
}
- public Graph Parent()
+ public Graph? Parent()
{
IntPtr p = Agparent(_ptr);
if (p == IntPtr.Zero)
@@ -123,7 +123,7 @@ public SubGraph GetOrAddSubgraph(string name)
return SubGraph.GetOrCreate(this, name);
}
- public SubGraph GetSubgraph(string name)
+ public SubGraph? GetSubgraph(string? name)
{
return SubGraph.Get(this, name);
}
@@ -133,7 +133,7 @@ public Node GetOrAddNode(string name)
return Node.GetOrCreate(this, name);
}
- public Node GetNode(string name)
+ public Node? GetNode(string? name)
{
return Node.Get(this, name);
}
@@ -142,7 +142,7 @@ public Node GetNode(string name)
/// Passing null as edge name (the default) will result in a new unique edge without a name.
/// Passing the empty string has the same effect as passing null.
///
- public Edge GetOrAddEdge(Node tail, Node head, string name = null)
+ public Edge GetOrAddEdge(Node tail, Node head, string? name = null)
{
return Edge.GetOrCreate(this, tail, head, name);
}
@@ -151,7 +151,7 @@ public Edge GetOrAddEdge(Node tail, Node head, string name = null)
/// Passing null as edge name will return any edge between the given endpoints, regardless of their name.
/// Passing the empty string has the same effect as passing null.
///
- public Edge GetEdge(Node tail, Node head, string name = null)
+ public Edge? GetEdge(Node tail, Node head, string? name = null)
{
return Edge.Get(this, tail, head, name);
}
@@ -161,7 +161,7 @@ public Edge GetEdge(Node tail, Node head, string name = null)
/// Passing null as edge name will return any edge between the given endpoints, regardless of their name.
/// Passing the empty string has the same effect as passing null.
///
- public Edge GetEdgeBetween(Node node1, Node node2, string name = null)
+ public Edge? GetEdgeBetween(Node node1, Node node2, string? name = null)
{
return Edge.Get(this, node1, node2, name) ?? Edge.Get(this, node2, node1, name);
}
@@ -181,7 +181,7 @@ public override string ToString()
/// https://gitlab.com/graphviz/graphviz/-/issues/1887
///
/// string with unix line endings
- public string ToDotString()
+ public string? ToDotString()
{
return Rjagmemwrite(_ptr);
}
@@ -218,6 +218,7 @@ public SubGraph AddSubgraphFromEdgeSet(string name, HashSet edges)
///
public SubGraph AddSubgraphFromNodes(string name, IEnumerable nodes)
{
+#nullable disable
// Freeze the list of descendants,
// since we are going to add subgraphs while iterating over existing subgraphs
List descendants = Descendants().ToList();
@@ -265,6 +266,7 @@ public SubGraph AddSubgraphFromNodes(string name, IEnumerable nodes)
Debug.Assert(result.Nodes().Count() == nodes.Count());
return result;
+#nullable enable
}
///
@@ -324,6 +326,7 @@ public RootGraph Clone(string resultname)
public void CloneInto(RootGraph target)
{
+#nullable disable
// Copy all nodes and edges
foreach (var node in Nodes())
{
@@ -391,6 +394,7 @@ public void CloneInto(RootGraph target)
_ = node.CopyAttributesTo(newnode);
}
}
+#nullable enable
}
///
@@ -464,7 +468,7 @@ public void Merge(Node merge, Node target, bool add_self_loops = false)
public bool IsCluster()
{
- return GetName().StartsWith("cluster");
+ return GetName()?.StartsWith("cluster") ?? false;
}
///
@@ -576,10 +580,10 @@ public RootGraph CreateLayout(string engine = LayoutEngines.Dot, CoordinateSyste
///
internal RectangleD RawBoundingBox()
{
- string bb_string = Agget(_ptr, "bb");
+ string? bb_string = Agget(_ptr, "bb");
if (string.IsNullOrEmpty(bb_string))
return default;
- return ParseRect(bb_string);
+ return ParseRect(bb_string!);
}
internal double RawMaxY()
diff --git a/Rubjerg.Graphviz/GraphComparer.cs b/Rubjerg.Graphviz/GraphComparer.cs
index 6657d50..8bc3190 100644
--- a/Rubjerg.Graphviz/GraphComparer.cs
+++ b/Rubjerg.Graphviz/GraphComparer.cs
@@ -12,23 +12,25 @@ public static bool CheckTopologicallyEquals(Graph A, Graph B, Action log
logger("");
bool result = true;
- var common_nodenames = new List();
+ var common_nodenames = new List();
foreach (var node in A.Nodes())
{
var othernode = B.GetNode(node.GetName());
- if (othernode == null)
+ if (othernode is null)
{
logger($"graph B does not contain node {node.GetName()}");
result = false;
- continue;
}
- common_nodenames.Add(node.GetName());
+ else
+ {
+ common_nodenames.Add(node.GetName());
+ }
}
foreach (var node in B.Nodes())
{
var othernode = A.GetNode(node.GetName());
- if (othernode == null)
+ if (othernode is null)
{
logger($"graph A does not contain node {node.GetName()}");
result = false;
@@ -45,9 +47,9 @@ public static bool CheckTopologicallyEquals(Graph A, Graph B, Action log
return result;
}
- private static bool CheckNode(Graph A, Graph B, Node nA, Node nB, Action logger)
+ private static bool CheckNode(Graph A, Graph B, Node? nA, Node? nB, Action logger)
{
- return InnerCheckNode(A, B, nA, nB, logger, "B") & InnerCheckNode(B, A, nA, nB, logger, "A");
+ return nA is not null && nB is not null && InnerCheckNode(A, B, nA, nB, logger, "B") & InnerCheckNode(B, A, nA, nB, logger, "A");
}
private static bool InnerCheckNode(Graph A, Graph B, Node nA, Node nB, Action logger, string nameOfGraphOfNodeB)
diff --git a/Rubjerg.Graphviz/GraphVizThing.cs b/Rubjerg.Graphviz/GraphVizThing.cs
index 3da813e..bd9bd1d 100644
--- a/Rubjerg.Graphviz/GraphVizThing.cs
+++ b/Rubjerg.Graphviz/GraphVizThing.cs
@@ -26,14 +26,14 @@ protected GraphvizThing(IntPtr ptr)
_ptr = ptr;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return Equals(obj as GraphvizThing);
}
- public virtual bool Equals(GraphvizThing obj)
+ public virtual bool Equals(GraphvizThing? obj)
{
- return obj != null && _ptr == obj._ptr;
+ return obj is not null && _ptr == obj._ptr;
}
public override int GetHashCode()
diff --git a/Rubjerg.Graphviz/GraphvizCommand.cs b/Rubjerg.Graphviz/GraphvizCommand.cs
index 5a7b7cc..eba4d39 100644
--- a/Rubjerg.Graphviz/GraphvizCommand.cs
+++ b/Rubjerg.Graphviz/GraphvizCommand.cs
@@ -36,7 +36,7 @@ public static string ConvertBytesToString(byte[] data)
///
/// When the Graphviz process did not return successfully
/// stderr may contain warnings
- public static (byte[] stdout, string stderr) Exec(Graph input, string format = "xdot", string outputPath = null, string engine = LayoutEngines.Dot)
+ public static (byte[] stdout, string stderr) Exec(Graph input, string format = "xdot", string? outputPath = null, string engine = LayoutEngines.Dot)
{
string exeName = "dot.exe";
string arguments = $"-T{format} -K{engine}";
@@ -44,7 +44,7 @@ public static (byte[] stdout, string stderr) Exec(Graph input, string format = "
{
arguments = $"{arguments} -o\"{outputPath}\"";
}
- string inputToStdin = input.ToDotString();
+ string? inputToStdin = input.ToDotString();
// Get the location of the currently executing DLL
// https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.codebase?view=net-5.0
diff --git a/Rubjerg.Graphviz/Marshaling.cs b/Rubjerg.Graphviz/Marshaling.cs
index d6d2774..639de17 100644
--- a/Rubjerg.Graphviz/Marshaling.cs
+++ b/Rubjerg.Graphviz/Marshaling.cs
@@ -13,12 +13,15 @@ internal static class Marshaling
/// Pointer to the native c-string
/// Whether to free the native c-string after marshaling
/// .NET unicode string
- public static string MarshalFromUtf8(IntPtr ptr, bool free)
+ public static string? MarshalFromUtf8(IntPtr ptr, bool free)
{
- return Encoding.UTF8.GetString(CopyCharPtrToByteArray(ptr, free));
+ var bytes = CopyCharPtrToByteArray(ptr, free);
+ if (bytes is null)
+ return null;
+ return Encoding.UTF8.GetString(bytes);
}
- public static void MarshalToUtf8(string s, Action continuation, bool free = true)
+ public static void MarshalToUtf8(string? s, Action continuation, bool free = true)
{
_ = MarshalToUtf8(s, ptr => { continuation(ptr); return 0; }, free);
}
@@ -30,34 +33,41 @@ public static void MarshalToUtf8(string s, Action continuation, bool fre
/// Whether to free the allocated native c-string after the action returned
/// the continuation consuming the native c-string
///
- public static T MarshalToUtf8(string s, Func continuation, bool free = true)
+ public static T MarshalToUtf8(string? s, Func continuation, bool free = true)
{
- var bytes = Encoding.UTF8.GetBytes(s);
IntPtr ptr = IntPtr.Zero;
- try
+ if (s is null)
{
- // allocate native c-string with an extra byte for the null terminator
- ptr = Marshal.AllocHGlobal(bytes.Length + 1);
-
- // copy the UTF8 bytes to the allocated memory
- Marshal.Copy(bytes, 0, ptr, bytes.Length);
-
- // set the null terminator
- Marshal.WriteByte(ptr + bytes.Length, 0);
-
- // call the continuation function with the native pointer
return continuation(ptr);
}
- finally
+ else
{
- if (free && ptr != IntPtr.Zero)
+ var bytes = Encoding.UTF8.GetBytes(s);
+ try
+ {
+ // allocate native c-string with an extra byte for the null terminator
+ ptr = Marshal.AllocHGlobal(bytes.Length + 1);
+
+ // copy the UTF8 bytes to the allocated memory
+ Marshal.Copy(bytes, 0, ptr, bytes.Length);
+
+ // set the null terminator
+ Marshal.WriteByte(ptr + bytes.Length, 0);
+
+ // call the continuation function with the native pointer
+ return continuation(ptr);
+ }
+ finally
{
- Marshal.FreeHGlobal(ptr);
+ if (free && ptr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
}
}
}
- public static byte[] CopyCharPtrToByteArray(IntPtr ptr, bool free)
+ public static byte[]? CopyCharPtrToByteArray(IntPtr ptr, bool free)
{
if (ptr == IntPtr.Zero) return null;
diff --git a/Rubjerg.Graphviz/Node.cs b/Rubjerg.Graphviz/Node.cs
index 3048f2c..5b081a7 100644
--- a/Rubjerg.Graphviz/Node.cs
+++ b/Rubjerg.Graphviz/Node.cs
@@ -13,7 +13,7 @@ public class Node : CGraphThing
///
internal Node(IntPtr ptr, RootGraph rootgraph) : base(ptr, rootgraph) { }
- internal static Node Get(Graph graph, string name)
+ internal static Node? Get(Graph graph, string? name)
{
name = NameString(name);
IntPtr ptr = Agnode(graph._ptr, name, 0);
@@ -24,7 +24,7 @@ internal static Node Get(Graph graph, string name)
internal static Node GetOrCreate(Graph graph, string name)
{
- name = NameString(name);
+ name = NameString(name)!;
IntPtr ptr = Agnode(graph._ptr, name, 1);
return new Node(ptr, graph.MyRootGraph);
}
@@ -45,7 +45,7 @@ public static void IntroduceAttributeHtml(RootGraph root, string name, string de
AgattrHtml(root._ptr, 1, name, deflt);
}
- public IEnumerable EdgesOut(Graph graph = null)
+ public IEnumerable EdgesOut(Graph? graph = null)
{
IntPtr graph_ptr = graph?._ptr ?? MyRootGraph._ptr;
var current = Agfstout(graph_ptr, _ptr);
@@ -56,7 +56,7 @@ public IEnumerable EdgesOut(Graph graph = null)
}
}
- public IEnumerable EdgesIn(Graph graph = null)
+ public IEnumerable EdgesIn(Graph? graph = null)
{
IntPtr graph_ptr = graph?._ptr ?? MyRootGraph._ptr;
var current = Agfstin(graph_ptr, _ptr);
@@ -70,7 +70,7 @@ public IEnumerable EdgesIn(Graph graph = null)
///
/// Iterate over both in and out edges. This will not yield self loops twice.
///
- public IEnumerable Edges(Graph graph = null)
+ public IEnumerable Edges(Graph? graph = null)
{
IntPtr graph_ptr = graph?._ptr ?? MyRootGraph._ptr;
var current = Agfstedge(graph_ptr, _ptr);
@@ -84,7 +84,7 @@ public IEnumerable Edges(Graph graph = null)
///
/// Get all neighbors connected via an out edge.
///
- public IEnumerable NeighborsOut(Graph graph = null)
+ public IEnumerable NeighborsOut(Graph? graph = null)
{
return EdgesOut(graph).Select(e => e.OppositeEndpoint(this));
}
@@ -92,7 +92,7 @@ public IEnumerable NeighborsOut(Graph graph = null)
///
/// Get all neighbors connected via an in edge.
///
- public IEnumerable NeighborsIn(Graph graph = null)
+ public IEnumerable NeighborsIn(Graph? graph = null)
{
return EdgesIn(graph).Select(e => e.OppositeEndpoint(this));
}
@@ -100,7 +100,7 @@ public IEnumerable NeighborsIn(Graph graph = null)
///
/// Get all neighbors.
///
- public IEnumerable Neighbors(Graph graph = null)
+ public IEnumerable Neighbors(Graph? graph = null)
{
return Edges(graph).Select(e => e.OppositeEndpoint(this));
}
@@ -108,7 +108,7 @@ public IEnumerable Neighbors(Graph graph = null)
///
/// Get all neighbors fullfilling a given attribute constraint.
///
- public IEnumerable NeighborsByAttribute(string attr_name, string attr_value, Graph graph = null)
+ public IEnumerable NeighborsByAttribute(string attr_name, string attr_value, Graph? graph = null)
{
return Neighbors(graph).Where(n => n.GetAttribute(attr_name) == attr_value);
}
@@ -116,7 +116,7 @@ public IEnumerable NeighborsByAttribute(string attr_name, string attr_valu
///
/// Get all neighbors connected by an edge with given name.
///
- public IEnumerable NeighborsByEdgeName(string edgename, Graph graph = null)
+ public IEnumerable NeighborsByEdgeName(string edgename, Graph? graph = null)
{
return Edges(graph).Where(e => e.GetName() == edgename).Select(e => e.OppositeEndpoint(this));
}
@@ -129,24 +129,24 @@ public IEnumerable NeighborsByEdgeName(string edgename, Graph graph = null
///
public Node CopyToOtherRoot(RootGraph destination)
{
- Node result = destination.GetOrAddNode(GetName());
+ Node result = destination.GetOrAddNode(GetName()!);
_ = CopyAttributesTo(result);
return result;
}
- public int OutDegree(Graph graph = null)
+ public int OutDegree(Graph? graph = null)
{
IntPtr graph_ptr = graph?._ptr ?? MyRootGraph._ptr;
return Agdegree(graph_ptr, _ptr, 0, 1);
}
- public int InDegree(Graph graph = null)
+ public int InDegree(Graph? graph = null)
{
IntPtr graph_ptr = graph?._ptr ?? MyRootGraph._ptr;
return Agdegree(graph_ptr, _ptr, 1, 0);
}
- public int TotalDegree(Graph graph = null)
+ public int TotalDegree(Graph? graph = null)
{
IntPtr graph_ptr = graph?._ptr ?? MyRootGraph._ptr;
return Agdegree(graph_ptr, _ptr, 1, 1);
@@ -175,9 +175,8 @@ public PointD GetPosition()
{
// The "pos" attribute is available as part of xdot output
PointD result;
- if (HasAttribute("pos"))
+ if (HasAttribute("pos") && GetAttribute("pos") is string posString)
{
- var posString = GetAttribute("pos");
var coords = posString.Split(',');
double x = double.Parse(coords[0], NumberStyles.Any, CultureInfo.InvariantCulture);
double y = double.Parse(coords[1], NumberStyles.Any, CultureInfo.InvariantCulture);
@@ -236,35 +235,35 @@ public RectangleD GetBoundingBox()
///
public IEnumerable GetRecordRectangles(bool snapOntoDrawingCoordinates = false)
{
- if (!HasAttribute("rects"))
- yield break;
-
- var polylinePoints = GetDrawing().OfType().SelectMany(p => p.Points).ToList();
- var validXCoords = polylinePoints.Select(p => p.X).OrderBy(x => x).Distinct().ToList();
- var validYCoords = polylinePoints.Select(p => p.Y).OrderBy(x => x).Distinct().ToList();
-
- var maxY = MyRootGraph.RawMaxY();
- foreach (string rectStr in GetAttribute("rects").Split(' '))
+ if (HasAttribute("rects") && GetAttribute("rects") is string rects)
{
- var rect = ParseRect(rectStr).ForCoordSystem(MyRootGraph.CoordinateSystem, maxY);
- if (!snapOntoDrawingCoordinates)
- {
- yield return rect;
- }
- else
+ var polylinePoints = GetDrawing().OfType().SelectMany(p => p.Points).ToList();
+ var validXCoords = polylinePoints.Select(p => p.X).OrderBy(x => x).Distinct().ToList();
+ var validYCoords = polylinePoints.Select(p => p.Y).OrderBy(x => x).Distinct().ToList();
+
+ var maxY = MyRootGraph.RawMaxY();
+ foreach (var rectStr in rects.Split(' '))
{
- var x1 = rect.X;
- var x2 = rect.X + rect.Width;
- var y1 = rect.Y;
- var y2 = rect.Y + rect.Height;
- var snappedX1 = FindClosest(validXCoords, x1);
- var snappedX2 = FindClosest(validXCoords, x2);
- var snappedY1 = FindClosest(validYCoords, y1);
- var snappedY2 = FindClosest(validYCoords, y2);
- var snappedRect = new RectangleD(
- new PointD(snappedX1, snappedY1),
- new SizeD(snappedX2 - snappedX1, snappedY2 - snappedY1));
- yield return snappedRect;
+ var rect = ParseRect(rectStr).ForCoordSystem(MyRootGraph.CoordinateSystem, maxY);
+ if (!snapOntoDrawingCoordinates)
+ {
+ yield return rect;
+ }
+ else
+ {
+ var x1 = rect.X;
+ var x2 = rect.X + rect.Width;
+ var y1 = rect.Y;
+ var y2 = rect.Y + rect.Height;
+ var snappedX1 = FindClosest(validXCoords, x1);
+ var snappedX2 = FindClosest(validXCoords, x2);
+ var snappedY1 = FindClosest(validYCoords, y1);
+ var snappedY2 = FindClosest(validYCoords, y2);
+ var snappedRect = new RectangleD(
+ new PointD(snappedX1, snappedY1),
+ new SizeD(snappedX2 - snappedX1, snappedY2 - snappedY1));
+ yield return snappedRect;
+ }
}
}
}
diff --git a/Rubjerg.Graphviz/RootGraph.cs b/Rubjerg.Graphviz/RootGraph.cs
index 51a02d6..07e13db 100644
--- a/Rubjerg.Graphviz/RootGraph.cs
+++ b/Rubjerg.Graphviz/RootGraph.cs
@@ -28,7 +28,7 @@ public class RootGraph : Graph
///
/// Contains any warnings that Graphviz generated during computation of the layout.
///
- public string Warnings { get; internal set; }
+ public string? Warnings { get; internal set; }
protected RootGraph(IntPtr ptr, CoordinateSystem coordinateSystem) : base(ptr, null)
{
@@ -67,7 +67,7 @@ public void UpdateMemoryPressure()
/// The name is not interpreted by Graphviz,
/// except it is recorded and preserved when the graph is written as a file
///
- public static RootGraph CreateNew(GraphType graphtype, string name = null, CoordinateSystem coordinateSystem = CoordinateSystem.BottomLeft)
+ public static RootGraph CreateNew(GraphType graphtype, string? name = null, CoordinateSystem coordinateSystem = CoordinateSystem.BottomLeft)
{
name = NameString(name);
var ptr = Rjagopen(name, (int)graphtype);
diff --git a/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj b/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj
index 55cb4c3..7116749 100644
--- a/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj
+++ b/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj
@@ -8,6 +8,7 @@
latest
AllEnabledByDefault
true
+ Enable
1701;1702;NU5100
2.0.1
Chiel ten Brinke
diff --git a/Rubjerg.Graphviz/SubGraph.cs b/Rubjerg.Graphviz/SubGraph.cs
index b7af1c4..a825ba5 100644
--- a/Rubjerg.Graphviz/SubGraph.cs
+++ b/Rubjerg.Graphviz/SubGraph.cs
@@ -11,7 +11,7 @@ public class SubGraph : Graph
///
internal SubGraph(IntPtr ptr, RootGraph rootgraph) : base(ptr, rootgraph) { }
- internal static SubGraph Get(Graph parent, string name = null)
+ internal static SubGraph? Get(Graph parent, string? name = null)
{
name = NameString(name);
IntPtr ptr = Agsubg(parent._ptr, name, 0);
@@ -21,7 +21,7 @@ internal static SubGraph Get(Graph parent, string name = null)
}
- internal static SubGraph GetOrCreate(Graph parent, string name = null)
+ internal static SubGraph GetOrCreate(Graph parent, string? name = null)
{
name = NameString(name);
IntPtr ptr = Agsubg(parent._ptr, name, 1);
diff --git a/Rubjerg.Graphviz/XDot.cs b/Rubjerg.Graphviz/XDot.cs
index de9abef..54d374f 100644
--- a/Rubjerg.Graphviz/XDot.cs
+++ b/Rubjerg.Graphviz/XDot.cs
@@ -44,7 +44,7 @@ public sealed record class PenColor(Color Value) : XDotOp { }
/// appear. Indeed, if style contains invis, there will not be any xdot output at all.
/// For reference see https://graphviz.org/docs/attr-types/style/
///
- public sealed record class Style(string Value) : XDotOp { }
+ public sealed record class Style(string? Value) : XDotOp { }
}
public interface IHasPoints
@@ -110,7 +110,7 @@ internal RectangleD ForCoordSystem(CoordinateSystem coordSystem, double maxY)
public override string ToString() => $"{{X={X}, Y={Y}, Width={Width}, Height={Height}}}";
}
-public record struct ColorStop(float Frac, string HtmlColor);
+public record struct ColorStop(float Frac, string? HtmlColor);
public record struct LinearGradient(PointD Point0, PointD Point1, ColorStop[] Stops)
{
@@ -133,7 +133,7 @@ internal RadialGradient ForCoordSystem(CoordinateSystem coordSystem, double maxY
public abstract record class Color
{
private Color() { }
- public sealed record class Uniform(string HtmlColor) : Color { }
+ public sealed record class Uniform(string? HtmlColor) : Color { }
public sealed record class Linear(LinearGradient Gradient) : Color { }
public sealed record class Radial(RadialGradient Gradient) : Color { }
}
@@ -159,7 +159,7 @@ public enum TextAlign
///
///
/// Used for computing the bounding box in the correct orientation.
-public record struct TextInfo(PointD Anchor, TextAlign Align, double WidthEstimate, string Text,
+public record struct TextInfo(PointD Anchor, TextAlign Align, double WidthEstimate, string? Text,
Font Font, FontChar FontChar, CoordinateSystem CoordSystem)
{
public SizeD TextSizeEstimate() => new SizeD(WidthEstimate, Font.Size);
@@ -217,7 +217,7 @@ internal TextInfo ForCoordSystem(CoordinateSystem coordSystem, double maxY) => t
};
}
-public record struct ImageInfo(RectangleD Position, string Name)
+public record struct ImageInfo(RectangleD Position, string? Name)
{
internal ImageInfo ForCoordSystem(CoordinateSystem coordSystem, double maxY) => this with
{
@@ -229,7 +229,7 @@ internal ImageInfo ForCoordSystem(CoordinateSystem coordSystem, double maxY) =>
/// Font size in points. This is usually the distance between the ascender and the descender of the font.
///
/// Font name
-public record struct Font(double Size, string Name)
+public record struct Font(double Size, string? Name)
{
public static Font Default => new() { Size = 14, Name = "Times-Roman" };
}
diff --git a/Rubjerg.Graphviz/XDotFFI.cs b/Rubjerg.Graphviz/XDotFFI.cs
index ba0a7e2..ca97728 100644
--- a/Rubjerg.Graphviz/XDotFFI.cs
+++ b/Rubjerg.Graphviz/XDotFFI.cs
@@ -27,7 +27,7 @@ internal static class XDotFFI
// Accessors for xdot_image
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_name_image(IntPtr img);
- public static string GetNameImage(IntPtr img) => MarshalFromUtf8(get_name_image(img), false);
+ public static string? GetNameImage(IntPtr img) => MarshalFromUtf8(get_name_image(img), false);
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_pos(IntPtr img);
@@ -38,7 +38,7 @@ internal static class XDotFFI
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_name_font(IntPtr font);
- public static string GetNameFont(IntPtr img) => MarshalFromUtf8(get_name_font(img), false);
+ public static string? GetNameFont(IntPtr img) => MarshalFromUtf8(get_name_font(img), false);
// Accessors for xdot_op
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
@@ -64,7 +64,7 @@ internal static class XDotFFI
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_color(IntPtr op);
- public static string GetColor(IntPtr op) => MarshalFromUtf8(get_color(op), false);
+ public static string? GetColor(IntPtr op) => MarshalFromUtf8(get_color(op), false);
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_grad_color(IntPtr op);
@@ -74,7 +74,7 @@ internal static class XDotFFI
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_style(IntPtr op);
- public static string GetStyle(IntPtr op) => MarshalFromUtf8(get_style(op), false);
+ public static string? GetStyle(IntPtr op) => MarshalFromUtf8(get_style(op), false);
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern uint get_fontchar(IntPtr op);
@@ -85,7 +85,7 @@ internal static class XDotFFI
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_clr(IntPtr clr);
- public static string GetClr(IntPtr clr) => MarshalFromUtf8(get_clr(clr), false);
+ public static string? GetClr(IntPtr clr) => MarshalFromUtf8(get_clr(clr), false);
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_ling(IntPtr clr);
@@ -108,7 +108,7 @@ internal static class XDotFFI
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_text_str(IntPtr txt);
- public static string GetTextStr(IntPtr txt) => MarshalFromUtf8(get_text_str(txt), false);
+ public static string? GetTextStr(IntPtr txt) => MarshalFromUtf8(get_text_str(txt), false);
// Accessors for xdot_linear_grad
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
@@ -160,7 +160,7 @@ internal static class XDotFFI
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_color_stop(IntPtr stop);
- public static string GetColorStop(IntPtr stop) => MarshalFromUtf8(get_color_stop(stop), false);
+ public static string? GetColorStop(IntPtr stop) => MarshalFromUtf8(get_color_stop(stop), false);
// Accessors for xdot_polyline
[DllImport("GraphvizWrapper.dll", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]