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 * http://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; 025 026import org.apache.directory.api.i18n.I18n; 027import org.apache.directory.api.ldap.model.constants.SchemaConstants; 028import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 029import org.apache.directory.api.util.Chars; 030import org.apache.directory.api.util.Strings; 031 032 033/** 034 * A SyntaxChecker which verifies that a value is a DSEType according to 035 * http://tools.ietf.org/id/draft-ietf-asid-ldapv3-attributes-03.txt, par 6.2.1.5 : 036 * <pre> 037 * <DSEType> ::= '(' <sp>* <DSEBit> <sp>* <DSEBitList> ')' 038 * <DSEBitList> ::= '$' <sp>* <DSEBit> <sp>* <DSEBitList> | e 039 * <DSEBit> ::= 'root' | 'glue' | 'cp' | 'entry' | 'alias' | 'subr' | 040 * 'nssr' | 'supr' | 'xr' | 'admPoint' | 'subentry' | 041 * 'shadow' | 'zombie' | 'immSupr' | 'rhob' | 'sa' 042 * </pre> 043 * 044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 045 */ 046@SuppressWarnings("serial") 047public final class DseTypeSyntaxChecker extends SyntaxChecker 048{ 049 /** The DSE BITS keywords */ 050 private static final String[] DSE_BITS_STRINGS = 051 { 052 "root", "glue", "cp", "entry", "alias", "subr", 053 "nssr", "supr", "xr", "admPoint", "subentry", 054 "shadow", "zombie", "immSupr", "rhob", "sa" 055 }; 056 057 /** The Set which contains the DESBits */ 058 private static final Set<String> DSE_BITS = new HashSet<>(); 059 060 /** 061 * A static instance of DseTypeSyntaxChecker 062 */ 063 public static final DseTypeSyntaxChecker INSTANCE = new DseTypeSyntaxChecker( SchemaConstants.DSE_TYPE_SYNTAX ); 064 065 /** 066 * A static Builder for this class 067 */ 068 public static final class Builder extends SCBuilder<DseTypeSyntaxChecker> 069 { 070 /** 071 * The Builder constructor 072 */ 073 private Builder() 074 { 075 super( SchemaConstants.DSE_TYPE_SYNTAX ); 076 } 077 078 079 /** 080 * Create a new instance of DseTypeSyntaxChecker 081 * @return A new instance of DseTypeSyntaxChecker 082 */ 083 @Override 084 public DseTypeSyntaxChecker build() 085 { 086 return new DseTypeSyntaxChecker( oid ); 087 } 088 } 089 090 091 /** Initialization of the country set */ 092 static 093 { 094 for ( String country : DSE_BITS_STRINGS ) 095 { 096 DSE_BITS.add( country ); 097 } 098 } 099 100 101 /** 102 * Creates a new instance of DSETypeSyntaxChecker. 103 * 104 * @param oid The OID to use for this SyntaxChecker 105 */ 106 private DseTypeSyntaxChecker( String oid ) 107 { 108 super( oid ); 109 } 110 111 112 /** 113 * @return An instance of the Builder for this class 114 */ 115 public static Builder builder() 116 { 117 return new Builder(); 118 } 119 120 121 /** 122 * {@inheritDoc} 123 */ 124 @Override 125 public boolean isValidSyntax( Object value ) 126 { 127 String strValue; 128 129 if ( value == null ) 130 { 131 if ( LOG.isDebugEnabled() ) 132 { 133 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, "null" ) ); 134 } 135 136 return false; 137 } 138 139 if ( value instanceof String ) 140 { 141 strValue = ( String ) value; 142 } 143 else if ( value instanceof byte[] ) 144 { 145 strValue = Strings.utf8ToString( ( byte[] ) value ); 146 } 147 else 148 { 149 strValue = value.toString(); 150 } 151 152 // We must have at least '(cp)', '(xr)' or '(ca)' 153 if ( strValue.length() < 4 ) 154 { 155 if ( LOG.isDebugEnabled() ) 156 { 157 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) ); 158 } 159 160 return false; 161 } 162 163 // Check the opening and closing parenthesis 164 if ( ( strValue.charAt( 0 ) != '(' ) 165 || ( strValue.charAt( strValue.length() - 1 ) != ')' ) ) 166 { 167 if ( LOG.isDebugEnabled() ) 168 { 169 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) ); 170 } 171 172 return false; 173 } 174 175 Set<String> keywords = new HashSet<>(); 176 int len = strValue.length() - 1; 177 boolean needKeyword = true; 178 179 // 180 for ( int i = 1; i < len; /* */) 181 { 182 // Skip spaces 183 while ( ( i < len ) && ( strValue.charAt( i ) == ' ' ) ) 184 { 185 i++; 186 } 187 188 int pos = i; 189 190 // Search for a keyword 191 while ( ( i < len ) && Chars.isAlphaASCII( strValue, pos ) ) 192 { 193 pos++; 194 } 195 196 if ( pos == i ) 197 { 198 // No keyword : error 199 if ( LOG.isDebugEnabled() ) 200 { 201 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) ); 202 } 203 204 return false; 205 } 206 207 String keyword = strValue.substring( i, pos ); 208 i = pos; 209 210 if ( !DSE_BITS.contains( keyword ) ) 211 { 212 // Unknown keyword 213 if ( LOG.isDebugEnabled() ) 214 { 215 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) ); 216 } 217 218 return false; 219 } 220 221 // Check that the keyword has not been met 222 if ( keywords.contains( keyword ) ) 223 { 224 if ( LOG.isDebugEnabled() ) 225 { 226 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) ); 227 } 228 229 return false; 230 } 231 232 keywords.add( keyword ); 233 needKeyword = false; 234 235 // Skip spaces 236 while ( ( i < len ) && ( strValue.charAt( i ) == ' ' ) ) 237 { 238 i++; 239 } 240 241 // Do we have another keyword ? 242 if ( ( i < len ) && ( strValue.charAt( i ) == '$' ) ) 243 { 244 // yes 245 i++; 246 needKeyword = true; 247 continue; 248 } 249 } 250 251 // We are done 252 if ( LOG.isDebugEnabled() ) 253 { 254 if ( needKeyword ) 255 { 256 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) ); 257 } 258 else 259 { 260 LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) ); 261 } 262 } 263 264 return !needKeyword; 265 } 266}