View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    https://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  package org.apache.directory.api.ldap.model.message;
21  
22  
23  import java.text.ParseException;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.List;
28  
29  import org.apache.directory.api.i18n.I18n;
30  import org.apache.directory.api.ldap.model.exception.LdapException;
31  import org.apache.directory.api.ldap.model.exception.LdapInvalidSearchFilterException;
32  import org.apache.directory.api.ldap.model.filter.BranchNormalizedVisitor;
33  import org.apache.directory.api.ldap.model.filter.ExprNode;
34  import org.apache.directory.api.ldap.model.filter.FilterParser;
35  import org.apache.directory.api.ldap.model.name.Dn;
36  
37  
38  /**
39   * SearchRequest implementation.
40   * 
41   * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a>
42   */
43  public class SearchRequestImpl extends AbstractAbandonableRequest implements SearchRequest
44  {
45      static final long serialVersionUID = -5655881944020886218L;
46  
47      /** Search base distinguished name */
48      private Dn baseDn;
49  
50      /** Search filter expression tree's root node */
51      private ExprNode filterNode;
52  
53      /** Search scope enumeration value */
54      private SearchScope scope;
55  
56      /** Types only return flag */
57      private boolean typesOnly;
58  
59      /** Max size in entries to return */
60      private long sizeLimit;
61  
62      /** Max seconds to wait for search to complete */
63      private int timeLimit;
64  
65      /** Alias dereferencing mode enumeration value (default to DEREF_ALWAYS) */
66      private AliasDerefMode aliasDerefMode = AliasDerefMode.DEREF_ALWAYS;
67  
68      /** Attributes to return */
69      private List<String> attributes = new ArrayList<>();
70  
71      /** The final result containing SearchResponseDone response */
72      private SearchResultDone response;
73  
74      /** A flag set to tell the search what to do wth referrals */
75      private ReferralsPolicyEnum referralHandling = ReferralsPolicyEnum.THROW;
76  
77  
78      // -----------------------------------------------------------------------
79      // Constructors
80      // -----------------------------------------------------------------------
81      /**
82       * Creates a SearcRequest implementing object used to search the
83       * DIT.
84       */
85      public SearchRequestImpl()
86      {
87          super( -1, MessageTypeEnum.SEARCH_REQUEST );
88      }
89  
90  
91      // ------------------------------------------------------------------------
92      // SearchRequest Interface Method Implementations
93      // ------------------------------------------------------------------------
94  
95      /**
96       * {@inheritDoc}
97       */
98      @Override
99      public List<String> getAttributes()
100     {
101         return Collections.unmodifiableList( attributes );
102     }
103 
104 
105     /**
106      * Gets the search base as a distinguished name.
107      * 
108      * @return the search base
109      */
110     @Override
111     public Dn getBase()
112     {
113         return baseDn;
114     }
115 
116 
117     /**
118      * {@inheritDoc}
119      */
120     @Override
121     public SearchRequest setBase( Dn base )
122     {
123         baseDn = base;
124 
125         return this;
126     }
127 
128 
129     /**
130      * {@inheritDoc}
131      */
132     @Override
133     public AliasDerefMode getDerefAliases()
134     {
135         return aliasDerefMode;
136     }
137 
138 
139     /**
140      * {@inheritDoc}
141      */
142     @Override
143     public SearchRequest setDerefAliases( AliasDerefMode aliasDerefAliases )
144     {
145         this.aliasDerefMode = aliasDerefAliases;
146 
147         return this;
148     }
149 
150 
151     /**
152      * {@inheritDoc}
153      */
154     @Override
155     public ExprNode getFilter()
156     {
157         return filterNode;
158     }
159 
160 
161     /**
162      * {@inheritDoc}
163      */
164     @Override
165     public SearchRequest setFilter( ExprNode filter )
166     {
167         this.filterNode = filter;
168         return this;
169     }
170 
171 
172     /**
173      * {@inheritDoc}
174      */
175     @Override
176     public SearchRequest setFilter( String filter ) throws LdapException
177     {
178         try
179         {
180             filterNode = FilterParser.parse( filter );
181         }
182         catch ( ParseException pe )
183         {
184             throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_13508_INVALID_FILTER, filter, pe.getMessage() ) );
185         }
186 
187         return this;
188     }
189 
190 
191     /**
192      * {@inheritDoc}
193      */
194     @Override
195     public MessageTypeEnum[] getResponseTypes()
196     {
197         return RESPONSE_TYPES;
198     }
199 
200 
201     /**
202      * {@inheritDoc}
203      */
204     @Override
205     public SearchScope getScope()
206     {
207         return scope;
208     }
209 
210 
211     /**
212      * {@inheritDoc}
213      */
214     @Override
215     public SearchRequest setScope( SearchScope scope )
216     {
217         this.scope = scope;
218 
219         return this;
220     }
221 
222 
223     /**
224      * {@inheritDoc}
225      */
226     @Override
227     public long getSizeLimit()
228     {
229         return sizeLimit;
230     }
231 
232 
233     /**
234      * {@inheritDoc}
235      */
236     @Override
237     public SearchRequest setSizeLimit( long entriesMax )
238     {
239         sizeLimit = entriesMax;
240 
241         return this;
242     }
243 
244 
245     /**
246      * {@inheritDoc}
247      */
248     @Override
249     public int getTimeLimit()
250     {
251         return timeLimit;
252     }
253 
254 
255     /**
256      * {@inheritDoc}
257      */
258     @Override
259     public SearchRequest setTimeLimit( int secondsMax )
260     {
261         timeLimit = secondsMax;
262 
263         return this;
264     }
265 
266 
267     /**
268      * {@inheritDoc}
269      */
270     @Override
271     public boolean getTypesOnly()
272     {
273         return typesOnly;
274     }
275 
276 
277     /**
278      * {@inheritDoc}
279      */
280     @Override
281     public SearchRequest setTypesOnly( boolean typesOnly )
282     {
283         this.typesOnly = typesOnly;
284 
285         return this;
286     }
287 
288 
289     /**
290      * {@inheritDoc}
291      */
292     @Override
293     public SearchRequest addAttributes( String... attributesToAdd )
294     {
295         this.attributes.addAll( Arrays.asList( attributesToAdd ) );
296 
297         return this;
298     }
299 
300 
301     /**
302      * {@inheritDoc}
303      */
304     @Override
305     public SearchRequest removeAttribute( String attribute )
306     {
307         attributes.remove( attribute );
308 
309         return this;
310     }
311 
312 
313     /**
314      * {@inheritDoc}
315      */
316     @Override
317     public SearchResultDone getResultResponse()
318     {
319         if ( response == null )
320         {
321             response = new SearchResultDoneImpl( getMessageId() );
322         }
323 
324         return response;
325     }
326 
327 
328     /**
329      * {@inheritDoc}
330      */
331     @Override
332     public SearchRequest setMessageId( int messageId )
333     {
334         super.setMessageId( messageId );
335 
336         return this;
337     }
338 
339 
340     /**
341      * {@inheritDoc}
342      */
343     @Override
344     public SearchRequest addControl( Control control )
345     {
346         return ( SearchRequest ) super.addControl( control );
347     }
348 
349 
350     /**
351      * {@inheritDoc}
352      */
353     @Override
354     public SearchRequest addAllControls( Control[] controls )
355     {
356         return ( SearchRequest ) super.addAllControls( controls );
357     }
358 
359 
360     /**
361      * {@inheritDoc}
362      */
363     @Override
364     public SearchRequest removeControl( Control control )
365     {
366         return ( SearchRequest ) super.removeControl( control );
367     }
368 
369 
370     /**
371      * {@inheritDoc}
372      */
373     @Override
374     public int hashCode()
375     {
376         int hash = 37;
377 
378         if ( baseDn != null )
379         {
380             hash = hash * 17 + baseDn.hashCode();
381         }
382 
383         hash = hash * 17 + aliasDerefMode.hashCode();
384         hash = hash * 17 + scope.hashCode();
385         hash = hash * 17 + Long.valueOf( sizeLimit ).hashCode();
386         hash = hash * 17 + timeLimit;
387         hash = hash * 17 + ( typesOnly ? 0 : 1 );
388 
389         if ( attributes != null )
390         {
391             hash = hash * 17 + attributes.size();
392 
393             // Order doesn't matter, thus just add hashCode
394             for ( String attr : attributes )
395             {
396                 if ( attr != null )
397                 {
398                     hash = hash + attr.hashCode();
399                 }
400             }
401         }
402 
403         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
404         filterNode.accept( visitor );
405         hash = hash * 17 + filterNode.toString().hashCode();
406         hash = hash * 17 + super.hashCode();
407 
408         return hash;
409     }
410 
411 
412     /**
413      * Checks to see if two search requests are equal. The Lockable properties
414      * and the get/set context specific parameters are not consulted to
415      * determine equality. The filter expression tree comparison will normalize
416      * the child order of filter branch nodes then generate a string
417      * representation which is comparable. For the time being this is a very
418      * costly operation.
419      * 
420      * @param obj the object to check for equality to this SearchRequest
421      * @return true if the obj is a SearchRequest and equals this SearchRequest,
422      *         false otherwise
423      */
424     @Override
425     public boolean equals( Object obj )
426     {
427         if ( obj == this )
428         {
429             return true;
430         }
431 
432         if ( !super.equals( obj ) )
433         {
434             return false;
435         }
436 
437         SearchRequest req = ( SearchRequest ) obj;
438 
439         if ( !req.getBase().equals( baseDn ) )
440         {
441             return false;
442         }
443 
444         if ( req.getDerefAliases() != aliasDerefMode )
445         {
446             return false;
447         }
448 
449         if ( req.getScope() != scope )
450         {
451             return false;
452         }
453 
454         if ( req.getSizeLimit() != sizeLimit )
455         {
456             return false;
457         }
458 
459         if ( req.getTimeLimit() != timeLimit )
460         {
461             return false;
462         }
463 
464         if ( req.getTypesOnly() != typesOnly )
465         {
466             return false;
467         }
468 
469         if ( req.getAttributes() == null && attributes != null && !attributes.isEmpty() )
470         {
471             return false;
472         }
473 
474         if ( req.getAttributes() != null && attributes == null && !req.getAttributes().isEmpty() )
475         {
476             return false;
477         }
478 
479         if ( req.getAttributes() != null && attributes != null )
480         {
481             if ( req.getAttributes().size() != attributes.size() )
482             {
483                 return false;
484             }
485 
486             for ( String attribute : attributes )
487             {
488                 if ( !req.getAttributes().contains( attribute ) )
489                 {
490                     return false;
491                 }
492             }
493         }
494 
495         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
496         req.getFilter().accept( visitor );
497         filterNode.accept( visitor );
498 
499         String myFilterString = filterNode.toString();
500         String reqFilterString = req.getFilter().toString();
501 
502         return myFilterString.equals( reqFilterString );
503     }
504 
505 
506     /**
507      * Return a string the represent a SearchRequest
508      * {@inheritDoc}
509      */
510     @Override
511     public String toString()
512     {
513         StringBuilder sb = new StringBuilder();
514 
515         sb.append( "    SearchRequest\n" );
516         sb.append( "        baseDn : '" ).append( baseDn ).append( "'\n" );
517 
518         if ( filterNode != null )
519         {
520             sb.append( "        filter : '" );
521             sb.append( filterNode.toString() );
522             sb.append( "'\n" );
523         }
524 
525         sb.append( "        scope : " );
526 
527         switch ( scope )
528         {
529             case OBJECT:
530                 sb.append( "base object" );
531                 break;
532 
533             case ONELEVEL:
534                 sb.append( "single level" );
535                 break;
536 
537             case SUBTREE:
538                 sb.append( "whole subtree" );
539                 break;
540 
541             default:
542                 throw new IllegalArgumentException( I18n.err( I18n.ERR_13509_UNEXPECTED_SCOPE, scope ) );
543         }
544 
545         sb.append( '\n' );
546 
547         sb.append( "        typesOnly : " ).append( typesOnly ).append( '\n' );
548 
549         sb.append( "        Size Limit : " );
550 
551         if ( sizeLimit == 0L )
552         {
553             sb.append( "no limit" );
554         }
555         else
556         {
557             sb.append( sizeLimit );
558         }
559 
560         sb.append( '\n' );
561 
562         sb.append( "        Time Limit : " );
563 
564         if ( timeLimit == 0 )
565         {
566             sb.append( "no limit" );
567         }
568         else
569         {
570             sb.append( timeLimit );
571         }
572 
573         sb.append( '\n' );
574 
575         sb.append( "        Deref Aliases : " );
576 
577         switch ( aliasDerefMode )
578         {
579             case NEVER_DEREF_ALIASES:
580                 sb.append( "never Deref Aliases" );
581                 break;
582 
583             case DEREF_IN_SEARCHING:
584                 sb.append( "deref In Searching" );
585                 break;
586 
587             case DEREF_FINDING_BASE_OBJ:
588                 sb.append( "deref Finding Base Obj" );
589                 break;
590 
591             case DEREF_ALWAYS:
592                 sb.append( "deref Always" );
593                 break;
594 
595             default:
596                 throw new IllegalArgumentException( I18n.err( I18n.ERR_13517_UNEXPECTED_ALIAS_DEREF_MODE, aliasDerefMode ) );
597         }
598 
599         sb.append( '\n' );
600         sb.append( "        attributes : " );
601 
602         boolean isFirst = true;
603 
604         if ( attributes != null )
605         {
606             for ( String attribute : attributes )
607             {
608                 if ( isFirst )
609                 {
610                     isFirst = false;
611                 }
612                 else
613                 {
614                     sb.append( ", " );
615                 }
616 
617                 sb.append( '\'' ).append( attribute ).append( '\'' );
618             }
619         }
620 
621         sb.append( '\n' );
622 
623         // The controls
624         sb.append( super.toString() );
625 
626         return super.toString( sb.toString() );
627     }
628 
629 
630     /**
631      * {@inheritDoc}
632      */
633     @Override
634     public boolean isFollowReferrals()
635     {
636         return referralHandling == ReferralsPolicyEnum.FOLLOW;
637     }
638 
639 
640     /**
641      * {@inheritDoc}
642      */
643     @Override
644     public SearchRequest followReferrals()
645     {
646         referralHandling = ReferralsPolicyEnum.FOLLOW;
647 
648         return this;
649     }
650 
651 
652     /**
653      * {@inheritDoc}
654      */
655     @Override
656     public boolean isIgnoreReferrals()
657     {
658         return referralHandling == ReferralsPolicyEnum.IGNORE;
659     }
660 
661 
662     /**
663      * {@inheritDoc}
664      */
665     @Override
666     public SearchRequest ignoreReferrals()
667     {
668         referralHandling = ReferralsPolicyEnum.IGNORE;
669 
670         return this;
671     }
672 }