001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.schema.syntaxCheckers; 021 022 023import java.util.HashSet; 024import java.util.Set; 025import java.util.regex.Pattern; 026import java.util.regex.PatternSyntaxException; 027 028import org.apache.directory.api.i18n.I18n; 029import org.apache.directory.api.ldap.model.constants.SchemaConstants; 030import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 031import org.apache.directory.api.util.Strings; 032 033 034/** 035 * A SyntaxChecker which verifies that a value is a facsimile TelephoneNumber according 036 * to ITU recommendation E.123 for the Telephone number part, and from RFC 4517, par. 037 * 3.3.11 : 038 * 039 * <pre> 040 * fax-number = telephone-number *( DOLLAR fax-parameter ) 041 * telephone-number = PrintableString 042 * fax-parameter = "twoDimensional" | 043 * "fineResolution" | 044 * "unlimitedLength" | 045 * "b4Length" | 046 * "a3Width" | 047 * "b4Width" | 048 * "uncompressed" 049 * </pre> 050 * 051 * If needed, and to allow more syntaxes, a list of regexps has been added 052 * which can be initialized to other values 053 * 054 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 055 */ 056@SuppressWarnings("serial") 057public final class FacsimileTelephoneNumberSyntaxChecker extends SyntaxChecker 058{ 059 /** The default pattern used to check a TelephoneNumber */ 060 private static final String DEFAULT_REGEXP = "^ *[+]? *((\\([0-9- ,;/#*]+\\))|[0-9- ,;/#*]+)+$"; 061 062 /** The compiled default pattern */ 063 private String defaultRegexp; 064 065 /** The compiled default pattern */ 066 private Pattern defaultPattern = Pattern.compile( DEFAULT_REGEXP ); 067 068 /** Fax parameters possible values */ 069 private static final String TWO_DIMENSIONAL = "twoDimensional"; 070 private static final String FINE_RESOLUTION = "fineResolution"; 071 private static final String UNLIMITED_LENGTH = "unlimitedLength"; 072 private static final String B4_LENGTH = "b4Length"; 073 private static final String A3_LENGTH = "a3Width"; 074 private static final String B4_WIDTH = "b4Width"; 075 private static final String UNCOMPRESSED = "uncompressed"; 076 077 /** A set which contains all the possible fax parameters values */ 078 private static Set<String> faxParameters = new HashSet<>(); 079 080 /** Initialization of the fax parameters set of values */ 081 static 082 { 083 faxParameters.add( Strings.toLowerCaseAscii( TWO_DIMENSIONAL ) ); 084 faxParameters.add( Strings.toLowerCaseAscii( FINE_RESOLUTION ) ); 085 faxParameters.add( Strings.toLowerCaseAscii( UNLIMITED_LENGTH ) ); 086 faxParameters.add( Strings.toLowerCaseAscii( B4_LENGTH ) ); 087 faxParameters.add( Strings.toLowerCaseAscii( A3_LENGTH ) ); 088 faxParameters.add( Strings.toLowerCaseAscii( B4_WIDTH ) ); 089 faxParameters.add( Strings.toLowerCaseAscii( UNCOMPRESSED ) ); 090 } 091 092 /** 093 * A static instance of FacsimileTelephoneNumberSyntaxChecker 094 */ 095 public static final FacsimileTelephoneNumberSyntaxChecker INSTANCE = 096 new FacsimileTelephoneNumberSyntaxChecker( SchemaConstants.FACSIMILE_TELEPHONE_NUMBER_SYNTAX ); 097 098 /** 099 * A static Builder for this class 100 */ 101 public static final class Builder extends SCBuilder<FacsimileTelephoneNumberSyntaxChecker> 102 { 103 /** The compiled default pattern */ 104 private String defaultRegexp; 105 106 /** The compiled default pattern */ 107 private Pattern defaultPattern; 108 109 /** 110 * The Builder constructor 111 */ 112 private Builder() 113 { 114 super( SchemaConstants.FACSIMILE_TELEPHONE_NUMBER_SYNTAX ); 115 setDefaultRegexp( DEFAULT_REGEXP ); 116 } 117 118 119 /** 120 * Create a new instance of FacsimileTelephoneNumberSyntaxChecker 121 * 122 * @return A new instance of FacsimileTelephoneNumberSyntaxChecker 123 */ 124 @Override 125 public FacsimileTelephoneNumberSyntaxChecker build() 126 { 127 return new FacsimileTelephoneNumberSyntaxChecker( oid, defaultRegexp, defaultPattern ); 128 } 129 130 131 /** 132 * Set the default regular expression for the Telephone number 133 * 134 * @param regexp the default regular expression. 135 * @return The FacsimileTelephneNumber Builder instance 136 */ 137 public Builder setDefaultRegexp( String regexp ) 138 { 139 defaultRegexp = regexp; 140 141 try 142 { 143 defaultPattern = Pattern.compile( regexp ); 144 } 145 catch ( PatternSyntaxException pse ) 146 { 147 // Roll back to the default pattern 148 defaultPattern = Pattern.compile( DEFAULT_REGEXP ); 149 } 150 151 return this; 152 } 153 } 154 155 156 /** 157 * Creates a new instance of TelephoneNumberSyntaxChecker. 158 * 159 * @param oid the OID 160 */ 161 private FacsimileTelephoneNumberSyntaxChecker( String oid ) 162 { 163 super( oid ); 164 } 165 166 167 /** 168 * Creates a new instance of TelephoneNumberSyntaxChecker. 169 * 170 * @param oid The OID 171 * @param defaultRegexp the default regexp to use 172 * @param defaultPattern The default pattern to use 173 */ 174 private FacsimileTelephoneNumberSyntaxChecker( String oid, String defaultRegexp, Pattern defaultPattern ) 175 { 176 super( oid ); 177 178 this.defaultPattern = defaultPattern; 179 this.defaultRegexp = defaultRegexp; 180 } 181 182 183 /** 184 * @return An instance of the Builder for this class 185 */ 186 public static Builder builder() 187 { 188 return new Builder(); 189 } 190 191 192 /** 193 * Get the default regexp (either the original one, or the one that has been set) 194 * 195 * @return The default regexp 196 */ 197 public String getRegexp() 198 { 199 if ( defaultRegexp == null ) 200 { 201 return DEFAULT_REGEXP; 202 } 203 else 204 { 205 return defaultRegexp; 206 } 207 } 208 209 210 /** 211 * {@inheritDoc} 212 */ 213 @Override 214 public boolean isValidSyntax( Object value ) 215 { 216 String strValue; 217 218 if ( value == null ) 219 { 220 if ( LOG.isDebugEnabled() ) 221 { 222 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) ); 223 } 224 225 return false; 226 } 227 228 if ( value instanceof String ) 229 { 230 strValue = ( String ) value; 231 } 232 else if ( value instanceof byte[] ) 233 { 234 strValue = Strings.utf8ToString( ( byte[] ) value ); 235 } 236 else 237 { 238 strValue = value.toString(); 239 } 240 241 if ( strValue.length() == 0 ) 242 { 243 if ( LOG.isDebugEnabled() ) 244 { 245 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 246 } 247 248 return false; 249 } 250 251 // The facsimile telephone number might be composed 252 // of two parts separated by a '$'. 253 int dollarPos = strValue.indexOf( '$' ); 254 255 if ( dollarPos == -1 ) 256 { 257 // We have no fax-parameter : check the Telephone number 258 boolean result = defaultPattern.matcher( strValue ).matches(); 259 260 if ( LOG.isDebugEnabled() ) 261 { 262 if ( result ) 263 { 264 LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) ); 265 } 266 else 267 { 268 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 269 } 270 } 271 272 return result; 273 } 274 275 // First check the telephone number if the '$' is not at the first position 276 if ( dollarPos > 0 ) 277 { 278 boolean result = defaultPattern.matcher( strValue.substring( 0, dollarPos - 1 ) ).matches(); 279 280 if ( LOG.isDebugEnabled() ) 281 { 282 if ( result ) 283 { 284 LOG.debug( I18n.err( I18n.MSG_13701_SYNTAX_VALID, value ) ); 285 } 286 else 287 { 288 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 289 290 return false; 291 } 292 } 293 294 // Now, try to validate the fax-parameters : we may 295 // have more than one, so we will store the seen params 296 // in a set to check that we don't have the same param twice 297 Set<String> paramsSeen = new HashSet<>(); 298 299 while ( dollarPos > 0 ) 300 { 301 String faxParam; 302 int newDollar = strValue.indexOf( '$', dollarPos + 1 ); 303 304 if ( newDollar == -1 ) 305 { 306 faxParam = strValue.substring( dollarPos + 1 ); 307 } 308 else 309 { 310 faxParam = strValue.substring( dollarPos + 1, newDollar ); 311 } 312 313 if ( faxParam.length() == 0 ) 314 { 315 // Not allowed 316 if ( LOG.isDebugEnabled() ) 317 { 318 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 319 } 320 321 return false; 322 } 323 324 // Relax a little bit the syntax by lowercasing the param 325 faxParam = Strings.toLowerCaseAscii( faxParam ); 326 327 if ( !faxParameters.contains( faxParam ) || paramsSeen.contains( faxParam ) ) 328 { 329 // This parameter is not in the possible set 330 if ( LOG.isDebugEnabled() ) 331 { 332 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 333 } 334 335 return false; 336 } 337 else 338 { 339 // It's a correct param, let's add it to the seen 340 // params. 341 paramsSeen.add( faxParam ); 342 } 343 344 dollarPos = newDollar; 345 } 346 347 if ( LOG.isDebugEnabled() ) 348 { 349 LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) ); 350 } 351 352 return true; 353 } 354 355 // We must have a valid telephone number ! 356 if ( LOG.isDebugEnabled() ) 357 { 358 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 359 } 360 361 return false; 362 } 363}