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; 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 /** Initialization of the country set */ 061 static 062 { 063 for ( String country : DSE_BITS_STRINGS ) 064 { 065 DSE_BITS.add( country ); 066 } 067 } 068 069 /** 070 * A static instance of DseTypeSyntaxChecker 071 */ 072 public static final DseTypeSyntaxChecker INSTANCE = new DseTypeSyntaxChecker( SchemaConstants.DSE_TYPE_SYNTAX ); 073 074 /** 075 * A static Builder for this class 076 */ 077 public static final class Builder extends SCBuilder<DseTypeSyntaxChecker> 078 { 079 /** 080 * The Builder constructor 081 */ 082 private Builder() 083 { 084 super( SchemaConstants.DSE_TYPE_SYNTAX ); 085 } 086 087 088 /** 089 * Create a new instance of DseTypeSyntaxChecker 090 * @return A new instance of DseTypeSyntaxChecker 091 */ 092 @Override 093 public DseTypeSyntaxChecker build() 094 { 095 return new DseTypeSyntaxChecker( oid ); 096 } 097 } 098 099 100 /** Initialization of the country set */ 101 static 102 { 103 for ( String country : DSE_BITS_STRINGS ) 104 { 105 DSE_BITS.add( country ); 106 } 107 } 108 109 110 /** 111 * Creates a new instance of DSETypeSyntaxChecker. 112 * 113 * @param oid The OID to use for this SyntaxChecker 114 */ 115 private DseTypeSyntaxChecker( String oid ) 116 { 117 super( oid ); 118 } 119 120 121 /** 122 * @return An instance of the Builder for this class 123 */ 124 public static Builder builder() 125 { 126 return new Builder(); 127 } 128 129 130 /** 131 * {@inheritDoc} 132 */ 133 @Override 134 public boolean isValidSyntax( Object value ) 135 { 136 String strValue; 137 138 if ( value == null ) 139 { 140 if ( LOG.isDebugEnabled() ) 141 { 142 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) ); 143 } 144 145 return false; 146 } 147 148 if ( value instanceof String ) 149 { 150 strValue = ( String ) value; 151 } 152 else if ( value instanceof byte[] ) 153 { 154 strValue = Strings.utf8ToString( ( byte[] ) value ); 155 } 156 else 157 { 158 strValue = value.toString(); 159 } 160 161 // We must have at least '(cp)', '(xr)' or '(ca)' 162 if ( strValue.length() < 4 ) 163 { 164 if ( LOG.isDebugEnabled() ) 165 { 166 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 167 } 168 169 return false; 170 } 171 172 // Check the opening and closing parenthesis 173 if ( ( strValue.charAt( 0 ) != '(' ) 174 || ( strValue.charAt( strValue.length() - 1 ) != ')' ) ) 175 { 176 if ( LOG.isDebugEnabled() ) 177 { 178 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 179 } 180 181 return false; 182 } 183 184 Set<String> keywords = new HashSet<>(); 185 int len = strValue.length() - 1; 186 boolean needKeyword = true; 187 188 // 189 for ( int i = 1; i < len; /* */) 190 { 191 // Skip spaces 192 while ( ( i < len ) && ( strValue.charAt( i ) == ' ' ) ) 193 { 194 i++; 195 } 196 197 int pos = i; 198 199 // Search for a keyword 200 while ( ( i < len ) && Chars.isAlphaASCII( strValue, pos ) ) 201 { 202 pos++; 203 } 204 205 if ( pos == i ) 206 { 207 // No keyword : error 208 if ( LOG.isDebugEnabled() ) 209 { 210 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 211 } 212 213 return false; 214 } 215 216 String keyword = strValue.substring( i, pos ); 217 i = pos; 218 219 if ( !DSE_BITS.contains( keyword ) ) 220 { 221 // Unknown keyword 222 if ( LOG.isDebugEnabled() ) 223 { 224 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 225 } 226 227 return false; 228 } 229 230 // Check that the keyword has not been met 231 if ( keywords.contains( keyword ) ) 232 { 233 if ( LOG.isDebugEnabled() ) 234 { 235 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 236 } 237 238 return false; 239 } 240 241 keywords.add( keyword ); 242 needKeyword = false; 243 244 // Skip spaces 245 while ( ( i < len ) && ( strValue.charAt( i ) == ' ' ) ) 246 { 247 i++; 248 } 249 250 // Do we have another keyword ? 251 if ( ( i < len ) && ( strValue.charAt( i ) == '$' ) ) 252 { 253 // yes 254 i++; 255 needKeyword = true; 256 } 257 } 258 259 // We are done 260 if ( LOG.isDebugEnabled() ) 261 { 262 if ( needKeyword ) 263 { 264 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 265 } 266 else 267 { 268 LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) ); 269 } 270 } 271 272 return !needKeyword; 273 } 274}