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 * http://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 */
020 package org.apache.directory.shared.ldap.entry;
021
022 import org.apache.directory.shared.ldap.exception.LdapException;
023
024 import org.apache.directory.shared.i18n.I18n;
025 import org.apache.directory.shared.ldap.schema.AttributeType;
026 import org.apache.directory.shared.ldap.schema.LdapComparator;
027 import org.apache.directory.shared.ldap.schema.MatchingRule;
028 import org.apache.directory.shared.ldap.schema.Normalizer;
029 import org.apache.directory.shared.ldap.schema.SyntaxChecker;
030 import org.slf4j.Logger;
031 import org.slf4j.LoggerFactory;
032
033
034 /**
035 * A wrapper around byte[] values in entries.
036 *
037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038 * @version $Rev$, $Date$
039 */
040 public abstract class AbstractValue<T> implements Value<T>
041 {
042 /** logger for reporting errors that might not be handled properly upstream */
043 private static final Logger LOG = LoggerFactory.getLogger( AbstractValue.class );
044
045 /** reference to the attributeType zssociated with the value */
046 protected transient AttributeType attributeType;
047
048 /** the wrapped binary value */
049 protected T wrappedValue;
050
051 /** the canonical representation of the wrapped value */
052 protected T normalizedValue;
053
054 /** A flag set when the value has been normalized */
055 protected boolean normalized;
056
057 /** cached results of the isValid() method call */
058 protected Boolean valid;
059
060 /** A flag set if the normalized data is different from the wrapped data */
061 protected transient boolean same;
062
063 /**
064 * {@inheritDoc}
065 */
066 public Value<T> clone()
067 {
068 try
069 {
070 return (Value<T>)super.clone();
071 }
072 catch ( CloneNotSupportedException cnse )
073 {
074 // Do nothing
075 return null;
076 }
077 }
078
079
080 /**
081 * Gets a reference to the wrapped binary value.
082 *
083 * Warning ! The value is not copied !!!
084 *
085 * @return a direct handle on the binary value that is wrapped
086 */
087 public T getReference()
088 {
089 return wrappedValue;
090 }
091
092
093 /**
094 * Get the associated AttributeType
095 * @return The AttributeType
096 */
097 public AttributeType getAttributeType()
098 {
099 return attributeType;
100 }
101
102
103 public void apply( AttributeType attributeType )
104 {
105 if ( this.attributeType != null )
106 {
107 if ( !attributeType.equals( this.attributeType ) )
108 {
109 throw new IllegalArgumentException( I18n.err( I18n.ERR_04476, attributeType.getName(), this.attributeType.getName() ) );
110 }
111 else
112 {
113 return;
114 }
115 }
116
117 this.attributeType = attributeType;
118
119 try
120 {
121 normalize();
122 }
123 catch ( LdapException ne )
124 {
125 String message = I18n.err( I18n.ERR_04447, ne.getLocalizedMessage() );
126 LOG.info( message );
127 normalized = false;
128 }
129 }
130
131
132 /**
133 * Gets a comparator using getMatchingRule() to resolve the matching
134 * that the comparator is extracted from.
135 *
136 * @return a comparator associated with the attributeType or null if one cannot be found
137 * @throws LdapException if resolution of schema entities fail
138 */
139 protected LdapComparator<T> getLdapComparator() throws LdapException
140 {
141 if ( attributeType != null )
142 {
143 MatchingRule mr = getMatchingRule();
144
145 if ( mr == null )
146 {
147 return null;
148 }
149
150 return (LdapComparator<T>)mr.getLdapComparator();
151 }
152 else
153 {
154 return null;
155 }
156 }
157
158
159 /**
160 * Find a matchingRule to use for normalization and comparison. If an equality
161 * matchingRule cannot be found it checks to see if other matchingRules are
162 * available: SUBSTR, and ORDERING. If a matchingRule cannot be found null is
163 * returned.
164 *
165 * @return a matchingRule or null if one cannot be found for the attributeType
166 * @throws LdapException if resolution of schema entities fail
167 */
168 protected MatchingRule getMatchingRule() throws LdapException
169 {
170 if ( attributeType != null )
171 {
172 MatchingRule mr = attributeType.getEquality();
173
174 if ( mr == null )
175 {
176 mr = attributeType.getOrdering();
177 }
178
179 if ( mr == null )
180 {
181 mr = attributeType.getSubstring();
182 }
183
184 return mr;
185 }
186 else
187 {
188 return null;
189 }
190 }
191
192
193 /**
194 * Gets a normalizer using getMatchingRule() to resolve the matchingRule
195 * that the normalizer is extracted from.
196 *
197 * @return a normalizer associated with the attributeType or null if one cannot be found
198 * @throws LdapException if resolution of schema entities fail
199 */
200 protected Normalizer getNormalizer() throws LdapException
201 {
202 if ( attributeType != null )
203 {
204 MatchingRule mr = getMatchingRule();
205
206 if ( mr == null )
207 {
208 return null;
209 }
210
211 return mr.getNormalizer();
212 }
213 else
214 {
215 return null;
216 }
217 }
218
219
220 /**
221 * Check if the value is stored into an instance of the given
222 * AttributeType, or one of its ascendant.
223 *
224 * For instance, if the Value is associated with a CommonName,
225 * checking for Name will match.
226 *
227 * @param attributeType The AttributeType we are looking at
228 * @return <code>true</code> if the value is associated with the given
229 * attributeType or one of its ascendant
230 */
231 public boolean instanceOf( AttributeType attributeType ) throws LdapException
232 {
233 if ( ( attributeType != null ) && this.attributeType.equals( attributeType ) )
234 {
235 if ( this.attributeType.equals( attributeType ) )
236 {
237 return true;
238 }
239
240 return this.attributeType.isDescendantOf( attributeType );
241 }
242
243 return false;
244 }
245
246
247 /**
248 * Gets the normalized (canonical) representation for the wrapped value.
249 * If the wrapped value is null, null is returned, otherwise the normalized
250 * form is returned. If the normalized Value is null, then the wrapped
251 * value is returned
252 *
253 * @return gets the normalized value
254 */
255 public T getNormalizedValue()
256 {
257 if ( isNull() )
258 {
259 return null;
260 }
261
262 if ( normalizedValue == null )
263 {
264 return get();
265 }
266
267 return getNormalizedValueCopy();
268 }
269
270
271 /**
272 * Gets a reference to the the normalized (canonical) representation
273 * for the wrapped value.
274 *
275 * @return gets a reference to the normalized value
276 */
277 public T getNormalizedValueReference()
278 {
279 if ( isNull() )
280 {
281 return null;
282 }
283
284 if ( normalizedValue == null )
285 {
286 return wrappedValue;
287 }
288
289 return normalizedValue;
290
291 }
292
293
294 /**
295 * Check if the contained value is null or not
296 *
297 * @return <code>true</code> if the inner value is null.
298 */
299 public final boolean isNull()
300 {
301 return wrappedValue == null;
302 }
303
304
305 /**
306 * This method is only used for serialization/deserialization
307 *
308 * @return Tells if the wrapped value and the normalized value are the same
309 */
310 /* no qualifier */ final boolean isSame()
311 {
312 return same;
313 }
314
315
316 /**
317 * Uses the syntaxChecker associated with the attributeType to check if the
318 * value is valid. Repeated calls to this method do not attempt to re-check
319 * the syntax of the wrapped value every time if the wrapped value does not
320 * change. Syntax checks only result on the first check, and when the wrapped
321 * value changes.
322 *
323 * @see Value#isValid()
324 */
325 public final boolean isValid()
326 {
327 if ( valid != null )
328 {
329 return valid;
330 }
331
332 if ( attributeType != null )
333 {
334 valid = attributeType.getSyntax().getSyntaxChecker().isValidSyntax( get() );
335 }
336 else
337 {
338 valid = false;
339 }
340
341 return valid;
342 }
343
344
345 /**
346 * Uses the syntaxChecker associated with the attributeType to check if the
347 * value is valid. Repeated calls to this method do not attempt to re-check
348 * the syntax of the wrapped value every time if the wrapped value does not
349 * change. Syntax checks only result on the first check, and when the wrapped
350 * value changes.
351 *
352 * @see ServerValue#isValid()
353 */
354 public final boolean isValid( SyntaxChecker syntaxChecker ) throws LdapException
355 {
356 if ( syntaxChecker == null )
357 {
358 String message = I18n.err( I18n.ERR_04139, toString() );
359 LOG.error( message );
360 throw new LdapException( message );
361 }
362
363 valid = syntaxChecker.isValidSyntax( getReference() );
364
365 return valid;
366 }
367
368
369 /**
370 * Normalize the value. In order to use this method, the Value
371 * must be schema aware.
372 *
373 * @exception LdapException If the value cannot be normalized
374 */
375 public void normalize() throws LdapException
376 {
377 normalized = true;
378 normalizedValue = wrappedValue;
379 }
380
381
382 /**
383 * Tells if the value has already be normalized or not.
384 *
385 * @return <code>true</code> if the value has already been normalized.
386 */
387 public final boolean isNormalized()
388 {
389 return normalized;
390 }
391
392
393 /**
394 * Set the normalized flag.
395 *
396 * @param the value : true or false
397 */
398 public final void setNormalized( boolean normalized )
399 {
400 this.normalized = normalized;
401 }
402 }