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