001/* 002 * MIT License 003 * 004 * Copyright (c) 2016 Michael Angstadt 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in 014 * all copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024 025package com.github.mangstadt.vinnie.validate; 026 027import java.util.EnumMap; 028import java.util.HashMap; 029import java.util.Map; 030 031import com.github.mangstadt.vinnie.SyntaxStyle; 032 033/** 034 * <p> 035 * Checks properties for illegal characters. 036 * </p> 037 * <p> 038 * Two kinds of checking are supported: strict and non-strict. Strict ensures 039 * that the data adhere to the specifications. Non-strict allows all characters 040 * to be used, as long as they do not break the syntax. 041 * </p> 042 * <p> 043 * <b>Example:</b> 044 * </p> 045 * 046 * <pre class="brush:java"> 047 * SyntaxStyle style = SyntaxStyle.NEW; 048 * String name = "NOTE #2"; 049 * 050 * boolean strict = false; 051 * assertTrue(VObjectValidator.validatePropertyName(name, style, strict)); 052 * 053 * strict = true; 054 * assertFalse(VObjectValidator.validatePropertyName(name, style, strict)); 055 * </pre> 056 * @author Michael Angstadt 057 */ 058public class VObjectValidator { 059 private static final Map<SyntaxStyle, Map<Boolean, AllowedCharacters>> propertyName = new EnumMap<SyntaxStyle, Map<Boolean, AllowedCharacters>>(SyntaxStyle.class); 060 static { 061 boolean strict; 062 SyntaxStyle syntax; 063 064 syntax = SyntaxStyle.OLD; 065 { 066 Map<Boolean, AllowedCharacters> map = new HashMap<Boolean, AllowedCharacters>(); 067 strict = false; 068 { 069 //@formatter:off 070 map.put(strict, new AllowedCharacters.Builder() 071 .allowAll() 072 .except("\r\n:.;") 073 .build()); 074 //@formatter:on 075 } 076 077 strict = true; 078 { 079 //@formatter:off 080 map.put(strict, new AllowedCharacters.Builder() 081 .allowPrintable() 082 .except("[]=:.,") 083 084 /* 085 * Note: The specification's formal grammar allows semicolons to 086 * be present in property name. This may be a mistake because 087 * this would break the syntax. This validator will treat 088 * semicolons as invalid in this context. 089 * 090 * The specifications state that semicolons can be included in 091 * parameter values by escaping them with a backslash--however, 092 * the specification is not clear as to whether this is also 093 * permitted in property names. 094 * 095 * vCard 2.1: Section 2.1.2 096 * vCal 1.0: Section 2, "Property" sub-heading 097 */ 098 .except(';') 099 .build()); 100 //@formatter:on 101 } 102 103 propertyName.put(syntax, map); 104 } 105 106 syntax = SyntaxStyle.NEW; 107 { 108 Map<Boolean, AllowedCharacters> map = new HashMap<Boolean, AllowedCharacters>(); 109 strict = false; 110 { 111 //same as old style syntax 112 map.put(strict, propertyName.get(SyntaxStyle.OLD).get(strict)); 113 } 114 115 strict = true; 116 { 117 //@formatter:off 118 map.put(strict, new AllowedCharacters.Builder() 119 .allow('A', 'Z') 120 .allow('a', 'z') 121 .allow('0', '9') 122 .allow('-') 123 .build()); 124 //@formatter:on 125 } 126 127 propertyName.put(syntax, map); 128 } 129 } 130 131 private static final Map<SyntaxStyle, Map<Boolean, AllowedCharacters>> group = propertyName; 132 133 private static final Map<SyntaxStyle, Map<Boolean, AllowedCharacters>> parameterName = new EnumMap<SyntaxStyle, Map<Boolean, AllowedCharacters>>(SyntaxStyle.class); 134 static { 135 boolean strict; 136 SyntaxStyle syntax; 137 138 syntax = SyntaxStyle.OLD; 139 { 140 Map<Boolean, AllowedCharacters> map = new HashMap<Boolean, AllowedCharacters>(); 141 strict = false; 142 { 143 //@formatter:off 144 map.put(strict, new AllowedCharacters.Builder() 145 .allowAll() 146 .except("\r\n:;=") 147 .build()); 148 //@formatter:on 149 } 150 151 strict = true; 152 { 153 //same as property name 154 map.put(strict, propertyName.get(syntax).get(strict)); 155 } 156 157 parameterName.put(syntax, map); 158 } 159 160 syntax = SyntaxStyle.NEW; 161 { 162 Map<Boolean, AllowedCharacters> map = new HashMap<Boolean, AllowedCharacters>(); 163 strict = false; 164 { 165 //same as old style syntax 166 map.put(strict, parameterName.get(SyntaxStyle.OLD).get(strict)); 167 } 168 169 strict = true; 170 { 171 //same as property name 172 map.put(strict, propertyName.get(syntax).get(strict)); 173 } 174 175 parameterName.put(syntax, map); 176 } 177 } 178 179 private static final Map<SyntaxStyle, Map<Boolean, Map<Boolean, AllowedCharacters>>> parameterValue = new EnumMap<SyntaxStyle, Map<Boolean, Map<Boolean, AllowedCharacters>>>(SyntaxStyle.class); 180 static { 181 boolean strict, caretEncoding; 182 SyntaxStyle syntax; 183 184 syntax = SyntaxStyle.OLD; 185 { 186 Map<Boolean, Map<Boolean, AllowedCharacters>> map = new HashMap<Boolean, Map<Boolean, AllowedCharacters>>(); 187 caretEncoding = false; 188 { 189 Map<Boolean, AllowedCharacters> map2 = new HashMap<Boolean, AllowedCharacters>(); 190 strict = false; 191 { 192 //@formatter:off 193 map2.put(strict, new AllowedCharacters.Builder() 194 .allowAll() 195 .except("\r\n:") 196 .build()); 197 //@formatter:on 198 } 199 200 strict = true; 201 { 202 //same as parameter name, except semicolons are allowed 203 //@formatter:off 204 AllowedCharacters paramName = parameterName.get(syntax).get(strict); 205 map2.put(strict, new AllowedCharacters.Builder(paramName) 206 .allow(';') 207 .build()); 208 //@formatter::on 209 } 210 map.put(caretEncoding, map2); 211 } 212 213 caretEncoding = true; 214 { 215 /* 216 * Same as when caret encoding is disabled because 217 * old style syntax does not support caret encoding. 218 */ 219 map.put(caretEncoding, map.get(false)); 220 } 221 222 parameterValue.put(syntax, map); 223 } 224 225 syntax = SyntaxStyle.NEW; 226 { 227 Map<Boolean, Map<Boolean, AllowedCharacters>> map = new HashMap<Boolean, Map<Boolean, AllowedCharacters>>(); 228 caretEncoding = false; 229 { 230 Map<Boolean, AllowedCharacters> map2 = new HashMap<Boolean, AllowedCharacters>(); 231 strict = false; 232 { 233 //@formatter:off 234 map2.put(strict, new AllowedCharacters.Builder() 235 .allowAll() 236 .except("\r\n\"") 237 .build()); 238 //@formatter:on 239 } 240 241 strict = true; 242 { 243 //@formatter:off 244 map2.put(strict, new AllowedCharacters.Builder() 245 .allowPrintable() 246 .allowNonAscii() 247 .allow('\t') 248 .except('"') 249 .build()); 250 //@formatter:on 251 } 252 253 map.put(caretEncoding, map2); 254 } 255 256 caretEncoding = true; 257 { 258 Map<Boolean, AllowedCharacters> map2 = new HashMap<Boolean, AllowedCharacters>(); 259 strict = false; 260 { 261 //@formatter:off 262 map2.put(strict, new AllowedCharacters.Builder() 263 .allowAll() 264 .build()); 265 //@formatter:on 266 } 267 268 strict = true; 269 { 270 //@formatter:off 271 map2.put(strict, new AllowedCharacters.Builder() 272 .allowPrintable() 273 .allowNonAscii() 274 .allow("\r\n\t") 275 .build()); 276 //@formatter:on 277 } 278 279 map.put(caretEncoding, map2); 280 } 281 282 parameterValue.put(syntax, map); 283 } 284 } 285 286 /** 287 * Validates a property name. 288 * @param name the property name 289 * @param syntax the syntax style to validate against 290 * @param strict false to allow all characters as long as they don't break 291 * the syntax, true for spec-compliant validation 292 * @return true if the property name is valid, false if not 293 */ 294 public static boolean validatePropertyName(String name, SyntaxStyle syntax, boolean strict) { 295 return allowedCharactersPropertyName(syntax, strict).check(name); 296 } 297 298 /** 299 * Gets the list of allowed characters for property names. 300 * @param syntax the syntax style 301 * @param strict false for the non-strict list, true for the spec-compliant 302 * list 303 * @return the character list 304 */ 305 public static AllowedCharacters allowedCharactersPropertyName(SyntaxStyle syntax, boolean strict) { 306 return propertyName.get(syntax).get(strict); 307 } 308 309 /** 310 * Validates a group name. 311 * @param group the group name 312 * @param syntax the syntax style to validate against 313 * @param strict false to allow all characters as long as they don't break 314 * the syntax, true for spec-compliant validation 315 * @return true if the group name is valid, false if not 316 */ 317 public static boolean validateGroupName(String group, SyntaxStyle syntax, boolean strict) { 318 return allowedCharactersGroup(syntax, strict).check(group); 319 } 320 321 /** 322 * Gets the list of allowed characters for group names. 323 * @param syntax the syntax style 324 * @param strict false for the non-strict list, true for the spec-compliant 325 * list 326 * @return the character list 327 */ 328 public static AllowedCharacters allowedCharactersGroup(SyntaxStyle syntax, boolean strict) { 329 return group.get(syntax).get(strict); 330 } 331 332 /** 333 * Validates a parameter name. 334 * @param name the parameter name 335 * @param syntax the syntax style to validate against 336 * @param strict false to allow all characters as long as they don't break 337 * the syntax, true for spec-compliant validation 338 * @return true if the parameter name is valid, false if not 339 */ 340 public static boolean validateParameterName(String name, SyntaxStyle syntax, boolean strict) { 341 return allowedCharactersParameterName(syntax, strict).check(name); 342 } 343 344 /** 345 * Gets the list of allowed characters for parameter names. 346 * @param syntax the syntax style 347 * @param strict false for the non-strict list, true for the spec-compliant 348 * list 349 * @return the character list 350 */ 351 public static AllowedCharacters allowedCharactersParameterName(SyntaxStyle syntax, boolean strict) { 352 return parameterName.get(syntax).get(strict); 353 } 354 355 /** 356 * Validates a parameter value. 357 * @param value the parameter value 358 * @param syntax the syntax style to validate against 359 * @param caretEncoding true if caret encoding is enabled, false if not 360 * @param strict false to allow all characters as long as they don't break 361 * the syntax, true for spec-compliant validation 362 * @return true if the parameter value is valid, false if not 363 */ 364 public static boolean validateParameterValue(String value, SyntaxStyle syntax, boolean caretEncoding, boolean strict) { 365 return allowedCharactersParameterValue(syntax, caretEncoding, strict).check(value); 366 } 367 368 /** 369 * Gets the list of allowed characters for parameter values. 370 * @param syntax the syntax style 371 * @param caretEncoding true if caret encoding is enabled, false if not 372 * @param strict false for the non-strict list, true for the spec-compliant 373 * list 374 * @return the character list 375 */ 376 public static AllowedCharacters allowedCharactersParameterValue(SyntaxStyle syntax, boolean caretEncoding, boolean strict) { 377 return parameterValue.get(syntax).get(caretEncoding).get(strict); 378 } 379 380 private VObjectValidator() { 381 //hide 382 } 383}