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