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    }