-
Notifications
You must be signed in to change notification settings - Fork 8
/
BBCodeNode.cs
306 lines (255 loc) · 11.3 KB
/
BBCodeNode.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace bbsharp
{
public class BBCodeNode
{
/// <summary>
/// Gets an array of this node's child nodes
/// </summary>
public BBCodeNode[] Children { get { return children.ToArray(); } }
List<BBCodeNode> children;
/// <summary>
/// Gets the parent node of this node.
/// </summary>
public BBCodeNode Parent { get; protected set; }
/// <summary>
/// Gets whether this node is singular. Singular nodes are self closing and can have no children.
/// </summary>
public bool Singular { get; protected set; }
/// <summary>
/// Gets the tag name of this node. The tag name is the main part of the tag, and is mandatory.
/// </summary>
public string TagName { get; protected set; }
/// <summary>
/// Gets or sets this node's attribute. The Attribute is the part of the tag that comes after the equals sign. It is optional, and this property may return either null or an empty string.
/// </summary>
public string Attribute { get; set; }
/// <summary>
/// Gets an array of children BBCodeNodes with the specified TagName
/// </summary>
/// <param name="TagName">The TagName of BBCodeNodes to return</param>
/// <returns>Array of matching BBCodeNodes</returns>
public BBCodeNode[] this[string TagName]
{
get
{
return children.Where(x => x.TagName == TagName).ToArray();
}
}
/// <summary>
/// Gets the nth child BBCodeNode
/// </summary>
/// <param name="Index">The index of the BBCodeNode to access</param>
/// <returns>BBCodeNode at the specified index</returns>
public BBCodeNode this[int Index]
{
get
{
return children[Index];
}
}
/// <summary>
/// Creates a new BBCodeNode.
/// </summary>
/// <param name="TagName">The node's tag name. Mandatory.</param>
/// <param name="Attribute">The node's optional attribute. This may be an empty string or null.</param>
/// <param name="IsSingular">Singular nodes are self closing and may not have children</param>
public BBCodeNode(string TagName, string Attribute, bool IsSingular)
{
if (TagName == null)
throw new ArgumentNullException("TagName cannot be null");
TagName = TagName.Trim();
if (TagName == "")
throw new ArgumentException("TagName cannot be empty");
this.TagName = TagName.ToLower();
this.Attribute = Attribute;
this.Singular = IsSingular;
children = new List<BBCodeNode>();
}
/// <summary>
/// Creates a new BBCodeNode.
/// </summary>
/// <param name="TagName">The node's tag name. Mandatory.</param>
/// <param name="Attribute">The node's optional attribute. This may be an empty string or null.</param>
public BBCodeNode(string TagName, string Attribute) : this(TagName, Attribute, false) { }
/// <summary>
/// Creates a new BBCodeNode.
/// </summary>
/// <param name="TagName">The node's tag name. Mandatory.</param>
public BBCodeNode(string TagName) : this(TagName, null) { }
protected BBCodeNode()
{
children = new List<BBCodeNode>();
}
/// <summary>
/// Adds a new child node at the end of this node's descendants
/// </summary>
/// <param name="Node">The existing BBCodeNode to add. This may not already be childed to another node.</param>
/// <returns>The node passed</returns>
public virtual BBCodeNode AppendChild(BBCodeNode Node)
{
if (Singular)
throw new InvalidOperationException("Cannot add children to a singular node");
if(Node == null)
throw new ArgumentNullException("Node may not be null");
if (Node.Parent != null)
throw new ArgumentException("The BBCodeNode provided is already a child of another node");
children.Add(Node);
Node.Parent = this;
return Node;
}
/// <summary>
/// Adds a new child node at the end of this node's descendants
/// </summary>
/// <param name="TagName">The node's tag name. Mandatory.</param>
/// <param name="Attribute">The node's optional attribute. This may be an empty string or null.</param>
/// <returns>The newly created child node</returns>
public virtual BBCodeNode AppendChild(string TagName, string Attribute)
{
var node = new BBCodeNode(TagName, Attribute)
{
Parent = this,
};
return AppendChild(node);
}
/// <summary>
/// Adds a new child node at the end of this node's descendants
/// </summary>
/// <param name="TagName">The node's tag name. Mandatory.</param>
/// <returns>The newly created child node</returns>
public virtual BBCodeNode AppendChild(string TagName)
{
return AppendChild(TagName, "");
}
/// <summary>
/// Creates a recursive copy of the current nodes and its children
/// </summary>
/// <returns>A deep clone of the current node</returns>
public virtual object Clone()
{
BBCodeNode node = new BBCodeNode(TagName, Attribute);
foreach (var Child in Children)
node.AppendChild((BBCodeNode)Child.Clone());
return node;
}
/// <summary>
/// Inserts a new child node after the reference node passed.
/// </summary>
/// <param name="Node">The new child node to add. This may not be already childed to another node</param>
/// <param name="After">The reference node. This must be a child of the current node</param>
/// <returns>The added node</returns>
public virtual BBCodeNode InsertAfter(BBCodeNode Node, BBCodeNode After)
{
if (Singular)
throw new InvalidOperationException("Cannot add children to a singular node");
if(Node == null)
throw new ArgumentNullException("Node may not be null");
if(After == null)
throw new ArgumentNullException("After may not be null");
if (Node.Parent != null)
throw new ArgumentException("The Node provided is already a child of another node");
if (After.Parent == null || After.Parent != this)
throw new ArgumentException("The After node provided is not a child of this node");
children.Insert(children.IndexOf(After) + 1, Node);
return Node;
}
/// <summary>
/// Inserts a new child node before the reference node passed.
/// </summary>
/// <param name="Node">The new child node to add. This may not be already childed to another node</param>
/// <param name="After">The reference node. This must be a child of the current node</param>
/// <returns>The added node</returns>
public virtual BBCodeNode InsertBefore(BBCodeNode Node, BBCodeNode Before)
{
if (Singular)
throw new InvalidOperationException("Cannot add children to a singular node");
if(Node == null)
throw new ArgumentNullException("Node may not be null");
if(Before == null)
throw new ArgumentNullException("After may not be null");
if (Node.Parent != null)
throw new ArgumentException("The Node provided is already a child of another node");
if (Before.Parent == null || Before.Parent != this)
throw new ArgumentException("The Before node provided is not a child of this node");
children.Insert(children.IndexOf(Before), Node);
return Node;
}
/// <summary>
/// Adds a new child node at the beginning of this node's descendants
/// </summary>
/// <param name="Node">The existing BBCodeNode to add. This may not already be childed to another node.</param>
/// <returns>The node passed</returns>
public virtual BBCodeNode PrependChild(BBCodeNode Node)
{
if (Singular)
throw new InvalidOperationException("Cannot add children to a singular node");
if (Node == null)
throw new ArgumentNullException("Node may not be null");
if (Node.Parent != null)
throw new ArgumentException("The BBCodeNode provided is already a child of another node");
children.Insert(0, Node);
return Node;
}
/// <summary>
/// Removes all child nodes
/// </summary>
public virtual void RemoveAll()
{
children.Clear();
}
/// <summary>
/// Removes a specific child node
/// </summary>
/// <param name="Node">The child node to remove. This must be a child of the current node.</param>
/// <returns>The removed node</returns>
public virtual BBCodeNode RemoveChild(BBCodeNode Node)
{
if (Node == null)
throw new ArgumentNullException("Node may not be null");
if (Node.Parent != null)
throw new ArgumentException("The BBCodeNode provided is not a child of this node");
children.Remove(Node);
return Node;
}
/// <summary>
/// Replaces a specific child node with another
/// </summary>
/// <param name="Old">The node to remove. This must be a child of this node</param>
/// <param name="New">The replacement node. This may not already be childed to another node</param>
/// <returns>The removed node</returns>
public virtual BBCodeNode ReplaceChild(BBCodeNode Old, BBCodeNode New)
{
if (Old == null || New == null)
throw new ArgumentNullException("Arguments may not be null");
if (Old.Parent != this)
throw new ArgumentException("The Old node provided is not a child of this node");
if (New.Parent != null)
throw new ArgumentException("The New node provided is a child of another node");
int index = children.IndexOf(Old);
children.Remove(Old);
children.Insert(index, New);
return Old;
}
/// <summary>
/// Recursively generates the BBCode representation of the current node and its children
/// </summary>
/// <returns>A BBCode string</returns>
public override string ToString()
{
StringBuilder str = new StringBuilder();
str.Append("[" + this.TagName);
if ((Attribute ?? "").Trim() != "")
str.Append("=" + Attribute);
str.Append("]");
if(Singular)
return str.ToString();
foreach (var child in children)
str.Append(child.ToString());
str.Append("[/" + this.TagName + "]");
return str.ToString();
}
}
}