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