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