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.regex.Pattern; 024 025import org.apache.directory.api.i18n.I18n; 026import org.apache.directory.api.ldap.model.constants.SchemaConstants; 027import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 028import org.apache.directory.api.util.Strings; 029 030 031/** 032 * A SyntaxChecker which verifies that a value is a generalized time 033 * according to RFC 4517. 034 * <p> 035 * From RFC 4517 : 036 * <pre> 037 * GeneralizedTime = century year month day hour 038 * [ minute [ second / leap-second ] ] 039 * [ fraction ] 040 * g-time-zone 041 * 042 * century = 2(%x30-39) ; "00" to "99" 043 * year = 2(%x30-39) ; "00" to "99" 044 * month = ( %x30 %x31-39 ) ; "01" (January) to "09" 045 * | ( %x31 %x30-32 ) ; "10" to "12" 046 * day = ( %x30 %x31-39 ) ; "01" to "09" 047 * | ( %x31-32 %x30-39 ) ; "10" to "29" 048 * | ( %x33 %x30-31 ) ; "30" to "31" 049 * hour = ( %x30-31 %x30-39 ) 050 * | ( %x32 %x30-33 ) ; "00" to "23" 051 * minute = %x30-35 %x30-39 ; "00" to "59" 052 * 053 * second = ( %x30-35 %x30-39 ) ; "00" to "59" 054 * leap-second = ( %x36 %x30 ) ; "60" 055 * 056 * fraction = ( DOT / COMMA ) 1*(%x30-39) 057 * g-time-zone = %x5A ; "Z" 058 * | g-differential 059 * g-differential = ( MINUS / PLUS ) hour [ minute ] 060 * MINUS = %x2D ; minus sign ("-") 061 * 062 * From RFC 4512 : 063 * PLUS = %x2B ; plus sign ("+") 064 * DOT = %x2E ; period (".") 065 * COMMA = %x2C ; comma (",") 066 * </pre> 067 * 068 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 069 */ 070@SuppressWarnings("serial") 071public final class GeneralizedTimeSyntaxChecker extends SyntaxChecker 072{ 073 /** The GeneralizedDate pattern matching */ 074 private static final String GENERALIZED_TIME_PATTERN = 075 // century + year : 0000 to 9999 076 "^\\d{4}" 077 // month : 01 to 12 078 + "(0[1-9]|1[0-2])" 079 // day : 01 to 31 080 + "(0[1-9]|[12]\\d|3[01])" 081 // hour : 00 to 23 082 + "([01]\\d|2[0-3])" 083 + "(" 084 // optional minute : 00 to 59 085 + "([0-5]\\d)" 086 // optional second | leap second 087 + "([0-5]\\d|60)?" 088 + ")?" 089 // fraction 090 + "([.,]\\d+)?" 091 // time-zone 092 + "(Z|[+-]([01]\\d|2[0-3])([0-5]\\d)?)$"; 093 094 /** The date pattern. The regexp pattern is immutable, only one instance needed. */ 095 private static final Pattern DATE_PATTERN = Pattern.compile( GENERALIZED_TIME_PATTERN ); 096 097 /** 098 * A static instance of GeneralizedTimeSyntaxChecker 099 */ 100 public static final GeneralizedTimeSyntaxChecker INSTANCE = 101 new GeneralizedTimeSyntaxChecker( SchemaConstants.GENERALIZED_TIME_SYNTAX ); 102 103 /** 104 * A static Builder for this class 105 */ 106 public static final class Builder extends SCBuilder<GeneralizedTimeSyntaxChecker> 107 { 108 /** 109 * The Builder constructor 110 */ 111 private Builder() 112 { 113 super( SchemaConstants.GENERALIZED_TIME_SYNTAX ); 114 } 115 116 117 /** 118 * Create a new instance of GeneralizedTimeSyntaxChecker 119 * @return A new instance of GeneralizedTimeSyntaxChecker 120 */ 121 @Override 122 public GeneralizedTimeSyntaxChecker build() 123 { 124 return new GeneralizedTimeSyntaxChecker( oid ); 125 } 126 } 127 128 129 /** 130 * Creates a new instance of GeneralizedTimeSyntaxChecker. 131 * 132 * @param oid The OID to use for this SyntaxChecker 133 */ 134 private GeneralizedTimeSyntaxChecker( String oid ) 135 { 136 super( oid ); 137 } 138 139 140 /** 141 * @return An instance of the Builder for this class 142 */ 143 public static Builder builder() 144 { 145 return new Builder(); 146 } 147 148 149 /** 150 * {@inheritDoc} 151 */ 152 @Override 153 public boolean isValidSyntax( Object value ) 154 { 155 String strValue; 156 157 if ( value == null ) 158 { 159 if ( LOG.isDebugEnabled() ) 160 { 161 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) ); 162 } 163 164 return false; 165 } 166 167 if ( value instanceof String ) 168 { 169 strValue = ( String ) value; 170 } 171 else if ( value instanceof byte[] ) 172 { 173 strValue = Strings.utf8ToString( ( byte[] ) value ); 174 } 175 else 176 { 177 strValue = value.toString(); 178 } 179 180 // A generalized time must have a minimal length of 11 181 if ( strValue.length() < 11 ) 182 { 183 if ( LOG.isDebugEnabled() ) 184 { 185 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 186 } 187 188 return false; 189 } 190 191 // Start the date parsing 192 boolean result = DATE_PATTERN.matcher( strValue ).find(); 193 194 if ( LOG.isDebugEnabled() ) 195 { 196 if ( result ) 197 { 198 LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) ); 199 } 200 else 201 { 202 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 203 } 204 } 205 206 return result; 207 } 208}