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.dsmlv2.request;
021
022
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.directory.api.asn1.DecoderException;
027import org.apache.directory.api.dsmlv2.ParserUtils;
028import org.apache.directory.api.ldap.codec.api.LdapApiService;
029import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
030import org.apache.directory.api.ldap.model.entry.Value;
031import org.apache.directory.api.ldap.model.exception.LdapException;
032import org.apache.directory.api.ldap.model.filter.AndNode;
033import org.apache.directory.api.ldap.model.filter.ApproximateNode;
034import org.apache.directory.api.ldap.model.filter.BranchNode;
035import org.apache.directory.api.ldap.model.filter.EqualityNode;
036import org.apache.directory.api.ldap.model.filter.ExprNode;
037import org.apache.directory.api.ldap.model.filter.ExtensibleNode;
038import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
039import org.apache.directory.api.ldap.model.filter.LeafNode;
040import org.apache.directory.api.ldap.model.filter.LessEqNode;
041import org.apache.directory.api.ldap.model.filter.NotNode;
042import org.apache.directory.api.ldap.model.filter.OrNode;
043import org.apache.directory.api.ldap.model.filter.PresenceNode;
044import org.apache.directory.api.ldap.model.filter.SimpleNode;
045import org.apache.directory.api.ldap.model.filter.SubstringNode;
046import org.apache.directory.api.ldap.model.message.AliasDerefMode;
047import org.apache.directory.api.ldap.model.message.Control;
048import org.apache.directory.api.ldap.model.message.MessageTypeEnum;
049import org.apache.directory.api.ldap.model.message.SearchRequest;
050import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
051import org.apache.directory.api.ldap.model.message.SearchResultDone;
052import org.apache.directory.api.ldap.model.message.SearchScope;
053import org.apache.directory.api.ldap.model.name.Dn;
054import org.dom4j.Element;
055import org.dom4j.Namespace;
056import org.dom4j.QName;
057
058
059/**
060 * DSML Decorator for SearchRequest
061 *
062 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
063 */
064public class SearchRequestDsml
065    extends AbstractResultResponseRequestDsml<SearchRequest, SearchResultDone>
066    implements SearchRequest
067{
068    /** Some string constants */
069    private static final String DEREF_ALIASES = "derefAliases";
070    private static final String NAME = "name";
071    private static final String VALUE = "value";
072    
073    /** A temporary storage for a terminal Filter */
074    private Filter terminalFilter;
075
076    /** The current filter. This is used while decoding a PDU */
077    private Filter currentFilter;
078
079    /** The global filter. This is used while decoding a PDU */
080    private Filter topFilter;
081
082
083    /**
084     * Creates a new getDecoratedMessage() of SearchRequestDsml.
085     * 
086     * @param codec The LDAP Service to use
087     */
088    public SearchRequestDsml( LdapApiService codec )
089    {
090        super( codec, new SearchRequestImpl() );
091    }
092
093
094    /**
095     * Creates a new getDecoratedMessage() of SearchRequestDsml.
096     *
097     * @param codec The LDAP Service to use
098     * @param ldapMessage the message to decorate
099     */
100    public SearchRequestDsml( LdapApiService codec, SearchRequest ldapMessage )
101    {
102        super( codec, ldapMessage );
103    }
104
105
106    /**
107     * Gets the search filter associated with this search request.
108     *
109     * @return the expression node for the root of the filter expression tree.
110     */
111    public Filter getCodecFilter()
112    {
113        return topFilter;
114    }
115
116
117    /**
118     * Gets the search filter associated with this search request.
119     *
120     * @return the expression node for the root of the filter expression tree.
121     */
122    public ExprNode getFilterNode()
123    {
124        return transform( topFilter );
125    }
126
127
128    /**
129     * Get the terminal filter
130     *
131     * @return Returns the terminal filter.
132     */
133    public Filter getTerminalFilter()
134    {
135        return terminalFilter;
136    }
137
138
139    /**
140     * Set the terminal filter
141     *
142     * @param terminalFilter the teminalFilter.
143     */
144    public void setTerminalFilter( Filter terminalFilter )
145    {
146        this.terminalFilter = terminalFilter;
147    }
148
149
150    /**
151     * set the currentFilter to its parent
152     */
153    public void endCurrentConnectorFilter()
154    {
155        currentFilter = currentFilter.getParent();
156    }
157
158
159    /**
160     * Add a current filter. We have two cases :
161     * <ul>
162     *   <li>there is no previous current filter : the filter
163     *     is the top level filter</li>
164     *   <li>there is a previous current filter : the filter is added
165     *     to the currentFilter set, and the current filter is changed</li>
166     * </ul>
167     * In any case, the previous current filter will always be a
168     * ConnectorFilter when this method is called.
169     *
170     * @param localFilter The filter to set.
171     * @throws DecoderException If the added filter is invalid
172     */
173    public void addCurrentFilter( Filter localFilter ) throws DecoderException
174    {
175        if ( currentFilter != null )
176        {
177            // Ok, we have a parent. The new Filter will be added to
178            // this parent, and will become the currentFilter if it's a connector.
179            ( ( ConnectorFilter ) currentFilter ).addFilter( localFilter );
180            localFilter.setParent( currentFilter );
181
182            if ( localFilter instanceof ConnectorFilter )
183            {
184                currentFilter = localFilter;
185            }
186        }
187        else
188        {
189            // No parent. This Filter will become the root.
190            currentFilter = localFilter;
191            currentFilter.setParent( null );
192            topFilter = localFilter;
193        }
194    }
195
196
197    /**
198     * Transform the Filter part of a SearchRequest to an ExprNode
199     *
200     * @param filter The filter to be transformed
201     * @return An ExprNode
202     */
203    @SuppressWarnings(
204        { "unchecked", "rawtypes" })
205    private ExprNode transform( Filter filter )
206    {
207        if ( filter != null )
208        {
209            // Transform OR, AND or NOT leaves
210            if ( filter instanceof ConnectorFilter )
211            {
212                BranchNode branch = null;
213
214                if ( filter instanceof AndFilter )
215                {
216                    branch = new AndNode();
217                }
218                else if ( filter instanceof OrFilter )
219                {
220                    branch = new OrNode();
221                }
222                else if ( filter instanceof NotFilter )
223                {
224                    branch = new NotNode();
225                }
226
227                List<Filter> filtersSet = ( ( ConnectorFilter ) filter ).getFilterSet();
228
229                // Loop on all AND/OR children
230                if ( filtersSet != null )
231                {
232                    for ( Filter node : filtersSet )
233                    {
234                        branch.addNode( transform( node ) );
235                    }
236                }
237
238                return branch;
239            }
240            else
241            {
242                // Transform PRESENT or ATTRIBUTE_VALUE_ASSERTION
243                LeafNode branch = null;
244
245                if ( filter instanceof PresentFilter )
246                {
247                    branch = new PresenceNode( ( ( PresentFilter ) filter ).getAttributeDescription() );
248                }
249                else if ( filter instanceof AttributeValueAssertionFilter )
250                {
251                    AttributeValueAssertionFilter avaFilter = ( AttributeValueAssertionFilter ) filter;
252
253                    AttributeValueAssertion ava = avaFilter.getAssertion();
254
255                    // Transform =, >=, <=, ~= filters
256                    int filterType = avaFilter.getFilterType();
257                    switch ( filterType )
258                    {
259                        case LdapCodecConstants.EQUALITY_MATCH_FILTER:
260                            branch = new EqualityNode( ava.getAttributeDesc(), ava.getAssertionValue() );
261                            break;
262
263                        case LdapCodecConstants.GREATER_OR_EQUAL_FILTER:
264                            branch = new GreaterEqNode( ava.getAttributeDesc(), ava.getAssertionValue() );
265                            break;
266
267                        case LdapCodecConstants.LESS_OR_EQUAL_FILTER:
268                            branch = new LessEqNode( ava.getAttributeDesc(), ava.getAssertionValue() );
269                            break;
270
271                        case LdapCodecConstants.APPROX_MATCH_FILTER:
272                            branch = new ApproximateNode( ava.getAttributeDesc(), ava.getAssertionValue() );
273                            break;
274
275                        default:
276                            throw new IllegalStateException( "Unexpected filter type " + filterType );
277                    }
278
279                }
280                else if ( filter instanceof SubstringFilter )
281                {
282                    // Transform Substring filters
283                    SubstringFilter substrFilter = ( SubstringFilter ) filter;
284                    String initialString = null;
285                    String finalString = null;
286                    List<String> anyString = null;
287
288                    if ( substrFilter.getInitialSubstrings() != null )
289                    {
290                        initialString = substrFilter.getInitialSubstrings();
291                    }
292
293                    if ( substrFilter.getFinalSubstrings() != null )
294                    {
295                        finalString = substrFilter.getFinalSubstrings();
296                    }
297
298                    if ( substrFilter.getAnySubstrings() != null )
299                    {
300                        anyString = new ArrayList<String>();
301
302                        for ( String any : substrFilter.getAnySubstrings() )
303                        {
304                            anyString.add( any );
305                        }
306                    }
307
308                    branch = new SubstringNode( anyString, substrFilter.getType(), initialString, finalString );
309                }
310                else if ( filter instanceof ExtensibleMatchFilter )
311                {
312                    // Transform Extensible Match Filter
313                    ExtensibleMatchFilter extFilter = ( ExtensibleMatchFilter ) filter;
314                    String matchingRule = null;
315
316                    Value<?> value = extFilter.getMatchValue();
317
318                    if ( extFilter.getMatchingRule() != null )
319                    {
320                        matchingRule = extFilter.getMatchingRule();
321                    }
322
323                    branch = new ExtensibleNode( extFilter.getType(), value, matchingRule, extFilter.isDnAttributes() );
324                }
325
326                return branch;
327            }
328        }
329        else
330        {
331            // We have found nothing to transform. Return null then.
332            return null;
333        }
334    }
335
336
337    /**
338     * {@inheritDoc}
339     */
340    public MessageTypeEnum getType()
341    {
342        return getDecorated().getType();
343    }
344
345
346    /**
347     * {@inheritDoc}
348     */
349    public Element toDsml( Element root )
350    {
351        Element element = super.toDsml( root );
352
353        SearchRequest request = getDecorated();
354
355        // Dn
356        if ( request.getBase() != null )
357        {
358            element.addAttribute( "dn", request.getBase().getName() );
359        }
360
361        // Scope
362        SearchScope scope = request.getScope();
363        if ( scope != null )
364        {
365            if ( scope == SearchScope.OBJECT )
366            {
367                element.addAttribute( "scope", "baseObject" );
368            }
369            else if ( scope == SearchScope.ONELEVEL )
370            {
371                element.addAttribute( "scope", "singleLevel" );
372            }
373            else if ( scope == SearchScope.SUBTREE )
374            {
375                element.addAttribute( "scope", "wholeSubtree" );
376            }
377        }
378
379        // DerefAliases
380        AliasDerefMode derefAliases = request.getDerefAliases();
381
382        switch ( derefAliases )
383        {
384            case NEVER_DEREF_ALIASES:
385                element.addAttribute( DEREF_ALIASES, "neverDerefAliases" );
386                break;
387
388            case DEREF_ALWAYS:
389                element.addAttribute( DEREF_ALIASES, "derefAlways" );
390                break;
391
392            case DEREF_FINDING_BASE_OBJ:
393                element.addAttribute( DEREF_ALIASES, "derefFindingBaseObj" );
394                break;
395
396            case DEREF_IN_SEARCHING:
397                element.addAttribute( DEREF_ALIASES, "derefInSearching" );
398                break;
399
400            default:
401                throw new IllegalStateException( "Unexpected deref alias mode " + derefAliases );
402        }
403
404        // SizeLimit
405        if ( request.getSizeLimit() != 0L )
406        {
407            element.addAttribute( "sizeLimit", "" + request.getSizeLimit() );
408        }
409
410        // TimeLimit
411        if ( request.getTimeLimit() != 0 )
412        {
413            element.addAttribute( "timeLimit", "" + request.getTimeLimit() );
414        }
415
416        // TypesOnly
417        if ( request.getTypesOnly() )
418        {
419            element.addAttribute( "typesOnly", "true" );
420        }
421
422        // Filter
423        Element filterElement = element.addElement( "filter" );
424        toDsml( filterElement, request.getFilter() );
425
426        // Attributes
427        List<String> attributes = request.getAttributes();
428
429        if ( attributes.size() > 0 )
430        {
431            Element attributesElement = element.addElement( "attributes" );
432
433            for ( String entryAttribute : attributes )
434            {
435                attributesElement.addElement( "attribute" ).addAttribute( NAME, entryAttribute );
436            }
437        }
438
439        return element;
440    }
441
442
443    /**
444     * Recursively converts the filter of the Search Request into a DSML representation and adds 
445     * it to the XML Element corresponding to the Search Request
446     *
447     * @param element
448     *      the parent Element
449     * @param filter
450     *      the filter to convert
451     */
452    private void toDsml( Element element, ExprNode filter )
453    {
454        // AND FILTER
455        if ( filter instanceof AndNode )
456        {
457            Element newElement = element.addElement( "and" );
458
459            List<ExprNode> filterList = ( ( AndNode ) filter ).getChildren();
460
461            for ( int i = 0; i < filterList.size(); i++ )
462            {
463                toDsml( newElement, filterList.get( i ) );
464            }
465        }
466
467        // OR FILTER
468        else if ( filter instanceof OrNode )
469        {
470            Element newElement = element.addElement( "or" );
471
472            List<ExprNode> filterList = ( ( OrNode ) filter ).getChildren();
473
474            for ( int i = 0; i < filterList.size(); i++ )
475            {
476                toDsml( newElement, filterList.get( i ) );
477            }
478        }
479
480        // NOT FILTER
481        else if ( filter instanceof NotNode )
482        {
483            Element newElement = element.addElement( "not" );
484
485            toDsml( newElement, ( ( NotNode ) filter ).getFirstChild() );
486        }
487
488        // SUBSTRING FILTER
489        else if ( filter instanceof SubstringNode )
490        {
491            Element newElement = element.addElement( "substrings" );
492
493            SubstringNode substringFilter = ( SubstringNode ) filter;
494
495            newElement.addAttribute( NAME, substringFilter.getAttribute() );
496
497            String initial = substringFilter.getInitial();
498
499            if ( ( initial != null ) && ( !"".equals( initial ) ) )
500            {
501                newElement.addElement( "initial" ).setText( initial );
502            }
503
504            List<String> anyList = substringFilter.getAny();
505
506            for ( int i = 0; i < anyList.size(); i++ )
507            {
508                newElement.addElement( "any" ).setText( anyList.get( i ) );
509            }
510
511            String finalString = substringFilter.getFinal();
512
513            if ( ( finalString != null ) && ( !"".equals( finalString ) ) )
514            {
515                newElement.addElement( "final" ).setText( finalString );
516            }
517        }
518
519        // APPROXMATCH, EQUALITYMATCH, GREATEROREQUALS & LESSOREQUAL FILTERS
520        else if ( filter instanceof SimpleNode )
521        {
522            Element newElement = null;
523
524            if ( filter instanceof ApproximateNode )
525            {
526                newElement = element.addElement( "approxMatch" );
527            }
528            else if ( filter instanceof EqualityNode )
529            {
530                newElement = element.addElement( "equalityMatch" );
531            }
532            else if ( filter instanceof GreaterEqNode )
533            {
534                newElement = element.addElement( "greaterOrEqual" );
535            }
536            else
537            // it is a LessEqNode )
538            {
539                newElement = element.addElement( "lessOrEqual" );
540            }
541
542            String attributeName = ( ( SimpleNode<?> ) filter ).getAttribute();
543            newElement.addAttribute( NAME, attributeName );
544
545            Value<?> value = ( ( SimpleNode<?> ) filter ).getValue();
546            if ( value != null )
547            {
548                if ( ParserUtils.needsBase64Encoding( value ) )
549                {
550                    Namespace xsdNamespace = new Namespace( "xsd", ParserUtils.XML_SCHEMA_URI );
551                    Namespace xsiNamespace = new Namespace( "xsi", ParserUtils.XML_SCHEMA_INSTANCE_URI );
552                    element.getDocument().getRootElement().add( xsdNamespace );
553                    element.getDocument().getRootElement().add( xsiNamespace );
554
555                    Element valueElement = newElement.addElement( VALUE ).addText(
556                        ParserUtils.base64Encode( value ) );
557                    valueElement
558                        .addAttribute( new QName( "type", xsiNamespace ), "xsd:" + ParserUtils.BASE64BINARY );
559                }
560                else
561                {
562                    newElement.addElement( VALUE ).setText( value.getString() );
563                }
564            }
565        }
566
567        // PRESENT FILTER
568        else if ( filter instanceof PresenceNode )
569        {
570            Element newElement = element.addElement( "present" );
571
572            newElement.addAttribute( NAME, ( ( PresenceNode ) filter ).getAttribute() );
573        }
574
575        // EXTENSIBLEMATCH
576        else if ( filter instanceof ExtensibleNode )
577        {
578            Element newElement = element.addElement( "extensibleMatch" );
579
580            Value<?> value = ( ( ExtensibleNode ) filter ).getValue();
581            if ( value != null )
582            {
583                if ( ParserUtils.needsBase64Encoding( value ) )
584                {
585                    Namespace xsdNamespace = new Namespace( "xsd", ParserUtils.XML_SCHEMA_URI );
586                    Namespace xsiNamespace = new Namespace( "xsi", ParserUtils.XML_SCHEMA_INSTANCE_URI );
587                    element.getDocument().getRootElement().add( xsdNamespace );
588                    element.getDocument().getRootElement().add( xsiNamespace );
589
590                    Element valueElement = newElement.addElement( VALUE ).addText(
591                        ParserUtils.base64Encode( value.getValue() ) );
592                    valueElement.addAttribute( new QName( "type", xsiNamespace ), "xsd:" + ParserUtils.BASE64BINARY );
593                }
594                else
595                {
596                    newElement.addElement( VALUE ).setText( value.getString() );
597                }
598            }
599
600            if ( ( ( ExtensibleNode ) filter ).hasDnAttributes() )
601            {
602                newElement.addAttribute( "dnAttributes", "true" );
603            }
604
605            String matchingRule = ( ( ExtensibleNode ) filter ).getMatchingRuleId();
606            if ( ( matchingRule != null ) && ( "".equals( matchingRule ) ) )
607            {
608                newElement.addAttribute( "matchingRule", matchingRule );
609            }
610        }
611    }
612
613
614    /**
615     * {@inheritDoc}
616     */
617    public MessageTypeEnum[] getResponseTypes()
618    {
619        return getDecorated().getResponseTypes();
620    }
621
622
623    /**
624     * {@inheritDoc}
625     */
626    public Dn getBase()
627    {
628        return getDecorated().getBase();
629    }
630
631
632    /**
633     * {@inheritDoc}
634     */
635    public SearchRequest setBase( Dn baseDn )
636    {
637        getDecorated().setBase( baseDn );
638
639        return this;
640    }
641
642
643    /**
644     * {@inheritDoc}
645     */
646    public SearchScope getScope()
647    {
648        return getDecorated().getScope();
649    }
650
651
652    /**
653     * {@inheritDoc}
654     */
655    public SearchRequest setScope( SearchScope scope )
656    {
657        getDecorated().setScope( scope );
658
659        return this;
660    }
661
662
663    /**
664     * {@inheritDoc}
665     */
666    public AliasDerefMode getDerefAliases()
667    {
668        return getDecorated().getDerefAliases();
669    }
670
671
672    /**
673     * {@inheritDoc}
674     */
675    public SearchRequest setDerefAliases( AliasDerefMode aliasDerefAliases )
676    {
677        getDecorated().setDerefAliases( aliasDerefAliases );
678
679        return this;
680    }
681
682
683    /**
684     * {@inheritDoc}
685     */
686    public long getSizeLimit()
687    {
688        return getDecorated().getSizeLimit();
689    }
690
691
692    /**
693     * {@inheritDoc}
694     */
695    public SearchRequest setSizeLimit( long entriesMax )
696    {
697        getDecorated().setSizeLimit( entriesMax );
698
699        return this;
700    }
701
702
703    /**
704     * {@inheritDoc}
705     */
706    public int getTimeLimit()
707    {
708        return getDecorated().getTimeLimit();
709    }
710
711
712    /**
713     * {@inheritDoc}
714     */
715    public SearchRequest setTimeLimit( int secondsMax )
716    {
717        getDecorated().setTimeLimit( secondsMax );
718
719        return this;
720    }
721
722
723    /**
724     * {@inheritDoc}
725     */
726    public boolean getTypesOnly()
727    {
728        return getDecorated().getTypesOnly();
729    }
730
731
732    /**
733     * {@inheritDoc}
734     */
735    public SearchRequest setTypesOnly( boolean typesOnly )
736    {
737        getDecorated().setTypesOnly( typesOnly );
738
739        return this;
740    }
741
742
743    /**
744     * {@inheritDoc}
745     */
746    public ExprNode getFilter()
747    {
748        return getDecorated().getFilter();
749    }
750
751
752    /**
753     * {@inheritDoc}
754     */
755    public SearchRequest setFilter( ExprNode filter )
756    {
757        getDecorated().setFilter( filter );
758
759        return this;
760    }
761
762
763    /**
764     * {@inheritDoc}
765     */
766    public SearchRequest setFilter( String filter ) throws LdapException
767    {
768        getDecorated().setFilter( filter );
769
770        return this;
771    }
772
773
774    /**
775     * {@inheritDoc}
776     */
777    public List<String> getAttributes()
778    {
779        return getDecorated().getAttributes();
780    }
781
782
783    /**
784     * {@inheritDoc}
785     */
786    public SearchRequest addAttributes( String... attributes )
787    {
788        getDecorated().addAttributes( attributes );
789
790        return this;
791    }
792
793
794    /**
795     * {@inheritDoc}
796     */
797    public SearchRequest removeAttribute( String attribute )
798    {
799        getDecorated().removeAttribute( attribute );
800
801        return this;
802    }
803
804
805    /**
806     * {@inheritDoc}
807     */
808    public SearchRequest setMessageId( int messageId )
809    {
810        return ( SearchRequest ) super.setMessageId( messageId );
811    }
812
813
814    /**
815     * {@inheritDoc}
816     */
817    public SearchRequest addControl( Control control )
818    {
819        return ( SearchRequest ) super.addControl( control );
820    }
821
822
823    /**
824     * {@inheritDoc}
825     */
826    public SearchRequest addAllControls( Control[] controls )
827    {
828        return ( SearchRequest ) super.addAllControls( controls );
829    }
830
831
832    /**
833     * {@inheritDoc}
834     */
835    public SearchRequest removeControl( Control control )
836    {
837        return ( SearchRequest ) super.removeControl( control );
838    }
839
840
841    /**
842     * {@inheritDoc}
843     */
844    public boolean isFollowReferrals()
845    {
846        return getDecorated().isFollowReferrals();
847    }
848
849
850    /**
851     * {@inheritDoc}
852     */
853    public SearchRequest followReferrals()
854    {
855        return getDecorated().followReferrals();
856    }
857
858
859    /**
860     * {@inheritDoc}
861     */
862    public boolean isIgnoreReferrals()
863    {
864        return getDecorated().isIgnoreReferrals();
865    }
866
867
868    /**
869     * {@inheritDoc}
870     */
871    public SearchRequest ignoreReferrals()
872    {
873        return getDecorated().ignoreReferrals();
874    }
875}