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     */
020    package org.apache.directory.shared.ldap.message;
021    
022    
023    import java.util.ArrayList;
024    import java.util.Collections;
025    import java.util.Iterator;
026    import java.util.List;
027    
028    import org.apache.directory.shared.ldap.codec.LdapConstants;
029    import org.apache.directory.shared.ldap.filter.BranchNormalizedVisitor;
030    import org.apache.directory.shared.ldap.filter.ExprNode;
031    import org.apache.directory.shared.ldap.filter.SearchScope;
032    import org.apache.directory.shared.ldap.name.LdapDN;
033    
034    
035    /**
036     * Lockable SearchRequest implementation.
037     * 
038     * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a>
039     * @version $Rev: 764131 $
040     */
041    public class SearchRequestImpl extends AbstractAbandonableRequest implements InternalSearchRequest
042    {
043        static final long serialVersionUID = -5655881944020886218L;
044    
045        /** Search base distinguished name */
046        private LdapDN baseDn;
047    
048        /** Search filter expression tree's root node */
049        private ExprNode filter;
050    
051        /** Search scope enumeration value */
052        private SearchScope scope;
053    
054        /** Types only return flag */
055        private boolean typesOnly;
056    
057        /** Max size in entries to return */
058        private int sizeLimit;
059    
060        /** Max seconds to wait for search to complete */
061        private int timeLimit;
062    
063        /** Alias dereferencing mode enumeration value */
064        private AliasDerefMode aliasDerefMode;
065    
066        /** Attributes to return */
067        private List<String> attributes = new ArrayList<String>();
068    
069        /** The final result containing SearchResponseDone response */
070        private InternalSearchResponseDone response;
071    
072    
073        // ------------------------------------------------------------------------
074        // Constructors
075        // ------------------------------------------------------------------------
076    
077        /**
078         * Creates a Lockable SearcRequest implementing object used to search the
079         * DIT.
080         * 
081         * @param id
082         *            the sequential message identifier
083         */
084        public SearchRequestImpl(final int id)
085        {
086            super( id, MessageTypeEnum.SEARCH_REQUEST );
087        }
088    
089    
090        // ------------------------------------------------------------------------
091        // SearchRequest Interface Method Implementations
092        // ------------------------------------------------------------------------
093    
094        /**
095         * Gets a list of the attributes to be returned from each entry which
096         * matches the search filter. There are two special values which may be
097         * used: an empty list with no attributes, and the attribute description
098         * string "*". Both of these signify that all user attributes are to be
099         * returned. (The "*" allows the client to request all user attributes in
100         * addition to specific operational attributes). Attributes MUST be named at
101         * most once in the list, and are returned at most once in an entry. If
102         * there are attribute descriptions in the list which are not recognized,
103         * they are ignored by the server. If the client does not want any
104         * attributes returned, it can specify a list containing only the attribute
105         * with OID "1.1". This OID was chosen arbitrarily and does not correspond
106         * to any attribute in use. Client implementors should note that even if all
107         * user attributes are requested, some attributes of the entry may not be
108         * included in search results due to access control or other restrictions.
109         * Furthermore, servers will not return operational attributes, such as
110         * objectClasses or attributeTypes, unless they are listed by name, since
111         * there may be extremely large number of values for certain operational
112         * attributes.
113         * 
114         * @return the collection of attributes to return for each entry
115         */
116        public List<String> getAttributes()
117        {
118            return Collections.unmodifiableList( attributes );
119        }
120    
121    
122        /**
123         * Gets the search base as a distinguished name.
124         * 
125         * @return the search base
126         */
127        public LdapDN getBase()
128        {
129            return baseDn;
130        }
131    
132    
133        /**
134         * Sets the search base as a distinguished name.
135         * 
136         * @param base
137         *            the search base
138         */
139        public void setBase( LdapDN base )
140        {
141            baseDn = base;
142        }
143    
144    
145        /**
146         * Gets the alias handling parameter.
147         * 
148         * @return the alias handling parameter enumeration.
149         */
150        public AliasDerefMode getDerefAliases()
151        {
152            return aliasDerefMode;
153        }
154    
155    
156        /**
157         * Sets the alias handling parameter.
158         * 
159         * @param aliasDerefAliases
160         *            the alias handling parameter enumeration.
161         */
162        public void setDerefAliases( AliasDerefMode aliasDerefAliases )
163        {
164            this.aliasDerefMode = aliasDerefAliases;
165        }
166    
167    
168        /**
169         * Gets the search filter associated with this search request.
170         * 
171         * @return the expression node for the root of the filter expression tree.
172         */
173        public ExprNode getFilter()
174        {
175            return filter;
176        }
177    
178    
179        /**
180         * Sets the search filter associated with this search request.
181         * 
182         * @param filter
183         *            the expression node for the root of the filter expression
184         *            tree.
185         */
186        public void setFilter( ExprNode filter )
187        {
188            this.filter = filter;
189        }
190    
191    
192        /**
193         * Gets the different response types generated by a search request.
194         * 
195         * @return the RESPONSE_TYPES array
196         * @see #RESPONSE_TYPES
197         */
198        public MessageTypeEnum[] getResponseTypes()
199        {
200            return RESPONSE_TYPES.clone();
201        }
202    
203    
204        /**
205         * Gets the search scope parameter enumeration.
206         * 
207         * @return the scope enumeration parameter.
208         */
209        public SearchScope getScope()
210        {
211            return scope;
212        }
213    
214    
215        /**
216         * Sets the search scope parameter enumeration.
217         * 
218         * @param scope
219         *            the scope enumeration parameter.
220         */
221        public void setScope( SearchScope scope )
222        {
223            this.scope = scope;
224        }
225    
226    
227        /**
228         * A sizelimit that restricts the maximum number of entries to be returned
229         * as a result of the search. A value of 0 in this field indicates that no
230         * client-requested sizelimit restrictions are in effect for the search.
231         * Servers may enforce a maximum number of entries to return.
232         * 
233         * @return search size limit.
234         */
235        public int getSizeLimit()
236        {
237            return sizeLimit;
238        }
239    
240    
241        /**
242         * Sets sizelimit that restricts the maximum number of entries to be
243         * returned as a result of the search. A value of 0 in this field indicates
244         * that no client-requested sizelimit restrictions are in effect for the
245         * search. Servers may enforce a maximum number of entries to return.
246         * 
247         * @param entriesMax
248         *            maximum search result entries to return.
249         */
250        public void setSizeLimit( int entriesMax )
251        {
252            sizeLimit = entriesMax;
253        }
254    
255    
256        /**
257         * Gets the timelimit that restricts the maximum time (in seconds) allowed
258         * for a search. A value of 0 in this field indicates that no client-
259         * requested timelimit restrictions are in effect for the search.
260         * 
261         * @return the search time limit in seconds.
262         */
263        public int getTimeLimit()
264        {
265            return timeLimit;
266        }
267    
268    
269        /**
270         * Sets the timelimit that restricts the maximum time (in seconds) allowed
271         * for a search. A value of 0 in this field indicates that no client-
272         * requested timelimit restrictions are in effect for the search.
273         * 
274         * @param secondsMax
275         *            the search time limit in seconds.
276         */
277        public void setTimeLimit( int secondsMax )
278        {
279            timeLimit = secondsMax;
280        }
281    
282    
283        /**
284         * An indicator as to whether search results will contain both attribute
285         * types and values, or just attribute types. Setting this field to TRUE
286         * causes only attribute types (no values) to be returned. Setting this
287         * field to FALSE causes both attribute types and values to be returned.
288         * 
289         * @return true for only types, false for types and values.
290         */
291        public boolean getTypesOnly()
292        {
293            return typesOnly;
294        }
295    
296    
297        /**
298         * An indicator as to whether search results will contain both attribute
299         * types and values, or just attribute types. Setting this field to TRUE
300         * causes only attribute types (no values) to be returned. Setting this
301         * field to FALSE causes both attribute types and values to be returned.
302         * 
303         * @param typesOnly
304         *            true for only types, false for types and values.
305         */
306        public void setTypesOnly( boolean typesOnly )
307        {
308            this.typesOnly = typesOnly;
309        }
310    
311    
312        /**
313         * Adds an attribute to the set of entry attributes to return.
314         * 
315         * @param attribute
316         *            the attribute description or identifier.
317         */
318        public void addAttribute( String attribute )
319        {
320            attributes.add( attribute );
321        }
322    
323    
324        /**
325         * Removes an attribute to the set of entry attributes to return.
326         * 
327         * @param attribute
328         *            the attribute description or identifier.
329         */
330        public void removeAttribute( String attribute )
331        {
332            attributes.remove( attribute );
333        }
334    
335    
336        /**
337         * The result containing response for this request.
338         * 
339         * @return the result containing response for this request
340         */
341        public InternalResultResponse getResultResponse()
342        {
343            if ( response == null )
344            {
345                response = new SearchResponseDoneImpl( getMessageId() );
346            }
347    
348            return response;
349        }
350    
351    
352        /**
353         * Checks to see if two search requests are equal. The Lockable properties
354         * and the get/set context specific parameters are not consulted to
355         * determine equality. The filter expression tree comparison will normalize
356         * the child order of filter branch nodes then generate a string
357         * representation which is comparable. For the time being this is a very
358         * costly operation.
359         * 
360         * @param obj
361         *            the object to check for equality to this SearchRequest
362         * @return true if the obj is a SearchRequest and equals this SearchRequest,
363         *         false otherwise
364         */
365        public boolean equals( Object obj )
366        {
367            if ( obj == this )
368            {
369                return true;
370            }
371    
372            if ( !super.equals( obj ) )
373            {
374                return false;
375            }
376    
377            InternalSearchRequest req = ( InternalSearchRequest ) obj;
378    
379            if ( !req.getBase().equals( baseDn ) )
380            {
381                return false;
382            }
383    
384            if ( req.getDerefAliases() != aliasDerefMode )
385            {
386                return false;
387            }
388    
389            if ( req.getScope() != scope )
390            {
391                return false;
392            }
393    
394            if ( req.getSizeLimit() != sizeLimit )
395            {
396                return false;
397            }
398    
399            if ( req.getTimeLimit() != timeLimit )
400            {
401                return false;
402            }
403    
404            if ( req.getTypesOnly() != typesOnly )
405            {
406                return false;
407            }
408    
409            if ( req.getAttributes() == null && attributes != null )
410            {
411                if ( attributes.size() > 0 )
412                {
413                    return false;
414                }
415            }
416    
417            if ( req.getAttributes() != null && attributes == null )
418            {
419                if ( req.getAttributes().size() > 0 )
420                {
421                    return false;
422                }
423            }
424    
425            if ( req.getAttributes() != null && attributes != null )
426            {
427                if ( req.getAttributes().size() != attributes.size() )
428                {
429                    return false;
430                }
431    
432                Iterator<String> list = attributes.iterator();
433                
434                while ( list.hasNext() )
435                {
436                    if ( !req.getAttributes().contains( list.next() ) )
437                    {
438                        return false;
439                    }
440                }
441            }
442    
443            BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
444            req.getFilter().accept( visitor );
445            filter.accept( visitor );
446    
447            String myFilterString = filter.toString();
448            String reqFilterString = req.getFilter().toString();
449    
450            return myFilterString.equals( reqFilterString );
451        }
452    
453        /**
454         * Return a string the represent a SearchRequest
455         */
456        public String toString()
457        {
458            StringBuilder    sb = new StringBuilder();
459    
460            sb.append( "    SearchRequest\n" );
461            sb.append( "        baseDn : '" ).append( baseDn ).append( "'\n" );
462            
463            if ( filter != null )
464            {
465                sb.append( "        filter : '" );
466                sb.append( filter.toString() );
467                sb.append( "'\n" );
468            }
469            
470            sb.append( "        scope : " );
471            
472            switch ( scope )
473            {
474                case OBJECT:
475                    sb.append( "base object" );
476                    break;
477    
478                case ONELEVEL:
479                    sb.append( "single level" );
480                    break;
481    
482                case SUBTREE:
483                    sb.append( "whole subtree" );
484                    break;
485            }
486            
487            sb.append( '\n' );
488            
489            sb.append( "        typesOnly : " ).append( typesOnly ).append( '\n' );
490    
491            sb.append( "        Size Limit : " );
492    
493            if ( sizeLimit == 0 )
494            {
495                sb.append( "no limit" );
496            }
497            else
498            {
499                sb.append( sizeLimit );
500            }
501    
502            sb.append( '\n' );
503    
504            sb.append( "        Time Limit : " );
505    
506            if ( timeLimit == 0 )
507            {
508                sb.append( "no limit" );
509            }
510            else
511            {
512                sb.append( timeLimit );
513            }
514    
515            sb.append( '\n' );
516    
517            sb.append( "        Deref Aliases : " );
518    
519            switch ( aliasDerefMode.getValue() )
520            {
521                case LdapConstants.NEVER_DEREF_ALIASES:
522                    sb.append( "never Deref Aliases" );
523                    break;
524    
525                case LdapConstants.DEREF_IN_SEARCHING:
526                    sb.append( "deref In Searching" );
527                    break;
528    
529                case LdapConstants.DEREF_FINDING_BASE_OBJ:
530                    sb.append( "deref Finding Base Obj" );
531                    break;
532    
533                case LdapConstants.DEREF_ALWAYS:
534                    sb.append( "deref Always" );
535                    break;
536            }
537    
538            sb.append( '\n' );
539            sb.append( "        attributes : " );
540    
541            boolean         isFirst = true;
542    
543            if ( attributes != null )
544            {
545                Iterator<String> it = attributes.iterator();
546                
547                while ( it.hasNext() )
548                {
549                    if ( isFirst )
550                    {
551                        isFirst = false;
552                    }
553                    else
554                    {
555                        sb.append( ", " );
556                    }
557                    
558                    sb.append( '\'' ).append( it.next() ).append( '\'' );
559                }
560                
561            }
562    
563            sb.append( '\n' );
564    
565            return sb.toString();
566        }
567    }