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                hash = hash + attr.hashCode();
374            }
375        }
376
377        BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
378        filterNode.accept( visitor );
379        hash = hash * 17 + filterNode.toString().hashCode();
380        hash = hash * 17 + super.hashCode();
381
382        return hash;
383    }
384
385
386    /**
387     * Checks to see if two search requests are equal. The Lockable properties
388     * and the get/set context specific parameters are not consulted to
389     * determine equality. The filter expression tree comparison will normalize
390     * the child order of filter branch nodes then generate a string
391     * representation which is comparable. For the time being this is a very
392     * costly operation.
393     * 
394     * @param obj the object to check for equality to this SearchRequest
395     * @return true if the obj is a SearchRequest and equals this SearchRequest,
396     *         false otherwise
397     */
398    public boolean equals( Object obj )
399    {
400        if ( obj == this )
401        {
402            return true;
403        }
404
405        if ( !super.equals( obj ) )
406        {
407            return false;
408        }
409
410        SearchRequest req = ( SearchRequest ) obj;
411
412        if ( !req.getBase().equals( baseDn ) )
413        {
414            return false;
415        }
416
417        if ( req.getDerefAliases() != aliasDerefMode )
418        {
419            return false;
420        }
421
422        if ( req.getScope() != scope )
423        {
424            return false;
425        }
426
427        if ( req.getSizeLimit() != sizeLimit )
428        {
429            return false;
430        }
431
432        if ( req.getTimeLimit() != timeLimit )
433        {
434            return false;
435        }
436
437        if ( req.getTypesOnly() != typesOnly )
438        {
439            return false;
440        }
441
442        if ( req.getAttributes() == null && attributes != null && attributes.size() > 0 )
443        {
444            return false;
445        }
446
447        if ( req.getAttributes() != null && attributes == null && req.getAttributes().size() > 0 )
448        {
449            return false;
450        }
451
452        if ( req.getAttributes() != null && attributes != null )
453        {
454            if ( req.getAttributes().size() != attributes.size() )
455            {
456                return false;
457            }
458
459            for ( String attribute : attributes )
460            {
461                if ( !req.getAttributes().contains( attribute ) )
462                {
463                    return false;
464                }
465            }
466        }
467
468        BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
469        req.getFilter().accept( visitor );
470        filterNode.accept( visitor );
471
472        String myFilterString = filterNode.toString();
473        String reqFilterString = req.getFilter().toString();
474
475        return myFilterString.equals( reqFilterString );
476    }
477
478
479    /**
480     * Return a string the represent a SearchRequest
481     * {@inheritDoc}
482     */
483    public String toString()
484    {
485        StringBuilder sb = new StringBuilder();
486
487        sb.append( "    SearchRequest\n" );
488        sb.append( "        baseDn : '" ).append( baseDn ).append( "'\n" );
489
490        if ( filterNode != null )
491        {
492            sb.append( "        filter : '" );
493            sb.append( filterNode.toString() );
494            sb.append( "'\n" );
495        }
496
497        sb.append( "        scope : " );
498
499        switch ( scope )
500        {
501            case OBJECT:
502                sb.append( "base object" );
503                break;
504
505            case ONELEVEL:
506                sb.append( "single level" );
507                break;
508
509            case SUBTREE:
510                sb.append( "whole subtree" );
511                break;
512        }
513
514        sb.append( '\n' );
515
516        sb.append( "        typesOnly : " ).append( typesOnly ).append( '\n' );
517
518        sb.append( "        Size Limit : " );
519
520        if ( sizeLimit == 0L )
521        {
522            sb.append( "no limit" );
523        }
524        else
525        {
526            sb.append( sizeLimit );
527        }
528
529        sb.append( '\n' );
530
531        sb.append( "        Time Limit : " );
532
533        if ( timeLimit == 0 )
534        {
535            sb.append( "no limit" );
536        }
537        else
538        {
539            sb.append( timeLimit );
540        }
541
542        sb.append( '\n' );
543
544        sb.append( "        Deref Aliases : " );
545
546        switch ( aliasDerefMode )
547        {
548            case NEVER_DEREF_ALIASES:
549                sb.append( "never Deref Aliases" );
550                break;
551
552            case DEREF_IN_SEARCHING:
553                sb.append( "deref In Searching" );
554                break;
555
556            case DEREF_FINDING_BASE_OBJ:
557                sb.append( "deref Finding Base Obj" );
558                break;
559
560            case DEREF_ALWAYS:
561                sb.append( "deref Always" );
562                break;
563        }
564
565        sb.append( '\n' );
566        sb.append( "        attributes : " );
567
568        boolean isFirst = true;
569
570        if ( attributes != null )
571        {
572            for ( String attribute : attributes )
573            {
574                if ( isFirst )
575                {
576                    isFirst = false;
577                }
578                else
579                {
580                    sb.append( ", " );
581                }
582
583                sb.append( '\'' ).append( attribute ).append( '\'' );
584            }
585        }
586
587        sb.append( '\n' );
588
589        // The controls
590        sb.append( super.toString() );
591
592        return super.toString( sb.toString() );
593    }
594    
595    
596    /**
597     * {@inheritDoc}
598     */
599    public boolean isFollowReferrals()
600    {
601        return referralHandling == ReferralsPolicyEnum.FOLLOW;
602    }
603
604
605    /**
606     * {@inheritDoc}
607     */
608    public SearchRequest followReferrals()
609    {
610        referralHandling = ReferralsPolicyEnum.FOLLOW;
611        
612        return this;
613    }
614    
615    
616    /**
617     * {@inheritDoc}
618     */
619    public boolean isIgnoreReferrals()
620    {
621        return referralHandling == ReferralsPolicyEnum.IGNORE;
622    }
623
624
625    /**
626     * {@inheritDoc}
627     */
628    public SearchRequest ignoreReferrals()
629    {
630        referralHandling = ReferralsPolicyEnum.IGNORE;
631        
632        return this;
633    }
634}