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