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 */
020package org.apache.directory.api.ldap.codec.search;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025
026import org.apache.directory.api.asn1.EncoderException;
027import org.apache.directory.api.asn1.ber.tlv.BerValue;
028import org.apache.directory.api.asn1.ber.tlv.TLV;
029import org.apache.directory.api.i18n.I18n;
030import org.apache.directory.api.ldap.codec.api.LdapConstants;
031import org.apache.directory.api.util.Strings;
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 */
039public 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.api.ldap.model.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
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.api.ldap.model.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.api.ldap.model.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 = Strings.getBytesUtf8( matchingRule );
218            extensibleMatchLength = 1 + TLV.getNbBytes( matchingRuleBytes.length ) + matchingRuleBytes.length;
219        }
220
221        if ( type != null )
222        {
223            typeBytes = Strings.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( BerValue.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}