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   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
41   */
42  public abstract class AbstractValue<T> implements Value<T>
43  {
44      /** logger for reporting errors that might not be handled properly upstream */
45      protected static final Logger LOG = LoggerFactory.getLogger( AbstractValue.class );
46  
47      /** reference to the attributeType zssociated with the value */
48      protected transient AttributeType attributeType;
49  
50      /** the wrapped binary value */
51      protected T wrappedValue;
52  
53      /** the canonical representation of the wrapped value */
54      protected T normalizedValue;
55  
56      /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */
57      protected volatile int h;
58  
59  
60      /**
61       * {@inheritDoc}
62       */
63      @SuppressWarnings("unchecked")
64      public Value<T> clone()
65      {
66          try
67          {
68              return ( Value<T> ) super.clone();
69          }
70          catch ( CloneNotSupportedException cnse )
71          {
72              // Do nothing
73              return null;
74          }
75      }
76  
77  
78      /**
79       * {@inheritDoc}
80       */
81      public T getReference()
82      {
83          return wrappedValue;
84      }
85  
86  
87      /**
88       * Get the wrapped value as a String.
89       *
90       * @return the wrapped value as a String
91       */
92      public String getString()
93      {
94          throw new UnsupportedOperationException( "Cannot call this method on a binary value" );
95      }
96  
97  
98      /**
99       * Get the wrapped value as a byte[].
100      *
101      * @return the wrapped value as a byte[]
102      */
103     public byte[] getBytes()
104     {
105         throw new UnsupportedOperationException( "Cannot call this method on a String value" );
106     }
107 
108 
109     /**
110      * {@inheritDoc}
111      */
112     public AttributeType getAttributeType()
113     {
114         return attributeType;
115     }
116 
117 
118     /**
119      * Apply an AttributeType to the current Value, normalizing it.
120      *
121      * @param attributeType The AttributeType to apply
122      * @throws LdapInvalidAttributeValueException If the value is not valid accordingly
123      * to the schema
124      */
125     @SuppressWarnings("unchecked")
126     protected void apply( AttributeType attributeType ) throws LdapInvalidAttributeValueException
127     {
128         if ( attributeType == null )
129         {
130             // No attributeType : the normalized value and the user provided value are the same
131             normalizedValue = wrappedValue;
132             return;
133         }
134 
135         this.attributeType = attributeType;
136 
137         // We first have to normalize the value before we can check its syntax
138         // Get the equality matchingRule, if we have one
139         MatchingRule equality = attributeType.getEquality();
140 
141         if ( equality != null )
142         {
143             // If we have an Equality MR, we *must* have a normalizer
144             Normalizer normalizer = equality.getNormalizer();
145 
146             if ( normalizer != null )
147             {
148                 if ( wrappedValue != null )
149                 {
150                     boolean isHR = attributeType.getSyntax().isHumanReadable();
151 
152                     if ( isHR != isHumanReadable() )
153                     {
154                         String message = "The '" + attributeType.getName() + "' AttributeType and values must " +
155                             "both be String or binary";
156                         LOG.error( message );
157                         throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
158                     }
159 
160                     try
161                     {
162                         if ( isHumanReadable() )
163                         {
164                             normalizedValue = ( T ) normalizer.normalize( ( String ) wrappedValue );
165                         }
166                         else
167                         {
168                             normalizedValue = ( T ) normalizer.normalize( new BinaryValue( ( byte[] ) wrappedValue ) )
169                                 .getNormReference();
170                         }
171                     }
172                     catch ( LdapException ne )
173                     {
174                         String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, ne.getLocalizedMessage() );
175                         LOG.info( message );
176                     }
177                 }
178             }
179             else
180             {
181                 String message = "The '" + attributeType.getName() + "' AttributeType does not have" +
182                     " a normalizer";
183                 LOG.error( message );
184                 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
185             }
186         }
187         else
188         {
189             // No MatchingRule, there is nothing we can do but make the normalized value
190             // to be a reference on the user provided value
191             normalizedValue = wrappedValue;
192         }
193 
194         // and checks that the value syntax is valid
195         try
196         {
197             LdapSyntax syntax = attributeType.getSyntax();
198 
199             // Check the syntax
200             if ( ( syntax != null ) && ( !isValid( syntax.getSyntaxChecker() ) ) )
201             {
202                 String message = I18n.err( I18n.ERR_04473_NOT_VALID_VALUE, wrappedValue, attributeType );
203                 LOG.info( message );
204                 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
205             }
206         }
207         catch ( LdapException le )
208         {
209             String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, le.getLocalizedMessage() );
210             LOG.info( message );
211             throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message, le );
212         }
213 
214         // Rehash the Value now
215         h = 0;
216         hashCode();
217     }
218 
219 
220     /**
221      * Gets a comparator using getMatchingRule() to resolve the matching
222      * that the comparator is extracted from.
223      *
224      * @return a comparator associated with the attributeType or null if one cannot be found
225      * @throws LdapException if resolution of schema entities fail
226      */
227     @SuppressWarnings("unchecked")
228     protected final LdapComparator<T> getLdapComparator() throws LdapException
229     {
230         if ( attributeType != null )
231         {
232             MatchingRule mr = attributeType.getEquality();
233 
234             if ( mr != null )
235             {
236                 return ( LdapComparator<T> ) mr.getLdapComparator();
237             }
238         }
239 
240         return null;
241     }
242 
243 
244     /**
245      * {@inheritDoc}
246      */
247     public boolean isInstanceOf( AttributeType attributeType )
248     {
249         return ( attributeType != null ) &&
250             ( this.attributeType.equals( attributeType ) ||
251             this.attributeType.isDescendantOf( attributeType ) );
252     }
253 
254 
255     /**
256      * {@inheritDoc}
257      */
258     public T getNormReference()
259     {
260         if ( isNull() )
261         {
262             return null;
263         }
264 
265         if ( normalizedValue == null )
266         {
267             return wrappedValue;
268         }
269 
270         return normalizedValue;
271     }
272 
273 
274     /**
275      * {@inheritDoc}
276      */
277     public final boolean isNull()
278     {
279         return wrappedValue == null;
280     }
281 
282 
283     /**
284      * {@inheritDoc}
285      */
286     public final boolean isValid( SyntaxChecker syntaxChecker ) throws LdapInvalidAttributeValueException
287     {
288         if ( syntaxChecker == null )
289         {
290             String message = I18n.err( I18n.ERR_04139, toString() );
291             LOG.error( message );
292             throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message );
293         }
294 
295         return syntaxChecker.isValidSyntax( normalizedValue );
296     }
297 
298 
299     /**
300      * {@inheritDoc}
301      */
302     public final boolean isSchemaAware()
303     {
304         return attributeType != null;
305     }
306 }