1 /**
2  * Copyright:
3  * (C) 2016 Martin Brzenska
4  *
5  * License:
6  * Distributed under the terms of the MIT license.
7  * Consult the provided LICENSE.md file for details
8  */
9 module libdominator.Attribute;
10 
11 import std.regex : regex , matchFirst ;
12 import std.array : split;
13 
14 import libdominator;
15 
16 ///Struct for Node Attributes
17 struct Attribute
18 {
19     string key;
20     string[] values;
21 
22     /**
23     * Params:
24     * key = the Name of the Attribute (can be prefixed with '(regex)')
25     * values = a whitespace serparated List of Attribute Values (each Value can be prefixed with '(regex)')
26     */
27     this(string key, string values)
28     {
29         import std.string : toLower;
30         this.key = toLower(key);
31         this.values = split(values);
32     }
33     /**
34     * Params:
35     * key = The name of the attribute (can be prefixed with '(regex)')
36     * values = Array of attribute values (each value can be prefixed with '(regex)')
37     */
38     this(string key, string[] values)
39     {
40         import std.string : toLower;
41         this.key = toLower(key);
42         this.values = values;
43     }
44 
45     ///Checks if the given node matches the attributes given key
46     bool matchesKey(Node node)
47     {
48         if (key.length > 6 && key[0..7] == "(regex)")
49         {
50             auto regx = regex(key[7..$]);
51             foreach (Attribute attrib; node.getAttributes())
52             {
53                 auto capture = matchFirst(attrib.key, regx);
54                 if (!capture.empty)
55                 {
56                     return true;
57                 }
58             }
59         }
60         else
61         {
62             foreach (Attribute attrib; node.getAttributes())
63             {
64                 if (attrib.key == key)
65                 {
66                     return true;
67                 }
68             }
69         }
70         return false;
71     }
72     ///Checks if at least one of the attribute values of the given node matches the given attribute values
73     bool matchesValue(Node node)
74     {
75         if (values.length == 0)
76         {
77             return true;
78         }
79         ubyte hitCount;
80         foreach (string value; values)
81         {
82             bool isRegex;
83             if (value.length > 6 && value[0 .. 7] == "(regex)")
84             {
85                 isRegex =true;
86             }
87             foreach (Attribute attrib; node.getAttributes())
88             {
89                 foreach (string nodeValue; attrib.values)
90                 {
91                     if(isRegex)
92                     {
93                         auto capture = matchFirst(nodeValue, regex(value[7..$]));
94                         if (!capture.empty) { hitCount++; }
95                     }
96                     else {
97                         if (nodeValue == value) { hitCount++; }
98                     }
99                     if(hitCount == values.length) { return true; }
100                 }
101             }
102         }
103         return false;
104     }
105 
106     /**
107     * Checks if the given node matches key and values of this attribute.
108     * Note that all atribute values from this attribute must match the given nodes attribute values - not the other way round
109     */
110     bool matches(Node node)
111     {
112         return this.matchesKey(node) && this.matchesValue(node);
113     }
114 
115 }