001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.schema; 028 import org.opends.messages.Message; 029 030 031 032 import org.opends.server.admin.std.server.EqualityMatchingRuleCfg; 033 import org.opends.server.api.EqualityMatchingRule; 034 import org.opends.server.config.ConfigException; 035 import org.opends.server.protocols.asn1.ASN1OctetString; 036 import org.opends.server.types.AttributeValue; 037 import org.opends.server.types.ByteString; 038 import org.opends.server.types.DirectoryException; 039 import org.opends.server.types.InitializationException; 040 import org.opends.server.types.ResultCode; 041 042 import static org.opends.server.loggers.debug.DebugLogger.*; 043 import org.opends.server.loggers.debug.DebugTracer; 044 import org.opends.server.types.DebugLogLevel; 045 import static org.opends.messages.SchemaMessages.*; 046 import static org.opends.server.schema.SchemaConstants.*; 047 import static org.opends.server.util.StaticUtils.*; 048 049 050 051 /** 052 * This class implements the integerFirstComponentMatch matching rule defined in 053 * X.520 and referenced in RFC 2252. This rule is intended for use with 054 * attributes whose values contain a set of parentheses enclosing a 055 * space-delimited set of names and/or name-value pairs (like attribute type or 056 * objectclass descriptions) in which the "first component" is the first item 057 * after the opening parenthesis. 058 */ 059 public class IntegerFirstComponentEqualityMatchingRule 060 extends EqualityMatchingRule 061 { 062 /** 063 * The tracer object for the debug logger. 064 */ 065 private static final DebugTracer TRACER = getTracer(); 066 067 068 069 070 /** 071 * Creates a new instance of this integerFirstComponentMatch matching rule. 072 */ 073 public IntegerFirstComponentEqualityMatchingRule() 074 { 075 super(); 076 } 077 078 079 080 /** 081 * {@inheritDoc} 082 */ 083 public void initializeMatchingRule(EqualityMatchingRuleCfg configuration) 084 throws ConfigException, InitializationException 085 { 086 // No initialization is required. 087 } 088 089 090 091 /** 092 * Retrieves the common name for this matching rule. 093 * 094 * @return The common name for this matching rule, or <CODE>null</CODE> if 095 * it does not have a name. 096 */ 097 public String getName() 098 { 099 return EMR_INTEGER_FIRST_COMPONENT_NAME; 100 } 101 102 103 104 /** 105 * Retrieves the OID for this matching rule. 106 * 107 * @return The OID for this matching rule. 108 */ 109 public String getOID() 110 { 111 return EMR_INTEGER_FIRST_COMPONENT_OID; 112 } 113 114 115 116 /** 117 * Retrieves the description for this matching rule. 118 * 119 * @return The description for this matching rule, or <CODE>null</CODE> if 120 * there is none. 121 */ 122 public String getDescription() 123 { 124 // There is no standard description for this matching rule. 125 return null; 126 } 127 128 129 130 /** 131 * Retrieves the OID of the syntax with which this matching rule is 132 * associated. 133 * 134 * @return The OID of the syntax with which this matching rule is associated. 135 */ 136 public String getSyntaxOID() 137 { 138 return SYNTAX_INTEGER_OID; 139 } 140 141 142 143 /** 144 * Retrieves the normalized form of the provided value, which is best suited 145 * for efficiently performing matching operations on that value. 146 * 147 * @param value The value to be normalized. 148 * 149 * @return The normalized version of the provided value. 150 * 151 * @throws DirectoryException If the provided value is invalid according to 152 * the associated attribute syntax. 153 */ 154 public ByteString normalizeValue(ByteString value) 155 throws DirectoryException 156 { 157 StringBuilder buffer = new StringBuilder(); 158 toLowerCase(value.value(), buffer, true); 159 160 int bufferLength = buffer.length(); 161 if (bufferLength == 0) 162 { 163 if (value.value().length > 0) 164 { 165 // This should only happen if the value is composed entirely of spaces. 166 // In that case, the normalized value is a single space. 167 return new ASN1OctetString(" "); 168 } 169 else 170 { 171 // The value is empty, so it is already normalized. 172 return new ASN1OctetString(); 173 } 174 } 175 176 177 // Replace any consecutive spaces with a single space. 178 for (int pos = bufferLength-1; pos > 0; pos--) 179 { 180 if (buffer.charAt(pos) == ' ') 181 { 182 if (buffer.charAt(pos-1) == ' ') 183 { 184 buffer.delete(pos, pos+1); 185 } 186 } 187 } 188 189 return new ASN1OctetString(buffer.toString()); 190 } 191 192 193 194 /** 195 * Indicates whether the two provided normalized values are equal to each 196 * other. 197 * 198 * @param value1 The normalized form of the first value to compare. 199 * @param value2 The normalized form of the second value to compare. 200 * 201 * @return <CODE>true</CODE> if the provided values are equal, or 202 * <CODE>false</CODE> if not. 203 */ 204 public boolean areEqual(ByteString value1, ByteString value2) 205 { 206 try 207 { 208 int intValue1 = extractIntValue(value1.stringValue()); 209 int intValue2 = extractIntValue(value2.stringValue()); 210 211 return (intValue1 == intValue2); 212 } 213 catch (Exception e) 214 { 215 if (debugEnabled()) 216 { 217 TRACER.debugCaught(DebugLogLevel.ERROR, e); 218 } 219 220 return false; 221 } 222 } 223 224 225 226 /** 227 * Generates a hash code for the provided attribute value. This version of 228 * the method will simply create a hash code from the normalized form of the 229 * attribute value. For matching rules explicitly designed to work in cases 230 * where byte-for-byte comparisons of normalized values is not sufficient for 231 * determining equality (e.g., if the associated attribute syntax is based on 232 * hashed or encrypted values), then this method must be overridden to provide 233 * an appropriate implementation for that case. 234 * 235 * @param attributeValue The attribute value for which to generate the hash 236 * code. 237 * 238 * @return The hash code generated for the provided attribute value. 239 */ 240 public int generateHashCode(AttributeValue attributeValue) 241 { 242 // In this case, we'll always return the same value because the matching 243 // isn't based on the entire value. 244 return 1; 245 } 246 247 248 249 /** 250 * Extracts the integer portion from the provided value string. 251 * 252 * @param valueString The value string from which to extract the integer 253 * portion. 254 * 255 * @return The extracted integer portion from the value string, 256 * 257 * @throws DirectoryException If a problem occurs while trying to extract 258 * the integer value. 259 */ 260 private static int extractIntValue(String valueString) 261 throws DirectoryException 262 { 263 int valueLength = valueString.length(); 264 265 if ((valueLength == 0) || (valueString.charAt(0) != '(')) 266 { 267 // We'll check to see if the entire string is an integer. If so, then 268 // use that value. If not, then fail. 269 try 270 { 271 return Integer.parseInt(valueString); 272 } 273 catch (Exception e) 274 { 275 if (debugEnabled()) 276 { 277 TRACER.debugCaught(DebugLogLevel.ERROR, e); 278 } 279 280 Message message = ERR_EMR_INTFIRSTCOMP_NO_INITIAL_PARENTHESIS.get( 281 String.valueOf(valueString)); 282 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 283 message, e); 284 } 285 } 286 287 char c; 288 int pos = 1; 289 while ((pos < valueLength) && ((c = valueString.charAt(pos)) == ' ')) 290 { 291 pos++; 292 } 293 294 if (pos >= valueLength) 295 { 296 Message message = 297 ERR_EMR_INTFIRSTCOMP_NO_NONSPACE.get(String.valueOf(valueString)); 298 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 299 message); 300 } 301 302 303 // The current position must be the start position for the value. Keep 304 // reading until we find the next space. 305 int startPos = pos++; 306 while ((pos < valueLength) && ((c = valueString.charAt(pos)) != ' ')) 307 { 308 pos++; 309 } 310 311 if (pos >= valueLength) 312 { 313 Message message = ERR_EMR_INTFIRSTCOMP_NO_SPACE_AFTER_INT.get( 314 String.valueOf(valueString)); 315 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 316 message); 317 } 318 319 320 // We should now have the position of the integer value. Make sure it's an 321 // integer and return it. 322 try 323 { 324 return Integer.parseInt(valueString.substring(startPos, pos)); 325 } 326 catch (Exception e) 327 { 328 if (debugEnabled()) 329 { 330 TRACER.debugCaught(DebugLogLevel.ERROR, e); 331 } 332 333 Message message = ERR_EMR_INTFIRSTCOMP_FIRST_COMPONENT_NOT_INT.get( 334 String.valueOf(valueString)); 335 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 336 message); 337 } 338 } 339 } 340