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