NTP Analyzer  0.8.2
Analyze the operation of time servers
SyntaxNode.cs
Go to the documentation of this file.
1 //
2 // Copyright (c) 2013-2017 Carsten Sonne Larsen <cs@innolan.net>
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 
22 using System;
23 using System.Collections;
24 using System.Collections.Generic;
25 using System.Linq;
26 using System.Text;
27 using Ntp.Analyzer.Config.Node;
29 using Ntp.Analyzer.Config.Table;
30 
31 namespace Ntp.Analyzer.Config.Syntax
32 {
33  public abstract class SyntaxNode<T> : ISyntaxNode
34  where T : ConfigurationNode
35  {
36  protected SyntaxNode(Symbol symbol, string name, int line, bool requirePath = false)
37  {
38  Symbol = symbol;
39  Name = name;
40  Line = line;
41  RequirePath = requirePath;
42  compiledNode = null;
43  Nodes = new List<ISyntaxNode>();
44  errors = new List<string>();
45  }
46 
47  private readonly List<string> errors;
48  private T compiledNode;
49 
50  protected List<ISyntaxNode> Nodes { get; }
51 
52  protected string Name { get; }
53 
54  public Symbol Symbol { get; }
55 
56  public bool RequirePath { get; }
57 
58  public int Line { get; }
59 
60  public bool HasErrors => errors.Count != 0;
61 
62  public IEnumerable<string> Errors => errors;
63 
64  public ConfigurationNode CompiledNode => compiledNode;
65 
66  public void Add(ISyntaxNode node)
67  {
68  Nodes.Add(node);
69  }
70 
71  public IEnumerator<ISyntaxNode> GetEnumerator()
72  {
73  return Nodes.GetEnumerator();
74  }
75 
76  IEnumerator IEnumerable.GetEnumerator()
77  {
78  return GetEnumerator();
79  }
80 
81  public void CompileNode()
82  {
83  Compile();
84  }
85 
86  public void Resolve(SymbolTable table)
87  {
88  InternalResolve(table);
89  }
90 
91  public void Validate(SymbolTable table)
92  {
93  ValidateTypes();
94  ValidateMandatories();
95  ValidateReferences(table);
96  }
97 
98  public void Assemble(ISyntaxNode node)
99  {
100  if (compiledNode == null)
101  return;
102 
103  compiledNode.Parent = node.CompiledNode;
104  node.CompiledNode.Assemble();
105  }
106 
107  public T Compile()
108  {
109  return compiledNode ?? (compiledNode = InternalCompile());
110  }
111 
112  protected abstract T InternalCompile();
113 
114  #region Validation methods
115 
120  protected virtual void ValidateTypes()
121  {
122  }
123 
128  protected virtual void ValidateMandatories()
129  {
130  }
131 
137  protected virtual void ValidateReferences(SymbolTable table)
138  {
139  }
140 
146  protected virtual void InternalResolve(SymbolTable table)
147  {
148  }
149 
150  #endregion
151 
152  #region Validation helpers
153 
154  protected void CheckTypeIs<TU>(Symbol symbol)
155  where TU : SettingNode, new()
156  {
157  foreach (var node in Nodes.Where(n => n.Symbol == symbol))
158  {
159  if (node is TU)
160  continue;
161 
162  // Build error message
163  var b = new StringBuilder();
164  if (Line != 0)
165  {
166  b.Append("Error in line ");
167  b.Append(Line);
168  b.Append(": ");
169  }
170  b.Append("Value of setting ");
171  b.Append(Keyword.Find(symbol).Name);
172  b.Append(" must be ");
173  b.Append(new TU().SettingValue);
174  errors.Add(b.ToString());
175  }
176  }
177 
178  protected void CheckTypeIs<TU, TV>(Symbol symbol)
179  where TU : SettingNode, new()
180  where TV : SettingNode, new()
181  {
182  foreach (var node in Nodes.Where(n => n.Symbol == symbol))
183  {
184  if (node is TU || node is TV)
185  continue;
186 
187  // Build error message
188  var b = new StringBuilder();
189  if (Line != 0)
190  {
191  b.Append("Error in line ");
192  b.Append(Line);
193  b.Append(": ");
194  }
195  b.Append("Value of setting ");
196  b.Append(Keyword.Find(symbol).Name);
197  b.Append(" must be ");
198  b.Append(new TU().SettingValue);
199  b.Append(" or ");
200  b.Append(new TV().SettingValue);
201  errors.Add(b.ToString());
202  }
203  }
204 
205  protected void CheckAllIsPresent(IEnumerable<Symbol> list)
206  {
207  var missing = list.
208  Where(item => Nodes.Count(n => n.Symbol == item) == 0).
209  ToList();
210 
211  if (missing.Count == 0)
212  return;
213 
214  // Build error message
215  int i = 0;
216  int count = missing.Count;
217  var b = new StringBuilder();
218 
219  if (Line != 0)
220  {
221  b.Append("Error in line ");
222  b.Append(Line);
223  b.Append(": ");
224  }
225  b.Append(Keyword.Find(Symbol).Name);
226  b.Append(" section is missing the setting");
227  b.Append(missing.Count > 1 ? "s: " : ": ");
228 
229  foreach (var item in missing)
230  {
231  i++;
232  b.Append(Keyword.Find(item).Name);
233  if (i == count - 1 && count > 1)
234  {
235  b.Append(" and ");
236  }
237  else if (i != count)
238  {
239  b.Append(", ");
240  }
241  else
242  {
243  b.Append(".");
244  }
245  }
246 
247  errors.Add(b.ToString());
248  }
249 
250  protected void CheckOneIsPresent(IEnumerable<Symbol> list)
251  {
252  var symbols = list as IList<Symbol> ?? list.ToList();
253  var set = new HashSet<Symbol>(symbols);
254 
255  if (Nodes.Any(node => set.Contains(node.Symbol)))
256  return;
257 
258  // Build error message
259  int i = 0;
260  int count = symbols.Count;
261  var b = new StringBuilder();
262 
263  if (Line != 0)
264  {
265  b.Append("Error in line ");
266  b.Append(Line);
267  b.Append(": ");
268  }
269  b.Append(Keyword.Find(Symbol).Name);
270  b.Append(" section must contain at least one of the setting");
271  b.Append(count > 1 ? "s: " : ": ");
272 
273  foreach (var item in symbols)
274  {
275  i++;
276  b.Append(Keyword.Find(item).Name);
277  if (i == count - 1)
278  {
279  b.Append(" or ");
280  }
281  else if (i != count)
282  {
283  b.Append(", ");
284  }
285  else
286  {
287  b.Append(".");
288  }
289  }
290 
291  errors.Add(b.ToString());
292  }
293 
294  protected void CheckOnlyOneIsPresent(IEnumerable<Symbol> list)
295  {
296  var symbols = list.ToList();
297  var set = new HashSet<Symbol>(symbols);
298 
299  int a = Nodes.Count(node => set.Contains(node.Symbol));
300 
301  if (a < 2)
302  return;
303 
304  // Build error message
305  int i = 0;
306  int count = symbols.Count;
307  var b = new StringBuilder();
308 
309  if (Line != 0)
310  {
311  b.Append("Error in line ");
312  b.Append(Line);
313  b.Append(": ");
314  }
315  b.Append(Keyword.Find(Symbol).Name);
316  b.Append(" section can only contain one of the setting");
317  b.Append(count > 1 ? "s: " : ": ");
318 
319  foreach (var item in symbols)
320  {
321  i++;
322  b.Append(Keyword.Find(item).Name);
323  if (i == count - 1)
324  {
325  b.Append(" or ");
326  }
327  else if (i != count)
328  {
329  b.Append(", ");
330  }
331  else
332  {
333  b.Append(".");
334  }
335  }
336 
337  errors.Add(b.ToString());
338  }
339 
340  protected void CheckIsUnique(IEnumerable<Symbol> list)
341  {
342  var nonUniques = (
343  from symbol in list
344  let c = Nodes.Count(n => n.Symbol == symbol)
345  where c > 1
346  select symbol
347  ).ToList();
348 
349  if (nonUniques.Count == 0)
350  return;
351 
352  // Build error message
353  int i = 0;
354  int count = nonUniques.Count;
355  var b = new StringBuilder();
356 
357  if (Line != 0)
358  {
359  b.Append("Error in line ");
360  b.Append(Line);
361  b.Append(": ");
362  }
363  b.Append(Keyword.Find(Symbol).Name);
364  b.Append(" section has multiple occurrences of the setting");
365  b.Append(count > 1 ? "s: " : ": ");
366 
367  foreach (var item in nonUniques)
368  {
369  i++;
370  b.Append(Keyword.Find(item).Name);
371  if (i == count - 1)
372  {
373  b.Append(" and ");
374  }
375  else if (i != count)
376  {
377  b.Append(", ");
378  }
379  else
380  {
381  b.Append(".");
382  }
383  }
384 
385  errors.Add(b.ToString());
386  }
387 
388  protected Uri CheckLink(string link, string keyword)
389  {
390  if (link == null)
391  return null;
392 
393  Uri uri;
394  string text = link.TrimStart();
395 
396  if ((text.StartsWith("http://") ||
397  text.StartsWith("https://") ||
398  text.StartsWith("ftp://") ||
399  text.StartsWith("mailto:") ||
400  text.StartsWith("javascript:")) &&
401  Uri.TryCreate(link, UriKind.Absolute, out uri))
402  return uri;
403 
404  if (Uri.TryCreate("/" + link.TrimStart('/'), UriKind.Relative, out uri))
405  return uri;
406 
407  if (Uri.TryCreate(link, UriKind.Absolute, out uri))
408  return uri;
409 
410  var name = Keyword.Find(Symbol).Name;
411  var b = new StringBuilder();
412  if (Line != 0)
413  {
414  b.Append("Error in line ");
415  b.Append(Line);
416  b.Append(": ");
417  }
418  b.Append($"{keyword} {name} is not a wellformed link.");
419  AddError(b.ToString());
420 
421  return null;
422  }
423 
424  #endregion
425 
426  #region Error message helpers
427 
428  protected void AddReferenceNameError(ISyntaxNode node, string keyword, string name)
429  {
430  var b = new StringBuilder();
431  if (Line != 0)
432  {
433  b.Append("Error in line ");
434  b.Append(node.Line);
435  b.Append(": ");
436  }
437  b.Append($"{keyword} {name} refers to a nonexistent section.");
438  AddError(b.ToString());
439  }
440 
441  protected void AddReferenceTypeError(ISyntaxNode node, string keyword, string section, string name)
442  {
443  var b = new StringBuilder();
444  if (node.Line != 0)
445  {
446  b.Append("Error in line ");
447  b.Append(node.Line);
448  b.Append(": ");
449  }
450  b.Append($"{keyword} {name} does not refer to a {section} section.");
451  AddError(b.ToString());
452  }
453 
454  protected void AddError(string message)
455  {
456  errors.Add(message);
457  }
458 
459  #endregion
460  }
461 }
IEnumerator< ISyntaxNode > GetEnumerator()
Definition: SyntaxNode.cs:71
void CheckOneIsPresent(IEnumerable< Symbol > list)
Definition: SyntaxNode.cs:250
void AddReferenceTypeError(ISyntaxNode node, string keyword, string section, string name)
Definition: SyntaxNode.cs:441
function a
Definition: bootstrap.min.js:6
Uri CheckLink(string link, string keyword)
Definition: SyntaxNode.cs:388
SyntaxNode(Symbol symbol, string name, int line, bool requirePath=false)
Definition: SyntaxNode.cs:36
void CheckOnlyOneIsPresent(IEnumerable< Symbol > list)
Definition: SyntaxNode.cs:294
static Keyword Find(Symbol symbol)
Definition: Keyword.cs:216
void Assemble(ISyntaxNode node)
Definition: SyntaxNode.cs:98
void CheckAllIsPresent(IEnumerable< Symbol > list)
Definition: SyntaxNode.cs:205
virtual void InternalResolve(SymbolTable table)
Override to resolve references to other syntax nodes from this syntax node.
Definition: SyntaxNode.cs:146
void Add(ISyntaxNode node)
Definition: SyntaxNode.cs:66
void Resolve(SymbolTable table)
Definition: SyntaxNode.cs:86
virtual void ValidateReferences(SymbolTable table)
Override to validates the references in this syntax node.
Definition: SyntaxNode.cs:137
var c
Definition: bootstrap.min.js:6
virtual void ValidateMandatories()
Override to validates the mandatory types in this syntax node.
Definition: SyntaxNode.cs:128
virtual void ValidateTypes()
Override to validates the types in this syntax node.
Definition: SyntaxNode.cs:120
void CheckIsUnique(IEnumerable< Symbol > list)
Definition: SyntaxNode.cs:340
void Validate(SymbolTable table)
Definition: SyntaxNode.cs:91
void AddReferenceNameError(ISyntaxNode node, string keyword, string name)
Definition: SyntaxNode.cs:428
var b
Definition: bootstrap.min.js:6
readonly List< string > errors
Definition: SyntaxNode.cs:47