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.AcceptRejectWarn;
040 import org.opends.server.types.ByteString;
041 import org.opends.server.types.DirectoryException;
042 import org.opends.server.types.DN;
043 import org.opends.server.types.InitializationException;
044 import org.opends.server.types.ResultCode;
045
046 import static org.opends.server.loggers.debug.DebugLogger.*;
047 import org.opends.server.loggers.debug.DebugTracer;
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 defines the distinguishedNameMatch matching rule defined in X.520
057 * and referenced in RFC 2252.
058 */
059 public class DistinguishedNameEqualityMatchingRule
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 caseExactMatch matching rule.
072 */
073 public DistinguishedNameEqualityMatchingRule()
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_DN_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_DN_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_DN_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 // Since the normalization for DNs is so complex, it will be handled
158 // elsewhere.
159 DN dn;
160 try
161 {
162 dn = DN.decode(value.stringValue());
163 }
164 catch (DirectoryException de)
165 {
166 if (debugEnabled())
167 {
168 TRACER.debugCaught(DebugLogLevel.ERROR, de);
169 }
170
171 // See if we should try to proceed anyway with a bare-bones normalization.
172 if (DirectoryServer.getSyntaxEnforcementPolicy() ==
173 AcceptRejectWarn.REJECT)
174 {
175 throw de;
176 }
177
178 return bestEffortNormalize(toLowerCase(value.stringValue()));
179 }
180 catch (Exception e)
181 {
182 if (debugEnabled())
183 {
184 TRACER.debugCaught(DebugLogLevel.ERROR, e);
185 }
186
187 if (DirectoryServer.getSyntaxEnforcementPolicy() ==
188 AcceptRejectWarn.REJECT)
189 {
190 Message message = ERR_ATTR_SYNTAX_DN_INVALID.get(
191 value.stringValue(), String.valueOf(e));
192 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
193 message);
194 }
195 else
196 {
197 return bestEffortNormalize(toLowerCase(value.stringValue()));
198 }
199 }
200
201 return new ASN1OctetString(dn.toNormalizedString());
202 }
203
204
205
206 /**
207 * Performs "best-effort" normalization on the provided string in the event
208 * that the real DN normalization code rejected the value. It will simply
209 * attempt to strip out any spaces that it thinks might be unnecessary.
210 *
211 * @param lowerString The all-lowercase version of the string to normalize.
212 *
213 * @return A best-effort normalized version of the provided value.
214 */
215 private ByteString bestEffortNormalize(String lowerString)
216 {
217 int length = lowerString.length();
218 StringBuilder buffer = new StringBuilder(length);
219
220 for (int i=0; i < length; i++)
221 {
222 char c = lowerString.charAt(i);
223 if (c == ' ')
224 {
225 if (i == 0)
226 {
227 // A space at the beginning of the value will be ignored.
228 continue;
229 }
230 else
231 {
232 // Look at the previous character. If it was a backslash, then keep
233 // the space. If it was a comma, then skip the space. Otherwise, keep
234 // processing.
235 char previous = lowerString.charAt(i-1);
236 if (previous == '\\')
237 {
238 buffer.append(' ');
239 continue;
240 }
241 else if (previous == ',')
242 {
243 continue;
244 }
245 }
246
247
248 if (i == (length-1))
249 {
250 // A space at the end of the value will be ignored.
251 break;
252 }
253 else
254 {
255 // Look at the next character. If it is a space or a comma, then skip
256 // the space. Otherwise, include it.
257 char next = lowerString.charAt(i+1);
258 if ((next == ' ') || (next == ','))
259 {
260 continue;
261 }
262 else
263 {
264 buffer.append(' ');
265 }
266 }
267 }
268 else
269 {
270 // It's not a space, so we'll include it.
271 buffer.append(c);
272 }
273 }
274
275 return new ASN1OctetString(buffer.toString());
276 }
277
278
279
280 /**
281 * Indicates whether the two provided normalized values are equal to each
282 * other.
283 *
284 * @param value1 The normalized form of the first value to compare.
285 * @param value2 The normalized form of the second value to compare.
286 *
287 * @return <CODE>true</CODE> if the provided values are equal, or
288 * <CODE>false</CODE> if not.
289 */
290 public boolean areEqual(ByteString value1, ByteString value2)
291 {
292 // Since the values are already normalized, we just need to compare the
293 // associated byte arrays.
294 return Arrays.equals(value1.value(), value2.value());
295 }
296 }
297