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 | package Torello.HTML; import Torello.HTML.helper.AttrRegEx; import Torello.HTML.NodeSearch.CSSStrException; import Torello.Java.StrCmpr; import static Torello.Java.StringParse.WHITE_SPACE_REGEX; import java.util.Properties; import java.util.regex.Matcher; import java.util.stream.Stream; class ClassIDStyle { static Stream<String> cssClasses(final TagNode tn) { // The CSS Class is just an attribute/inner-tag within an HTML Element. String classes = GetSetAttr.AV(tn, "class", false); // IF the "class" attribute was not present, OR (after trimming) was empty, return // "empty stream" if ((classes == null) || ((classes = classes.trim()).length() == 0)) return Stream.empty(); // STEP 1: Split the string on white-space // STEP 2: Check each element of the output Stream using the "CSSStrException Checker" return CSSStrException.check(WHITE_SPACE_REGEX.splitAsStream(classes)); } static TagNode setCSSClasses( final TagNode tn, final SD quote, final boolean appendOrClobber, final String... cssClasses ) { // Throw CSSStrException if any of the input strings are invalid CSS Class-Names. CSSStrException.check(cssClasses); // Build the CSS 'class' Attribute String. This will be inserted into the TagNode Element StringBuilder sb = new StringBuilder(); boolean first = true; for (String c : cssClasses) { sb.append((first ? "" : " ") + c.trim()); first=false; } /* TagNode ret */ return appendOrClobber ? GetSetAttr.appendToAV(tn, "class", " " + sb.toString(), false, quote) : GetSetAttr.setAV(tn, "class", sb.toString(), quote); /* System.out.println( "ClassIDStyle.setCSSClasses.sb: [" + sb.toString() + "]\n" + "ClassIDStyle.setCSSClasses.quote [" + quote + "]\n" + "ClassIDStyle.setCSSClasses.Before: [" + tn + "]\n" + "ClassIDStyle.setCSSClasses.After: [" + ret + "]" ); return ret; */ } static TagNode setCSSStyle( final TagNode tn, final Properties p, final SD quote, final boolean appendOrClobber ) { // this is used in the "exception constructor" below (which means, it might not be used). int counter = 0; // Follows the "FAIL-FAST" philosophy, and does not allow invalid CSS declaration-name // tokens. Use TagNode.setAV("style", ...), or TagNode.appendToAV("style", ...), to // bypass exception-check. for (String key : p.stringPropertyNames()) if (! CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN_PRED.test(key)) { String[] keyArr = new String[p.size()]; throw new CSSStrException( "CSS Style Definition Property: [" + key + "] does not conform to the " + "valid, HTML 5, regular-expression for CSS Style Definitions Properties:\n[" + CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN.pattern() + "].", // One minor "PRESUMPTION" that is the Iterator will return the elements of // Properties in the EXACT SAME ORDER on both creations / instantiations of the // iterator. Specifically: two invocations of method .iterator(), will return // the same-list of property-keys, in the same order, BOTH TIMES. Once for the // for-loop, and once for the exception message. This only matters if there is // an exception. p.stringPropertyNames().toArray(keyArr), ++counter ); } else ++counter; // Follows the "FAIL-FAST" philosophy, and does not allow "quotes-within-quote" problems // to occur. An attribute-value surrounded by single-quotes, cannot contain a // single-quote inside, and double-within-double. counter = 0; for (String key : p.stringPropertyNames()) if (StrCmpr.containsOR(p.get(key).toString(), ("" + quote.quote), ";")) { String[] keyArr = new String[p.size()]; throw new CSSStrException( "CSS Style Definition Property: [" + key + "], which maps to style-" + "definition property-value:\n[" + p.get(key) + "], contains either a " + "semi-colon ';' character, or the same quotation-mark specified: [" + quote.quote + "], and is therefore not a valid assignment for a CSS " + "Definition Property.", p .stringPropertyNames() .stream() .map((String propertyName) -> p.get(propertyName)) .toArray((int i) -> new String[i]), ++counter ); } else ++counter; // ERROR-CHECKING: FINISHED, NOW JUST BUILD THE ATTRIBUTE-VALUE STRING // (using StringBuilder), AND INSERT IT. StringBuilder sb = new StringBuilder(); for (String key : p.stringPropertyNames()) sb.append(key + ": " + p.get(key) + ";"); /* TagNode ret = */ return appendOrClobber ? GetSetAttr.appendToAV(tn, "style", " " + sb.toString(), false, quote) : GetSetAttr.setAV(tn, "style", sb.toString(), quote); /* System.out.println( "ClassIDStyle.setCSSStyle.sb: [" + sb.toString() + "]\n" + "ClassIDStyle.setCSSStyle.Before: [" + tn + "]\n" + "ClassIDStyle.setCSSStyle.After: [" + ret + "]" ); return ret; */ } static TagNode appendCSSClass( final TagNode tn, final String cssClass, final SD quote ) { // Do a validity check on the class. If this is "problematic" due to use of specialized / // pre-processed CSS Class directives, use the general purpose "setAV(...)" method CSSStrException.check(cssClass); final String curCSSClassSetting = GetSetAttr.AV(tn, "class", false); // If there wasn't a CSS Class already defined, use "setAV(...)", // otherwise use "appendToAV(...)" TagNode ret; boolean firstOrSecond; if ((curCSSClassSetting == null) || (curCSSClassSetting.length() == 0)) { /*return ret = */ return GetSetAttr.setAV(tn, "class", cssClass, quote); // firstOrSecond = true; } else { /*return ret = */ return GetSetAttr.appendToAV(tn, "class", cssClass + ' ', true, quote); // firstOrSecond = false; } /* System.out.println( "ClassIDStyle.appendCSSClass.curCSSClassSetting: [" + curCSSClassSetting + "]\n" + "ClassIDStyle.appendCSSClass.Before: [" + tn + "]\n" + "ClassIDStyle.appendCSSClass.After: [" + ret + "]\n" + "ClassIDStyle.appendCSSClass.firstOrSecond: [" + firstOrSecond + "]" ); return ret; */ } static Properties cssStyle(final TagNode tn) { Properties p = new Properties(); String styleStr = GetSetAttr.AV(tn, "style", false); // Returns the complete attribute-value of "style" in the TagNode // There was no STYLE='...' attribute found, return empty Properties. if (styleStr == null) return p; // Standard String-trim routine styleStr = styleStr.trim(); if (styleStr.length() == 0) return p; // This reg-ex "iterates" over matches of strings that follow the (very basic) form: // declaration-name: declaration-string; // // Where the ":" (color), and ";" (semi-colon) are the only watched/monitored tokens. // See the reg-ex definition in "See Also" tag. Matcher m = AttrRegEx.CSS_INLINE_STYLE_REGEX.matcher(styleStr); // m.group(1): declaration-name (stuff before the ":") // m.group(2): declaration-string (stuff before the next ";", or end-of-string) // // For Example, if the style attribute-value was specified as: // STYLE="font-weight: bold; margin: 1em 1em 1em 2em; color: #0000FF" // // The returned Properties object would contain the string key-value pair elements: // "font-weight" -> "bold" // "margin" -> "1em 1em 1em 2em" // "color" -> "#0000FF" while (m.find()) p.put(m.group(1).toLowerCase(), m.group(2)); return p; } } |