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