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