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 java.util.Arrays; 033 034 import org.opends.server.admin.std.server.EqualityMatchingRuleCfg; 035 import org.opends.server.api.EqualityMatchingRule; 036 import org.opends.server.config.ConfigException; 037 import org.opends.server.core.DirectoryServer; 038 import org.opends.server.protocols.asn1.ASN1OctetString; 039 import org.opends.server.types.ByteString; 040 import org.opends.server.types.DirectoryException; 041 import org.opends.server.types.DN; 042 import org.opends.server.types.InitializationException; 043 import org.opends.server.types.ResultCode; 044 045 import static org.opends.server.loggers.debug.DebugLogger.*; 046 import org.opends.server.loggers.debug.DebugTracer; 047 import org.opends.server.loggers.ErrorLogger; 048 import org.opends.server.types.DebugLogLevel; 049 import static org.opends.messages.SchemaMessages.*; 050 import static org.opends.server.schema.SchemaConstants.*; 051 import static org.opends.server.util.StaticUtils.*; 052 053 054 055 /** 056 * This class implements the uniqueMemberMatch matching rule defined in X.520 057 * and referenced in RFC 2252. It is based on the name and optional UID syntax, 058 * and will compare values with a distinguished name and optional bit string 059 * suffix. 060 */ 061 public class UniqueMemberEqualityMatchingRule 062 extends EqualityMatchingRule 063 { 064 /** 065 * The tracer object for the debug logger. 066 */ 067 private static final DebugTracer TRACER = getTracer(); 068 069 070 071 /** 072 * Creates a new instance of this uniqueMemberMatch matching rule. 073 */ 074 public UniqueMemberEqualityMatchingRule() 075 { 076 super(); 077 } 078 079 080 081 /** 082 * {@inheritDoc} 083 */ 084 public void initializeMatchingRule(EqualityMatchingRuleCfg configuration) 085 throws ConfigException, InitializationException 086 { 087 // No initialization is required. 088 } 089 090 091 092 /** 093 * Retrieves the common name for this matching rule. 094 * 095 * @return The common name for this matching rule, or <CODE>null</CODE> if 096 * it does not have a name. 097 */ 098 public String getName() 099 { 100 return EMR_UNIQUE_MEMBER_NAME; 101 } 102 103 104 105 /** 106 * Retrieves the OID for this matching rule. 107 * 108 * @return The OID for this matching rule. 109 */ 110 public String getOID() 111 { 112 return EMR_UNIQUE_MEMBER_OID; 113 } 114 115 116 117 /** 118 * Retrieves the description for this matching rule. 119 * 120 * @return The description for this matching rule, or <CODE>null</CODE> if 121 * there is none. 122 */ 123 public String getDescription() 124 { 125 // There is no standard description for this matching rule. 126 return null; 127 } 128 129 130 131 /** 132 * Retrieves the OID of the syntax with which this matching rule is 133 * associated. 134 * 135 * @return The OID of the syntax with which this matching rule is associated. 136 */ 137 public String getSyntaxOID() 138 { 139 return SYNTAX_NAME_AND_OPTIONAL_UID_OID; 140 } 141 142 143 144 /** 145 * Retrieves the normalized form of the provided value, which is best suited 146 * for efficiently performing matching operations on that value. 147 * 148 * @param value The value to be normalized. 149 * 150 * @return The normalized version of the provided value. 151 * 152 * @throws DirectoryException If the provided value is invalid according to 153 * the associated attribute syntax. 154 */ 155 public ByteString normalizeValue(ByteString value) 156 throws DirectoryException 157 { 158 String valueString = value.stringValue().trim(); 159 int valueLength = valueString.length(); 160 161 162 // See if the value contains the "optional uid" portion. If we think it 163 // does, then mark its location. 164 int dnEndPos = valueLength; 165 int sharpPos = -1; 166 if (valueString.endsWith("'B") || valueString.endsWith("'b")) 167 { 168 sharpPos = valueString.lastIndexOf("#'"); 169 if (sharpPos > 0) 170 { 171 dnEndPos = sharpPos; 172 } 173 } 174 175 176 // Take the DN portion of the string and try to normalize it. If it fails, 177 // then this will throw an exception. 178 StringBuilder valueBuffer = new StringBuilder(valueLength); 179 try 180 { 181 DN dn = DN.decode(valueString.substring(0, dnEndPos)); 182 dn.toNormalizedString(valueBuffer); 183 } 184 catch (Exception e) 185 { 186 if (debugEnabled()) 187 { 188 TRACER.debugCaught(DebugLogLevel.ERROR, e); 189 } 190 191 // We couldn't normalize the DN for some reason. If we're supposed to use 192 // strict syntax enforcement, then throw an exception. Otherwise, log a 193 // message and just try our best. 194 Message message = ERR_ATTR_SYNTAX_NAMEANDUID_INVALID_DN.get( 195 valueString, getExceptionMessage(e)); 196 197 switch (DirectoryServer.getSyntaxEnforcementPolicy()) 198 { 199 case REJECT: 200 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 201 message); 202 case WARN: 203 ErrorLogger.logError(message); 204 205 valueBuffer.append(toLowerCase(valueString).substring(0, dnEndPos)); 206 break; 207 208 default: 209 valueBuffer.append(toLowerCase(valueString).substring(0, dnEndPos)); 210 break; 211 } 212 } 213 214 215 216 // If there is an "optional uid", then normalize it and make sure it only 217 // contains valid binary digits. 218 if (sharpPos > 0) 219 { 220 valueBuffer.append("#'"); 221 222 int endPos = valueLength - 2; 223 boolean logged = false; 224 for (int i=sharpPos+2; i < endPos; i++) 225 { 226 char c = valueString.charAt(i); 227 if ((c == '0') || (c == '1')) 228 { 229 valueBuffer.append(c); 230 } 231 else 232 { 233 // There was an invalid binary digit. We'll either throw an exception 234 // or log a message and continue, based on the server's configuration. 235 Message message = ERR_ATTR_SYNTAX_NAMEANDUID_ILLEGAL_BINARY_DIGIT.get( 236 valueString, String.valueOf(c), i); 237 238 switch (DirectoryServer.getSyntaxEnforcementPolicy()) 239 { 240 case REJECT: 241 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 242 message); 243 case WARN: 244 if (! logged) 245 { 246 ErrorLogger.logError(message); 247 logged = true; 248 } 249 break; 250 } 251 } 252 } 253 254 valueBuffer.append("'B"); 255 } 256 257 return new ASN1OctetString(valueBuffer.toString()); 258 } 259 260 261 262 /** 263 * Indicates whether the two provided normalized values are equal to each 264 * other. 265 * 266 * @param value1 The normalized form of the first value to compare. 267 * @param value2 The normalized form of the second value to compare. 268 * 269 * @return <CODE>true</CODE> if the provided values are equal, or 270 * <CODE>false</CODE> if not. 271 */ 272 public boolean areEqual(ByteString value1, ByteString value2) 273 { 274 // Since the values are already normalized, we just need to compare the 275 // associated byte arrays. 276 return Arrays.equals(value1.value(), value2.value()); 277 } 278 } 279