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