View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  package org.apache.directory.api.ldap.model.entry;
21  
22  
23  import org.apache.directory.api.i18n.I18n;
24  import org.apache.directory.api.ldap.model.exception.LdapException;
25  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
26  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
27  import org.apache.directory.api.ldap.model.schema.AttributeType;
28  import org.apache.directory.api.ldap.model.schema.LdapComparator;
29  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
30  import org.apache.directory.api.ldap.model.schema.MatchingRule;
31  import org.apache.directory.api.ldap.model.schema.Normalizer;
32  import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  
37  /**
38   * A wrapper around byte[] values in entries.
39   * 
40   * @param <T> The valye type
41   * 
42   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   */
44  public abstract class AbstractValue<T> implements Value<T>
45  {
46      /** logger for reporting errors that might not be handled properly upstream */
47      protected static final Logger LOG = LoggerFactory.getLogger( AbstractValue.class );
48  
49      /** reference to the attributeType zssociated with the value */
50      protected transient AttributeType attributeType;
51  
52      /** the User Provided value */
53      protected T upValue;
54  
55      /** the canonical representation of the user provided value */
56      protected T normalizedValue;
57  
58      /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */
59      protected volatile int h;
60  
61  
62      /**
63       * {@inheritDoc}
64       */
65      @SuppressWarnings("unchecked")
66      @Override
67      public Value<T> clone()
68      {
69          try
70          {
71              return ( Value<T> ) super.clone();
72          }
73          catch ( CloneNotSupportedException cnse )
74          {
75              // Do nothing
76              return null;
77          }
78      }
79  
80  
81      /**
82       * {@inheritDoc}
83       */
84      @Override
85      public T getReference()
86      {
87          return upValue;
88      }
89  
90  
91      /**
92       * Get the wrapped value as a String.
93       *
94       * @return the wrapped value as a String
95       */
96      @Override
97      public String getString()
98      {
99          throw new UnsupportedOperationException( "Cannot call this method on a binary value" );
100     }
101 
102 
103     /**
104      * Get the wrapped value as a byte[].
105      *
106      * @return the wrapped value as a byte[]
107      */
108     @Override
109     public byte[] getBytes()
110     {
111         throw new UnsupportedOperationException( "Cannot call this method on a String value" );
112     }
113 
114 
115     /**
116      * {@inheritDoc}
117      */
118     @Override
119     public AttributeType getAttributeType()
120     {
121         return attributeType;
122     }
123 
124 
125     /**
126      * Apply an AttributeType to the current Value, normalizing it.
127      *
128      * @param attributeType The AttributeType to apply
129      * @throws LdapInvalidAttributeValueException If the value is not valid accordingly
130      * to the schema
131      */
132     @SuppressWarnings("unchecked")
133     @Override
134     public void apply( AttributeType attributeType ) throws LdapInvalidAttributeValueException
135     {
136         if ( this.attributeType != null )
137         {
138             // We already have applied an AttributeType, get out
139             LOG.warn( "AttributeType {0} already applied", attributeType.getName() );
140             return;
141         }
142         
143         if ( attributeType == null )
144         {
145             // No attributeType : the normalized value and the user provided value are the same
146             normalizedValue = upValue;
147             return;
148         }
149 
150         this.attributeType = attributeType;
151 
152         // We first have to normalize the value before we can check its syntax
153         // Get the equality matchingRule, if we have one
154         MatchingRule equality = attributeType.getEquality();
155 
156         if ( equality != null )
157         {
158             // If we have an Equality MR, we *must* have a normalizer
159             Normalizer normalizer = equality.getNormalizer();
160 
161             if ( normalizer != null )
162             {
163                 if ( upValue != null )
164                 {
165                     boolean isHR = true;
166                     // Some broken LDAP servers do not have proper syntax definitions
167                     if ( attributeType.getSyntax() != null )
168                     {
169                         isHR = attributeType.getSyntax().isHumanReadable();
170                     }
171                     
172 
173                     if ( isHR != isHumanReadable() )
174                     {
175                         
176                         String message = "The '" + attributeType.getName() + "' AttributeType and values must "
177                             + "both be String or binary";
178                         LOG.error( message );
179                         throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
180                     }
181 
182                     try
183                     {
184                         if ( isHumanReadable() )
185                         {
186                             if ( normalizedValue != null )
187                             {    
188                                 normalizedValue = ( T ) normalizer.normalize( ( String ) normalizedValue );
189                             }
190                             else
191                             {
192                                 normalizedValue = ( T ) normalizer.normalize( ( String ) upValue );
193                             }
194                         }
195                         else
196                         {
197                             normalizedValue = ( T ) normalizer.normalize( new BinaryValue( ( byte[] ) upValue ) )
198                                 .getNormReference();
199                         }
200                     }
201                     catch ( LdapException ne )
202                     {
203                         String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, ne.getLocalizedMessage() );
204                         LOG.info( message );
205                     }
206                 }
207             }
208             else
209             {
210                 String message = "The '" + attributeType.getName() + "' AttributeType does not have" + " a normalizer";
211                 LOG.error( message );
212                 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
213             }
214         }
215         else
216         {
217             // No MatchingRule, there is nothing we can do but make the normalized value
218             // to be a reference on the user provided value
219             normalizedValue = upValue;
220         }
221 
222         // and checks that the value syntax is valid
223         if ( !attributeType.isRelaxed() )
224         {
225             try
226             {
227                 LdapSyntax syntax = attributeType.getSyntax();
228     
229                 // Check the syntax if not in relaxed mode
230                 if ( ( syntax != null ) && ( !isValid( syntax.getSyntaxChecker() ) ) )
231                 {
232                     String message = I18n.err( I18n.ERR_04473_NOT_VALID_VALUE, upValue, attributeType );
233                     LOG.info( message );
234                     throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
235                 }
236             }
237             catch ( LdapException le )
238             {
239                 String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, le.getLocalizedMessage() );
240                 LOG.info( message );
241                 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message, le );
242             }
243         }
244 
245         // Rehash the Value now
246         h = 0;
247         hashCode();
248     }
249 
250 
251     /**
252      * Gets a comparator using getMatchingRule() to resolve the matching
253      * that the comparator is extracted from.
254      *
255      * @return a comparator associated with the attributeType or null if one cannot be found
256      * @throws LdapException if resolution of schema entities fail
257      */
258     @SuppressWarnings("unchecked")
259     protected final LdapComparator<T> getLdapComparator() throws LdapException
260     {
261         if ( attributeType != null )
262         {
263             MatchingRule mr = attributeType.getEquality();
264 
265             if ( mr != null )
266             {
267                 return ( LdapComparator<T> ) mr.getLdapComparator();
268             }
269         }
270 
271         return null;
272     }
273 
274 
275     /**
276      * {@inheritDoc}
277      */
278     @Override
279     public boolean isInstanceOf( AttributeType attributeType )
280     {
281         return ( attributeType != null )
282             && ( this.attributeType.equals( attributeType ) || this.attributeType.isDescendantOf( attributeType ) );
283     }
284 
285 
286     /**
287      * {@inheritDoc}
288      */
289     @Override
290     public T getNormReference()
291     {
292         if ( isNull() )
293         {
294             return null;
295         }
296 
297         if ( normalizedValue == null )
298         {
299             return upValue;
300         }
301 
302         return normalizedValue;
303     }
304 
305 
306     /**
307      * {@inheritDoc}
308      */
309     @Override
310     public final boolean isNull()
311     {
312         return upValue == null;
313     }
314 
315 
316     /**
317      * {@inheritDoc}
318      */
319     @Override
320     public final boolean isValid( SyntaxChecker syntaxChecker ) throws LdapInvalidAttributeValueException
321     {
322         if ( syntaxChecker == null )
323         {
324             String message = I18n.err( I18n.ERR_04139, toString() );
325             LOG.error( message );
326             throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
327         }
328 
329         if ( ( attributeType != null ) && attributeType.isRelaxed() ) 
330         {
331             return true;
332         }
333         else
334         { 
335             return syntaxChecker.isValidSyntax( normalizedValue );
336         }
337     }
338 
339 
340     /**
341      * {@inheritDoc}
342      */
343     @Override
344     public final boolean isSchemaAware()
345     {
346         return attributeType != null;
347     }
348 }