1 /* 2 * Copyright Lodovico Giaretta 2016 - . 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 8 // TODO: write an in-depth explanation of this module, how to create validations, 9 // how validations should behave, etc... 10 11 /++ 12 + Authors: 13 + Lodovico Giaretta 14 + László Szerémi 15 + 16 + License: 17 + <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 18 + 19 + Copyright: 20 + Copyright Lodovico Giaretta 2016 -- 21 +/ 22 23 module newxml.validation; 24 25 import newxml.interfaces; 26 27 /** 28 * Checks whether a character can appear in an XML 1.0 document. 29 */ 30 pure nothrow @nogc @safe bool isValidXMLCharacter10(dchar c) 31 { 32 return c == '\r' || c == '\n' || c == '\t' 33 || (0x20 <= c && c <= 0xD7FF) 34 || (0xE000 <= c && c <= 0xFFFD) 35 || (0x10000 <= c && c <= 0x10FFFF); 36 } 37 38 /** 39 * Checks whether a character can appear in an XML 1.1 document. 40 */ 41 pure nothrow @nogc @safe bool isValidXMLCharacter11(dchar c) 42 { 43 return (1 <= c && c <= 0xD7FF) 44 || (0xE000 <= c && c <= 0xFFFD) 45 || (0x10000 <= c && c <= 0x10FFFF); 46 } 47 /** 48 * Checks whether a text contains invalid characters for an XML 1.0 document. 49 * Params: 50 * input = The text to test for. 51 * Returns: true if text doesn't contain any invalid characters. 52 */ 53 pure nothrow @nogc @safe bool isValidXMLText10(T)(T[] input) 54 { 55 foreach (elem; input) 56 { 57 if (!isValidXMLCharacter10(elem)) return false; 58 } 59 return true; 60 } 61 /** 62 * Checks whether a text contains invalid characters for an XML 1.1 document. 63 * Params: 64 * input = The text to test for. 65 * Returns: true if text doesn't contain any invalid characters. 66 */ 67 pure nothrow @nogc @safe bool isValidXMLText11(T)(T[] input) 68 { 69 foreach (elem; input) 70 { 71 if (!isValidXMLCharacter11(elem)) return false; 72 } 73 return true; 74 } 75 76 /** 77 * Checks whether a character can start an XML name (tag name or attribute name). 78 */ 79 pure nothrow @nogc @safe bool isValidXMLNameStart(dchar c) 80 { 81 return c == ':' 82 || ('A' <= c && c <= 'Z') 83 || c == '_' 84 || ('a' <= c && c <= 'z') 85 || (0xC0 <= c && c <= 0x2FF && c != 0xD7 && c != 0xF7) 86 || (0x370 <= c && c <= 0x1FFF && c != 0x37E) 87 || c == 0x200C 88 || c == 0x200D 89 || (0x2070 <= c && c <= 0x218F) 90 || (0x2C00 <= c && c <= 0x2FEF) 91 || (0x3001 <= c && c <= 0xD7FF) 92 || (0xF900 <= c && c <= 0xFDCF) 93 || (0xFDF0 <= c && c <= 0xEFFFF && c != 0xFFFE && c != 0xFFFF); 94 } 95 96 /** 97 * Checks whether a character can appear inside an XML name (tag name or attribute name). 98 */ 99 pure nothrow @nogc @safe bool isValidXMLNameChar(dchar c) 100 { 101 return isValidXMLNameStart(c) 102 || c == '-' 103 || c == '.' 104 || ('0' <= c && c <= '9') 105 || c == 0xB7 106 || (0x300 <= c && c <= 0x36F) 107 || (0x203F <= c && c <= 2040); 108 } 109 110 /** 111 * Checks whether a name is a valid XML name or not. 112 * Params: 113 * input = The input string. 114 * Returns: True if XML name is valid. 115 */ 116 pure nothrow @nogc @safe bool isValidXMLName(T)(T[] input) { 117 if (!input.length) return false; 118 if (!isValidXMLNameStart(input[0])) return false; 119 for (sizediff_t i = 1 ; i < input.length; i++) 120 if (!isValidXMLNameChar(input[i])) return false; 121 return true; 122 } 123 124 /** 125 * Checks whether a character can appear in an XML public ID. 126 */ 127 pure nothrow @nogc @safe bool isValidXMLPublicIdCharacter(dchar c) 128 { 129 import std.string: indexOf; 130 return c == ' ' 131 || c == '\n' 132 || c == '\r' 133 || ('a' <= c && c <= 'z') 134 || ('A' <= c && c <= 'Z') 135 || ('0' <= c && c <= '9') 136 || "-'()+,./:=?;!*#@$_%".indexOf(c) != -1; 137 } 138 139 unittest 140 { 141 assert(isValidXMLName("foo")); 142 assert(isValidXMLName("bar")); 143 assert(!isValidXMLName(".foo")); 144 assert(isValidXMLName("foo:bar")); 145 } 146 147 /** 148 * A simple document validation stack. 149 * Node names on every non-empty starting nodes are pushed here, then on every ending node the top is popped then 150 * compared with the name. 151 */ 152 struct ValidationStack(StringType) 153 { 154 StringType[] stack; 155 /** 156 * Pushes a name to the top. 157 */ 158 void push(StringType input) @safe pure nothrow 159 { 160 stack ~= input; 161 } 162 /** 163 * Pops a name from the top, then compared with the input. 164 * Params: 165 * input = the string that is being compared with the input. 166 * Returns: True if a string could been removed from the stack and it's identical with the input, false otherwise. 167 */ 168 bool pop(StringType input) @safe pure nothrow 169 { 170 if (stack.length) 171 { 172 StringType top = stack[$-1]; 173 stack = stack[0..$-1]; 174 return top == input; 175 } 176 else 177 { 178 return false; 179 } 180 } 181 }