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.codec.search;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    
026    import org.apache.directory.shared.asn1.ber.tlv.TLV;
027    import org.apache.directory.shared.asn1.ber.tlv.Value;
028    import org.apache.directory.shared.asn1.codec.EncoderException;
029    import org.apache.directory.shared.ldap.codec.LdapConstants;
030    import org.apache.directory.shared.ldap.util.StringTools;
031    
032    
033    /**
034     * The search request filter Matching Rule assertion
035     * 
036     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
037     * @version $Rev: 798550 $, $Date: 2009-07-28 16:54:01 +0200 (Mar, 28 jul 2009) $, 
038     */
039    public class ExtensibleMatchFilter extends Filter
040    {
041        // ~ Instance fields
042        // ----------------------------------------------------------------------------
043    
044        /** The expected lenth of the Matching Rule Assertion */
045        private int expectedMatchingRuleLength;
046    
047        /** Matching rule */
048        private String matchingRule;
049        
050        /** Matching rule bytes */
051        private byte[] matchingRuleBytes;
052    
053        /** Matching rule type */
054        private String type;
055        
056        private byte[] typeBytes;
057    
058        /** Matching rule value */
059        private org.apache.directory.shared.ldap.entry.Value<?> matchValue;
060    
061        /** The dnAttributes flag */
062        private boolean dnAttributes = false;
063    
064        /** The extensible match length */
065        private int extensibleMatchLength;
066    
067        // ~ Constructors
068        // -------------------------------------------------------------------------------
069        /**
070         * Creates a new ExtensibleMatchFilter object. The dnAttributes flag
071         * defaults to false.
072         */
073        public ExtensibleMatchFilter( int tlvId )
074        {
075            super( tlvId );
076        }
077        
078        
079        /**
080         * Creates a new ExtensibleMatchFilter object. The dnAttributes flag
081         * defaults to false.
082         */
083        public ExtensibleMatchFilter()
084        {
085            super();
086        }
087    
088    
089        // ~ Methods
090        // ------------------------------------------------------------------------------------
091    
092        /**
093         * Get the dnAttributes flag
094         * 
095         * @return Returns the dnAttributes.
096         */
097        public boolean isDnAttributes()
098        {
099            return dnAttributes;
100        }
101    
102    
103        /**
104         * Set the dnAttributes flag
105         * 
106         * @param dnAttributes The dnAttributes to set.
107         */
108        public void setDnAttributes( boolean dnAttributes )
109        {
110            this.dnAttributes = dnAttributes;
111        }
112    
113    
114        /**
115         * Get the matchingRule
116         * 
117         * @return Returns the matchingRule.
118         */
119        public String getMatchingRule()
120        {
121            return matchingRule;
122        }
123    
124    
125        /**
126         * Set the matchingRule
127         * 
128         * @param matchingRule The matchingRule to set.
129         */
130        public void setMatchingRule( String matchingRule )
131        {
132            this.matchingRule = matchingRule;
133        }
134    
135    
136        /**
137         * Get the matchValue
138         * 
139         * @return Returns the matchValue.
140         */
141        public org.apache.directory.shared.ldap.entry.Value<?> getMatchValue()
142        {
143            return matchValue;
144        }
145    
146    
147        /**
148         * Set the matchValue
149         * 
150         * @param matchValue The matchValue to set.
151         */
152        public void setMatchValue( org.apache.directory.shared.ldap.entry.Value<?> matchValue )
153        {
154            this.matchValue = matchValue;
155        }
156    
157    
158        /**
159         * Get the type
160         * 
161         * @return Returns the type.
162         */
163        public String getType()
164        {
165            return type;
166        }
167    
168    
169        /**
170         * Set the type
171         * 
172         * @param type The type to set.
173         */
174        public void setType( String type )
175        {
176            this.type = type;
177        }
178    
179    
180        /**
181         * get the expectedMatchingRuleLength
182         * 
183         * @return Returns the expectedMatchingRuleLength.
184         */
185        public int getExpectedMatchingRuleLength()
186        {
187            return expectedMatchingRuleLength;
188        }
189    
190    
191        /**
192         * Set the expectedMatchingRuleLength
193         * 
194         * @param expectedMatchingRuleLength The expectedMatchingRuleLength to set.
195         */
196        public void setExpectedMatchingRuleLength( int expectedMatchingRuleLength )
197        {
198            this.expectedMatchingRuleLength = expectedMatchingRuleLength;
199        }
200    
201    
202        /**
203         * Compute the ExtensibleMatchFilter length 
204         * ExtensibleMatchFilter : 
205         * 0xA9 L1 
206         *   |
207         *  [+--> 0x81 L3 matchingRule] 
208         *  [+--> 0x82 L4 type] 
209         *  [+--> 0x83 L5 matchValue]
210         *  [+--> 0x01 0x01 dnAttributes]
211         */
212        public int computeLength()
213        {
214            if ( matchingRule != null )
215            {
216                matchingRuleBytes = StringTools.getBytesUtf8( matchingRule );
217                extensibleMatchLength = 1 + TLV.getNbBytes( matchingRuleBytes.length ) + matchingRuleBytes.length;
218            }
219    
220            if ( type != null )
221            {
222                typeBytes = StringTools.getBytesUtf8( type );
223                extensibleMatchLength += 1 + TLV.getNbBytes( typeBytes.length ) + typeBytes.length;
224            }
225    
226            if ( matchValue != null )
227            {
228                int bytesLength = matchValue.getBytes().length;
229                extensibleMatchLength += 1 + TLV.getNbBytes( bytesLength ) + bytesLength;
230            }
231    
232            if ( dnAttributes )
233            {
234                extensibleMatchLength += 1 + 1 + 1;
235            }
236    
237            return 1 + TLV.getNbBytes( extensibleMatchLength ) + extensibleMatchLength;
238        }
239    
240    
241        /**
242         * Encode the ExtensibleMatch Filters to a PDU. 
243         * 
244         * ExtensibleMatch filter :
245         * 
246         * 0xA9 LL 
247         *  |     0x81 LL matchingRule
248         *  |    / |   0x82 LL Type  
249         *  |   /  |  /0x83 LL matchValue
250         *  +--+   +-+
251         *  |   \     \
252         *  |    \     0x83 LL MatchValue
253         *  |     0x82 LL type
254         *  |     0x83 LL matchValue
255         *  +--[0x84 0x01 dnAttributes]
256         * 
257         * @param buffer The buffer where to put the PDU
258         * @return The PDU.
259         */
260        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
261        {
262            if ( buffer == null )
263            {
264                throw new EncoderException( "Cannot put a PDU in a null buffer !" );
265            }
266    
267            try
268            {
269                // The ExtensibleMatch Tag
270                buffer.put( ( byte ) LdapConstants.EXTENSIBLE_MATCH_FILTER_TAG );
271                buffer.put( TLV.getBytes( extensibleMatchLength ) );
272    
273                if ( ( matchingRule == null ) && ( type == null ) )
274                {
275                    throw new EncoderException( "Cannot have a null matching rule and a null type" );
276                }
277    
278                // The matching rule
279                if ( matchingRule != null )
280                {
281                    buffer.put( ( byte ) LdapConstants.MATCHING_RULE_ID_TAG );
282                    buffer.put( TLV.getBytes( matchingRuleBytes.length ) );
283                    buffer.put( matchingRuleBytes );
284                }
285    
286                // The type
287                if ( type != null )
288                {
289                    buffer.put( ( byte ) LdapConstants.MATCHING_RULE_TYPE_TAG );
290                    buffer.put( TLV.getBytes( typeBytes.length ) );
291                    buffer.put( typeBytes );
292                }
293    
294                // The match value
295                if ( matchValue != null )
296                {
297                    buffer.put( ( byte ) LdapConstants.MATCH_VALUE_TAG );
298    
299                    byte[] bytes = matchValue.getBytes();
300                    int bytesLength = bytes.length;
301                    buffer.put( TLV.getBytes( bytesLength ) );
302    
303                    if ( bytesLength != 0 )
304                    {
305                        buffer.put( bytes );
306                    }
307    
308                }
309    
310                // The dnAttributes flag, if true only
311                if ( dnAttributes )
312                {
313                    buffer.put( ( byte ) LdapConstants.DN_ATTRIBUTES_FILTER_TAG );
314                    buffer.put( ( byte ) 1 );
315                    buffer.put( Value.TRUE_VALUE );
316                }
317            }
318            catch ( BufferOverflowException boe )
319            {
320                throw new EncoderException( "The PDU buffer size is too small !" );
321            }
322    
323            return buffer;
324        }
325    
326    
327        /**
328         * Return a String representing an extended filter as of RFC 2254
329         * 
330         * @return An Extened Filter String
331         */
332        public String toString()
333        {
334    
335            StringBuffer sb = new StringBuffer();
336    
337            if ( type != null )
338            {
339                sb.append( type );
340            }
341    
342            if ( dnAttributes )
343            {
344                sb.append( ":dn" );
345            }
346    
347            if ( matchingRule == null )
348            {
349    
350                if ( type == null )
351                {
352                    return "Extended Filter wrong syntax";
353                }
354            }
355            else
356            {
357                sb.append( ':' ).append( matchingRule );
358            }
359    
360            sb.append( ":=" ).append( matchValue );
361    
362            return sb.toString();
363        }
364    }