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.model.message;
021
022
023import java.text.ParseException;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Collections;
027import java.util.List;
028
029import org.apache.directory.shared.ldap.model.exception.LdapException;
030import org.apache.directory.shared.ldap.model.exception.LdapProtocolErrorException;
031import org.apache.directory.shared.ldap.model.exception.MessageException;
032import org.apache.directory.shared.ldap.model.filter.BranchNormalizedVisitor;
033import org.apache.directory.shared.ldap.model.filter.ExprNode;
034import org.apache.directory.shared.ldap.model.filter.FilterParser;
035import org.apache.directory.shared.ldap.model.name.Dn;
036
037
038/**
039 * SearchRequest implementation.
040 * 
041 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a>
042 */
043public class SearchRequestImpl extends AbstractAbandonableRequest implements SearchRequest
044{
045    static final long serialVersionUID = -5655881944020886218L;
046
047    /** Search base distinguished name */
048    private Dn baseDn;
049
050    /** Search filter expression tree's root node */
051    private ExprNode filterNode;
052
053    /** Search scope enumeration value */
054    private SearchScope scope;
055
056    /** Types only return flag */
057    private boolean typesOnly;
058
059    /** Max size in entries to return */
060    private long sizeLimit;
061
062    /** Max seconds to wait for search to complete */
063    private int timeLimit;
064
065    /** Alias dereferencing mode enumeration value (default to DEREF_ALWAYS) */
066    private AliasDerefMode aliasDerefMode = AliasDerefMode.DEREF_ALWAYS;
067
068    /** Attributes to return */
069    private List<String> attributes = new ArrayList<String>();
070
071    /** The final result containing SearchResponseDone response */
072    private SearchResultDone response;
073
074
075    // -----------------------------------------------------------------------
076    // Constructors
077    // -----------------------------------------------------------------------
078    /**
079     * Creates a SearcRequest implementing object used to search the
080     * DIT.
081     */
082    public SearchRequestImpl()
083    {
084        super( -1, MessageTypeEnum.SEARCH_REQUEST );
085    }
086
087
088    // ------------------------------------------------------------------------
089    // SearchRequest Interface Method Implementations
090    // ------------------------------------------------------------------------
091
092
093    /**
094     * {@inheritDoc}
095     */
096    public List<String> getAttributes()
097    {
098        return Collections.unmodifiableList( attributes );
099    }
100
101
102    /**
103     * Gets the search base as a distinguished name.
104     * 
105     * @return the search base
106     */
107    public Dn getBase()
108    {
109        return baseDn;
110    }
111
112
113    /**
114     * {@inheritDoc}
115     */
116    public SearchRequest setBase( Dn base )
117    {
118        baseDn = base;
119        
120        return this;
121    }
122
123
124    /**
125     * {@inheritDoc}
126     */
127    public AliasDerefMode getDerefAliases()
128    {
129        return aliasDerefMode;
130    }
131
132
133    /**
134     * {@inheritDoc}
135     */
136    public SearchRequest setDerefAliases( AliasDerefMode aliasDerefAliases )
137    {
138        this.aliasDerefMode = aliasDerefAliases;
139        
140        return this;
141    }
142
143
144    /**
145     * {@inheritDoc}
146     */
147    public ExprNode getFilter()
148    {
149        return filterNode;
150    }
151
152
153    /**
154     * {@inheritDoc}
155     */
156    public SearchRequest setFilter( ExprNode filter )
157    {
158        this.filterNode = filter;
159        return this;
160    }
161
162
163    /**
164     * {@inheritDoc}
165     */
166    public SearchRequest setFilter( String filter ) throws LdapException
167    {
168        try
169        {
170            filterNode = FilterParser.parse( filter );
171        }
172        catch ( ParseException pe )
173        {
174            String msg = "The filter" + filter + " is invalid.";
175            throw new LdapProtocolErrorException( msg, pe );
176        }
177        
178        return this;
179    }
180
181
182    /**
183     * {@inheritDoc}
184     */
185    public MessageTypeEnum[] getResponseTypes()
186    {
187        return RESPONSE_TYPES.clone();
188    }
189
190
191    /**
192     * {@inheritDoc}
193     */
194    public SearchScope getScope()
195    {
196        return scope;
197    }
198
199
200    /**
201     * {@inheritDoc}
202     */
203    public SearchRequest setScope( SearchScope scope )
204    {
205        this.scope = scope;
206        
207        return this;
208    }
209
210
211    /**
212     * {@inheritDoc}
213     */
214    public long getSizeLimit()
215    {
216        return sizeLimit;
217    }
218
219
220    /**
221     * {@inheritDoc}
222     */
223    public SearchRequest setSizeLimit( long entriesMax )
224    {
225        sizeLimit = entriesMax;
226        
227        return this;
228    }
229
230
231    /**
232     * {@inheritDoc}
233     */
234    public int getTimeLimit()
235    {
236        return timeLimit;
237    }
238
239
240    /**
241     * {@inheritDoc}
242     */
243    public SearchRequest setTimeLimit( int secondsMax )
244    {
245        timeLimit = secondsMax;
246        
247        return this;
248    }
249
250
251    /**
252     * {@inheritDoc}
253     */
254    public boolean getTypesOnly()
255    {
256        return typesOnly;
257    }
258
259
260    /**
261     * {@inheritDoc}
262     */
263    public SearchRequest setTypesOnly( boolean typesOnly )
264    {
265        this.typesOnly = typesOnly;
266        
267        return this;
268    }
269
270
271    /**
272     * {@inheritDoc}
273     */
274    public SearchRequest addAttributes( String... attributesToAdd )
275    {
276        this.attributes.addAll( Arrays.asList( attributesToAdd ) );
277        
278        return this;
279    }
280
281
282    /**
283     * {@inheritDoc}
284     */
285    public SearchRequest removeAttribute( String attribute )
286    {
287        attributes.remove( attribute );
288        
289        return this;
290    }
291
292
293    /**
294     * {@inheritDoc}
295     */
296    public SearchResultDone getResultResponse()
297    {
298        if ( response == null )
299        {
300            response = new SearchResultDoneImpl( getMessageId() );
301        }
302
303        return response;
304    }
305    
306    
307    /**
308     * {@inheritDoc}
309     */
310    public SearchRequest setMessageId( int messageId )
311    {
312        super.setMessageId( messageId );
313        
314        return this;
315    }
316
317    
318    /**
319     * {@inheritDoc}
320     */
321    public SearchRequest addControl( Control control ) throws MessageException
322    {
323        return (SearchRequest)super.addControl( control );
324    }
325    
326    
327    /**
328     * {@inheritDoc}
329     */
330    public SearchRequest addAllControls( Control[] controls ) throws MessageException
331    {
332        return (SearchRequest)super.addAllControls( controls );
333    }
334    
335    
336    /**
337     * {@inheritDoc}
338     */
339    public SearchRequest removeControl( Control control ) throws MessageException
340    {
341        return (SearchRequest)super.removeControl( control );
342    }
343
344
345    /**
346     * {@inheritDoc}
347     */
348    @Override
349    public int hashCode()
350    {
351        int hash = 37;
352
353        if ( baseDn != null )
354        {
355            hash = hash * 17 + baseDn.hashCode();
356        }
357
358        hash = hash * 17 + aliasDerefMode.hashCode();
359        hash = hash * 17 + scope.hashCode();
360        hash = hash * 17 + Long.valueOf( sizeLimit ).hashCode();
361        hash = hash * 17 + timeLimit;
362        hash = hash * 17 + ( typesOnly ? 0 : 1 );
363
364        if ( attributes != null )
365        {
366            hash = hash * 17 + attributes.size();
367
368            // Order doesn't matter, thus just add hashCode
369            for ( String attr : attributes )
370            {
371                hash = hash + attr.hashCode();
372            }
373        }
374
375        BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
376        filterNode.accept( visitor );
377        hash = hash * 17 + filterNode.toString().hashCode();
378        hash = hash * 17 + super.hashCode();
379
380        return hash;
381    }
382
383
384    /**
385     * Checks to see if two search requests are equal. The Lockable properties
386     * and the get/set context specific parameters are not consulted to
387     * determine equality. The filter expression tree comparison will normalize
388     * the child order of filter branch nodes then generate a string
389     * representation which is comparable. For the time being this is a very
390     * costly operation.
391     * 
392     * @param obj the object to check for equality to this SearchRequest
393     * @return true if the obj is a SearchRequest and equals this SearchRequest,
394     *         false otherwise
395     */
396    public boolean equals( Object obj )
397    {
398        if ( obj == this )
399        {
400            return true;
401        }
402
403        if ( ! super.equals( obj ) )
404        {
405            return false;
406        }
407
408        SearchRequest req = ( SearchRequest ) obj;
409
410        if ( !req.getBase().equals( baseDn ) )
411        {
412            return false;
413        }
414
415        if ( req.getDerefAliases() != aliasDerefMode )
416        {
417            return false;
418        }
419
420        if ( req.getScope() != scope )
421        {
422            return false;
423        }
424
425        if ( req.getSizeLimit() != sizeLimit )
426        {
427            return false;
428        }
429
430        if ( req.getTimeLimit() != timeLimit )
431        {
432            return false;
433        }
434
435        if ( req.getTypesOnly() != typesOnly )
436        {
437            return false;
438        }
439
440        if ( req.getAttributes() == null && attributes != null && attributes.size() > 0 )
441        {
442            return false;
443        }
444
445        if ( req.getAttributes() != null && attributes == null && req.getAttributes().size() > 0 )
446        {
447            return false;
448        }
449
450        if ( req.getAttributes() != null && attributes != null )
451        {
452            if ( req.getAttributes().size() != attributes.size() )
453            {
454                return false;
455            }
456
457            for ( String attribute : attributes )
458            {
459                if ( ! req.getAttributes().contains( attribute ) )
460                {
461                    return false;
462                }
463            }
464        }
465
466        BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
467        req.getFilter().accept( visitor );
468        filterNode.accept( visitor );
469
470        String myFilterString = filterNode.toString();
471        String reqFilterString = req.getFilter().toString();
472
473        return myFilterString.equals( reqFilterString );
474    }
475
476
477    /**
478     * Return a string the represent a SearchRequest
479     * {@inheritDoc}
480     */
481    public String toString()
482    {
483        StringBuilder sb = new StringBuilder();
484
485        sb.append( "    SearchRequest\n" );
486        sb.append( "        baseDn : '" ).append( baseDn ).append( "'\n" );
487
488        if ( filterNode != null )
489        {
490            sb.append( "        filter : '" );
491            sb.append( filterNode.toString() );
492            sb.append( "'\n" );
493        }
494
495        sb.append( "        scope : " );
496
497        switch ( scope )
498        {
499            case OBJECT:
500                sb.append( "base object" );
501                break;
502
503            case ONELEVEL:
504                sb.append( "single level" );
505                break;
506
507            case SUBTREE:
508                sb.append( "whole subtree" );
509                break;
510        }
511
512        sb.append( '\n' );
513
514        sb.append( "        typesOnly : " ).append( typesOnly ).append( '\n' );
515
516        sb.append( "        Size Limit : " );
517
518        if ( sizeLimit == 0L )
519        {
520            sb.append( "no limit" );
521        }
522        else
523        {
524            sb.append( sizeLimit );
525        }
526
527        sb.append( '\n' );
528
529        sb.append( "        Time Limit : " );
530
531        if ( timeLimit == 0 )
532        {
533            sb.append( "no limit" );
534        }
535        else
536        {
537            sb.append( timeLimit );
538        }
539
540        sb.append( '\n' );
541
542        sb.append( "        Deref Aliases : " );
543
544        switch ( aliasDerefMode )
545        {
546            case NEVER_DEREF_ALIASES:
547                sb.append( "never Deref Aliases" );
548                break;
549
550            case DEREF_IN_SEARCHING:
551                sb.append( "deref In Searching" );
552                break;
553
554            case DEREF_FINDING_BASE_OBJ:
555                sb.append( "deref Finding Base Obj" );
556                break;
557
558            case DEREF_ALWAYS:
559                sb.append( "deref Always" );
560                break;
561        }
562
563        sb.append( '\n' );
564        sb.append( "        attributes : " );
565
566        boolean isFirst = true;
567
568        if ( attributes != null )
569        {
570            for ( String attribute : attributes )
571            {
572                if ( isFirst )
573                {
574                    isFirst = false;
575                }
576                else
577                {
578                    sb.append(", ");
579                }
580
581                sb.append('\'').append( attribute ).append('\'');
582            }
583        }
584
585        sb.append( '\n' );
586
587        // The controls
588        sb.append( super.toString() );
589
590        return super.toString( sb.toString() );
591    }
592}