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 */
019package org.apache.directory.server.core.api.entry;
020
021
022import java.util.ArrayList;
023import java.util.HashSet;
024import java.util.List;
025import java.util.NoSuchElementException;
026import java.util.Set;
027
028import javax.naming.NamingEnumeration;
029import javax.naming.NamingException;
030import javax.naming.directory.Attributes;
031import javax.naming.directory.BasicAttribute;
032import javax.naming.directory.BasicAttributes;
033import javax.naming.directory.DirContext;
034import javax.naming.directory.ModificationItem;
035import javax.naming.directory.SearchResult;
036
037import org.apache.directory.api.ldap.model.constants.SchemaConstants;
038import org.apache.directory.api.ldap.model.entry.Attribute;
039import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
040import org.apache.directory.api.ldap.model.entry.DefaultEntry;
041import org.apache.directory.api.ldap.model.entry.DefaultModification;
042import org.apache.directory.api.ldap.model.entry.Entry;
043import org.apache.directory.api.ldap.model.entry.Modification;
044import org.apache.directory.api.ldap.model.entry.ModificationOperation;
045import org.apache.directory.api.ldap.model.entry.Value;
046import org.apache.directory.api.ldap.model.exception.LdapException;
047import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeTypeException;
048import org.apache.directory.api.ldap.model.name.Dn;
049import org.apache.directory.api.ldap.model.schema.AttributeType;
050import org.apache.directory.api.ldap.model.schema.AttributeTypeOptions;
051import org.apache.directory.api.ldap.model.schema.SchemaManager;
052import org.apache.directory.api.ldap.model.schema.SchemaUtils;
053import org.apache.directory.api.util.EmptyEnumeration;
054import org.apache.directory.api.util.Strings;
055import org.apache.directory.server.core.api.interceptor.context.FilteringOperationContext;
056import org.apache.directory.server.i18n.I18n;
057
058
059/**
060 * A helper class used to manipulate Entries, Attributes and Values.
061 *
062 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
063 */
064public final class ServerEntryUtils
065{
066    private ServerEntryUtils()
067    {
068    }
069
070
071    /**
072     * Convert a ServerAttribute into a BasicAttribute. The Dn is lost
073     * during this conversion, as the Attributes object does not store
074     * this element.
075     *
076     * @param entryAttribute The Server entry to convert
077     * @return An instance of a AttributesImpl() object
078     */
079    public static javax.naming.directory.Attribute toBasicAttribute( Attribute entryAttribute )
080    {
081        AttributeType attributeType = entryAttribute.getAttributeType();
082
083        javax.naming.directory.Attribute attribute = new BasicAttribute( attributeType.getName() );
084
085        for ( Value value : entryAttribute )
086        {
087            if ( attributeType.isHR() )
088            {
089                attribute.add( value.getString() );
090            }
091            else
092            {
093                attribute.add( value.getBytes() );
094            }
095        }
096
097        return attribute;
098    }
099
100
101    /**
102     * Convert a ServerEntry into a BasicAttributes. The Dn is lost
103     * during this conversion, as the Attributes object does not store
104     * this element.
105     *
106     * @param entry The entry to convert
107     * @return An instance of a AttributesImpl() object
108     */
109    public static Attributes toBasicAttributes( Entry entry )
110    {
111        if ( entry == null )
112        {
113            return null;
114        }
115
116        Attributes attributes = new BasicAttributes( true );
117
118        for ( Attribute attribute : entry.getAttributes() )
119        {
120            AttributeType attributeType = attribute.getAttributeType();
121            Attribute attr = entry.get( attributeType );
122
123            // Deal with a special case : an entry without any ObjectClass
124            if ( attributeType.getOid().equals( SchemaConstants.OBJECT_CLASS_AT_OID ) && attr.size() == 0 )
125            {
126                // We don't have any objectClass, just dismiss this element
127                continue;
128            }
129
130            attributes.put( toBasicAttribute( attr ) );
131        }
132
133        return attributes;
134    }
135
136
137    /**
138     * Convert a BasicAttribute or a AttributeImpl to a ServerAtribute
139     *
140     * @param attribute the BasicAttributes or AttributesImpl instance to convert
141     * @param attributeType The AttributeType to use
142     * @return An instance of a ServerEntry object
143     * 
144     * @throws LdapException If we had an incorrect attribute
145     */
146    public static Attribute toServerAttribute( javax.naming.directory.Attribute attribute, AttributeType attributeType )
147        throws LdapException
148    {
149        if ( attribute == null )
150        {
151            return null;
152        }
153
154        try
155        {
156            Attribute serverAttribute = new DefaultAttribute( attributeType );
157
158            for ( NamingEnumeration<?> values = attribute.getAll(); values.hasMoreElements(); )
159            {
160                Object value = values.nextElement();
161                int nbAdded = 0;
162
163                if ( value == null )
164                {
165                    continue;
166                }
167
168                if ( serverAttribute.isHumanReadable() )
169                {
170                    if ( value instanceof String )
171                    {
172                        nbAdded = serverAttribute.add( ( String ) value );
173                    }
174                    else if ( value instanceof byte[] )
175                    {
176                        nbAdded = serverAttribute.add( Strings.utf8ToString( ( byte[] ) value ) );
177                    }
178                    else
179                    {
180                        throw new LdapInvalidAttributeTypeException();
181                    }
182                }
183                else
184                {
185                    if ( value instanceof String )
186                    {
187                        nbAdded = serverAttribute.add( Strings.getBytesUtf8( ( String ) value ) );
188                    }
189                    else if ( value instanceof byte[] )
190                    {
191                        nbAdded = serverAttribute.add( ( byte[] ) value );
192                    }
193                    else
194                    {
195                        throw new LdapInvalidAttributeTypeException();
196                    }
197                }
198
199                if ( nbAdded == 0 )
200                {
201                    throw new LdapInvalidAttributeTypeException();
202                }
203            }
204
205            return serverAttribute;
206        }
207        catch ( NamingException ne )
208        {
209            throw new LdapInvalidAttributeTypeException();
210        }
211    }
212
213
214    /**
215     * Convert a BasicAttributes or a AttributesImpl to a ServerEntry
216     *
217     * @param attributes the BasicAttributes or AttributesImpl instance to convert
218     * @param dn The Dn which is needed by the ServerEntry
219     * @param schemaManager The SchemaManager instance
220     * @return An instance of a ServerEntry object
221     * 
222     * @throws LdapInvalidAttributeTypeException If we get an invalid attribute
223     */
224    public static Entry toServerEntry( Attributes attributes, Dn dn, SchemaManager schemaManager )
225        throws LdapInvalidAttributeTypeException
226    {
227        if ( attributes instanceof BasicAttributes )
228        {
229            try
230            {
231                Entry entry = new DefaultEntry( schemaManager, dn );
232
233                for ( NamingEnumeration<? extends javax.naming.directory.Attribute> attrs = attributes.getAll(); attrs
234                    .hasMoreElements(); )
235                {
236                    javax.naming.directory.Attribute attr = attrs.nextElement();
237
238                    String attributeId = attr.getID();
239                    String id = SchemaUtils.stripOptions( attributeId );
240                    Set<String> options = SchemaUtils.getOptions( attributeId );
241                    // TODO : handle options.
242                    AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
243                    Attribute serverAttribute = ServerEntryUtils.toServerAttribute( attr, attributeType );
244
245                    if ( serverAttribute != null )
246                    {
247                        entry.put( serverAttribute );
248                    }
249                }
250
251                return entry;
252            }
253            catch ( LdapException ne )
254            {
255                throw new LdapInvalidAttributeTypeException( ne.getLocalizedMessage() );
256            }
257        }
258        else
259        {
260            return null;
261        }
262    }
263
264
265    /**
266     * Gets the target entry as it would look after a modification operation 
267     * was performed on it.
268     * 
269     * @param mod the modification
270     * @param entry the source entry that is modified
271     * @param schemaManager The SchemaManager instance
272     * @return the resultant entry after the modification has taken place
273     * @throws LdapException if there are problems accessing attributes
274     */
275    public static Entry getTargetEntry( Modification mod, Entry entry, SchemaManager schemaManager )
276        throws LdapException
277    {
278        Entry targetEntry = entry.clone();
279        ModificationOperation modOp = mod.getOperation();
280        String id = mod.getAttribute().getUpId();
281        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
282
283        switch ( modOp )
284        {
285            case REPLACE_ATTRIBUTE:
286                targetEntry.put( mod.getAttribute() );
287                break;
288
289            case REMOVE_ATTRIBUTE:
290                Attribute toBeRemoved = mod.getAttribute();
291
292                if ( toBeRemoved.size() == 0 )
293                {
294                    targetEntry.removeAttributes( id );
295                }
296                else
297                {
298                    Attribute existing = targetEntry.get( id );
299
300                    if ( existing != null )
301                    {
302                        for ( Value value : toBeRemoved )
303                        {
304                            existing.remove( value );
305                        }
306                    }
307                }
308                break;
309
310            case ADD_ATTRIBUTE:
311                Attribute combined = new DefaultAttribute( id, attributeType );
312                Attribute toBeAdded = mod.getAttribute();
313                Attribute existing = entry.get( id );
314
315                if ( existing != null )
316                {
317                    for ( Value value : existing )
318                    {
319                        combined.add( value );
320                    }
321                }
322
323                for ( Value value : toBeAdded )
324                {
325                    combined.add( value );
326                }
327
328                targetEntry.put( combined );
329                break;
330
331            default:
332                throw new IllegalStateException( I18n.err( I18n.ERR_464, modOp ) );
333        }
334
335        return targetEntry;
336    }
337
338
339    /**
340     * Creates a new attribute which contains the values representing the union
341     * of two attributes. If one attribute is null then the resultant attribute
342     * returned is a copy of the non-null attribute. If both are null then we
343     * cannot determine the attribute ID and an {@link IllegalArgumentException}
344     * is raised.
345     * 
346     * @param attr0 the first attribute
347     * @param attr1 the second attribute
348     * @return a new attribute with the union of values from both attribute
349     *         arguments
350     * @throws LdapException if there are problems accessing attribute values
351     */
352    public static Attribute getUnion( Attribute attr0, Attribute attr1 ) throws LdapException
353    {
354        if ( attr0 == null && attr1 == null )
355        {
356            throw new IllegalArgumentException( I18n.err( I18n.ERR_465 ) );
357        }
358        else if ( attr0 == null )
359        {
360            return attr1.clone();
361        }
362        else if ( attr1 == null )
363        {
364            return attr0.clone();
365        }
366        else if ( !attr0.getAttributeType().equals( attr1.getAttributeType() ) )
367        {
368            throw new IllegalArgumentException( I18n.err( I18n.ERR_466 ) );
369        }
370
371        Attribute attr = attr0.clone();
372
373        for ( Value value : attr1 )
374        {
375            attr.add( value );
376        }
377
378        return attr;
379    }
380
381
382    /**
383     * Convert a ModificationItem to an instance of a ServerModification object
384     *
385     * @param modificationImpl the modification instance to convert
386     * @param attributeType the associated attributeType
387     * @return a instance of a ServerModification object
388     */
389    private static Modification toServerModification( ModificationItem modificationImpl, AttributeType attributeType )
390        throws LdapException
391    {
392        ModificationOperation operation;
393
394        switch ( modificationImpl.getModificationOp() )
395        {
396            case DirContext.REMOVE_ATTRIBUTE:
397                operation = ModificationOperation.REMOVE_ATTRIBUTE;
398                break;
399
400            case DirContext.REPLACE_ATTRIBUTE:
401                operation = ModificationOperation.REPLACE_ATTRIBUTE;
402                break;
403
404            case DirContext.ADD_ATTRIBUTE:
405            default:
406                operation = ModificationOperation.ADD_ATTRIBUTE;
407                break;
408
409        }
410
411        return new DefaultModification( operation,
412            ServerEntryUtils.toServerAttribute( modificationImpl.getAttribute(), attributeType ) );
413    }
414
415
416    /**
417     * 
418     * Convert a list of ModificationItemImpl to a list of LDAP API Modifications
419     *
420     * @param modificationItems The modificationItems to convert
421     * @param schemaManager The SchemaManager instance
422     * @return A list of converted Modification
423     * @throws LdapException If the conversion failed
424     */
425    public static List<Modification> convertToServerModification( List<ModificationItem> modificationItems,
426        SchemaManager schemaManager ) throws LdapException
427    {
428        if ( modificationItems != null )
429        {
430            List<Modification> modifications = new ArrayList<>( modificationItems.size() );
431
432            for ( ModificationItem modificationItem : modificationItems )
433            {
434                AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( modificationItem
435                    .getAttribute().getID() );
436                modifications.add( toServerModification( modificationItem, attributeType ) );
437            }
438
439            return modifications;
440        }
441        else
442        {
443            return null;
444        }
445    }
446
447
448    /**
449     * Convert a Modification to an instance of a ServerModification object.
450     *
451     * @param modificationImpl the modification instance to convert
452     * @param attributeType the associated attributeType
453     * @return a instance of a ServerModification object
454     */
455    private static Modification toServerModification( Modification modification, AttributeType attributeType )
456        throws LdapException
457    {
458        return new DefaultModification(
459            modification.getOperation(),
460            new DefaultAttribute( attributeType, modification.getAttribute() ) );
461    }
462
463
464    /**
465     * Convert a JNDI set of Modifications to LDAP API Modifications
466     * 
467     * @param modifications The modifications to convert
468     * @param schemaManager The SchemaManager instance
469     * @return The list of converted Modifications
470     * @throws LdapException If the conversion failed
471     */
472    public static List<Modification> toServerModification( Modification[] modifications,
473        SchemaManager schemaManager ) throws LdapException
474    {
475        if ( modifications != null )
476        {
477            List<Modification> modificationsList = new ArrayList<>();
478
479            for ( Modification modification : modifications )
480            {
481                String attributeId = modification.getAttribute().getUpId();
482                String id = stripOptions( attributeId );
483                modification.getAttribute().setUpId( id );
484                Set<String> options = getOptions( attributeId );
485
486                // -------------------------------------------------------------------
487                // DIRSERVER-646 Fix: Replacing an unknown attribute with no values 
488                // (deletion) causes an error
489                // -------------------------------------------------------------------
490                if ( !schemaManager.getAttributeTypeRegistry().contains( id )
491                    && modification.getAttribute().size() == 0
492                    && modification.getOperation() == ModificationOperation.REPLACE_ATTRIBUTE )
493                {
494                    // The attributeType does not exist in the schema.
495                    // It's an error
496                    String message = I18n.err( I18n.ERR_467, id );
497                    throw new LdapInvalidAttributeTypeException( message );
498                }
499                else
500                {
501                    // TODO : handle options
502                    AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
503                    modificationsList.add( toServerModification( modification, attributeType ) );
504                }
505            }
506
507            return modificationsList;
508        }
509        else
510        {
511            return null;
512        }
513    }
514
515
516    /**
517     * Convert a JNDI set of ModificationItems to LDAP API Modifications
518     * 
519     * @param modifications The modificationItems to convert
520     * @param schemaManager The SchemaManager instance
521     * @return The list of converted ModificationItems
522     * @throws LdapException If the conversion failed
523     */
524    public static List<Modification> toServerModification( ModificationItem[] modifications,
525        SchemaManager schemaManager ) throws LdapException
526    {
527        if ( modifications != null )
528        {
529            List<Modification> modificationsList = new ArrayList<>();
530
531            for ( ModificationItem modification : modifications )
532            {
533                String attributeId = modification.getAttribute().getID();
534                String id = stripOptions( attributeId );
535                Set<String> options = getOptions( attributeId );
536
537                // -------------------------------------------------------------------
538                // DIRSERVER-646 Fix: Replacing an unknown attribute with no values 
539                // (deletion) causes an error
540                // -------------------------------------------------------------------
541
542                // TODO - after removing JNDI we need to make the server handle 
543                // this in the codec
544
545                if ( !schemaManager.getAttributeTypeRegistry().contains( id )
546                    && modification.getAttribute().size() == 0
547                    && modification.getModificationOp() == DirContext.REPLACE_ATTRIBUTE )
548                {
549                    continue;
550                }
551
552                // -------------------------------------------------------------------
553                // END DIRSERVER-646 Fix
554                // -------------------------------------------------------------------
555
556                // TODO : handle options
557                AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
558                modificationsList.add( toServerModification( modification, attributeType ) );
559            }
560
561            return modificationsList;
562        }
563        else
564        {
565            return null;
566        }
567    }
568
569
570    /**
571     * Utility method to extract a modification item from an array of modifications.
572     * 
573     * @param mods the array of ModificationItems to extract the Attribute from.
574     * @param type the attributeType spec of the Attribute to extract
575     * @return the modification item on the attributeType specified
576     */
577    public static Modification getModificationItem( List<Modification> mods, AttributeType type )
578    {
579        for ( Modification modification : mods )
580        {
581            Attribute attribute = modification.getAttribute();
582
583            if ( attribute.getAttributeType() == type )
584            {
585                return modification;
586            }
587        }
588
589        return null;
590    }
591
592
593    /**
594     * Utility method to extract an attribute from a list of modifications.
595     * 
596     * @param mods the list of ModificationItems to extract the Attribute from.
597     * @param type the attributeType spec of the Attribute to extract
598     * @return the extract Attribute or null if no such attribute exists
599     */
600    public static Attribute getAttribute( List<Modification> mods, AttributeType type )
601    {
602        Modification mod = getModificationItem( mods, type );
603
604        if ( mod != null )
605        {
606            return mod.getAttribute();
607        }
608
609        return null;
610    }
611
612
613    /**
614     * Encapsulate a ServerSearchResult enumeration into a SearchResult enumeration
615     * @param result The ServerSearchResult enumeration
616     * @return A SearchResultEnumeration
617     */
618    public static NamingEnumeration<SearchResult> toSearchResultEnum( final NamingEnumeration<ServerSearchResult> result )
619    {
620        if ( result instanceof EmptyEnumeration<?> )
621        {
622            return new EmptyEnumeration<>();
623        }
624
625        return new NamingEnumeration<SearchResult>()
626        {
627            public void close() throws NamingException
628            {
629                result.close();
630            }
631
632
633            /**
634             * @see javax.naming.NamingEnumeration#hasMore()
635             */
636            public boolean hasMore() throws NamingException
637            {
638                return result.hasMore();
639            }
640
641
642            /**
643             * @see javax.naming.NamingEnumeration#next()
644             */
645            public SearchResult next() throws NamingException
646            {
647                ServerSearchResult rec = result.next();
648
649                return new SearchResult(
650                    rec.getDn().getName(),
651                    rec.getObject(),
652                    toBasicAttributes( rec.getServerEntry() ),
653                    rec.isRelative() );
654            }
655
656
657            /**
658             * @see java.util.Enumeration#hasMoreElements()
659             */
660            public boolean hasMoreElements()
661            {
662                return result.hasMoreElements();
663            }
664
665
666            /**
667             * @see java.util.Enumeration#nextElement()
668             */
669            public SearchResult nextElement()
670            {
671                try
672                {
673                    ServerSearchResult rec = result.next();
674
675                    return new SearchResult(
676                        rec.getDn().getName(),
677                        rec.getObject(),
678                        toBasicAttributes( rec.getServerEntry() ),
679                        rec.isRelative() );
680                }
681                catch ( NamingException ne )
682                {
683                    NoSuchElementException nsee =
684                        new NoSuchElementException( I18n.err( I18n.ERR_468 ) );
685                    nsee.initCause( ne );
686                    throw nsee;
687                }
688            }
689        };
690    }
691
692
693    /**
694     * Remove the options from the attributeType, and returns the ID.
695     * 
696     * RFC 4512 :
697     * attributedescription = attributetype options
698     * attributetype = oid
699     * options = *( SEMI option )
700     * option = 1*keychar
701     */
702    private static String stripOptions( String attributeId )
703    {
704        int optionsPos = attributeId.indexOf( ';' );
705
706        if ( optionsPos != -1 )
707        {
708            return attributeId.substring( 0, optionsPos );
709        }
710        else
711        {
712            return attributeId;
713        }
714    }
715
716
717    /**
718     * Get the options from the attributeType.
719     * 
720     * For instance, given :
721     * jpegphoto;binary;lang=jp
722     * 
723     * your get back a set containing { "binary", "lang=jp" }
724     */
725    private static Set<String> getOptions( String attributeId )
726    {
727        int optionsPos = attributeId.indexOf( ';' );
728
729        if ( optionsPos != -1 )
730        {
731            Set<String> options = new HashSet<>();
732
733            String[] res = attributeId.substring( optionsPos + 1 ).split( ";" );
734
735            for ( String option : res )
736            {
737                if ( !Strings.isEmpty( option ) )
738                {
739                    options.add( option );
740                }
741            }
742
743            return options;
744        }
745        else
746        {
747            return null;
748        }
749    }
750
751
752    /**
753     * Filters an entry accordingly to the requested Attribute list.
754     * 
755     * @param schemaManager The SchemaManager instance
756     * @param operationContext The SearchingOperationContext
757     * @param entry The entry to filter
758     * @throws LdapException If the filtering fails
759     */
760    public static void filterContents( SchemaManager schemaManager, FilteringOperationContext operationContext,
761        Entry entry ) throws LdapException
762    {
763        boolean typesOnly = operationContext.isTypesOnly();
764
765        boolean returnAll = ( operationContext.isAllOperationalAttributes() && operationContext.isAllUserAttributes() )
766            && ( !typesOnly );
767
768        if ( returnAll )
769        {
770            return;
771        }
772
773        // for special handling of entryDN attribute, see DIRSERVER-1902
774        Entry originalEntry = ( ( ClonedServerEntry ) entry ).getOriginalEntry();
775
776        AttributeType entryDnType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ENTRY_DN_AT_OID );
777        AttributeType refType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.REF_AT_OID );
778
779        // First, remove all the attributes if we have the NoAttribute flag set to true
780        if ( operationContext.isNoAttributes() )
781        {
782            for ( Attribute attribute : originalEntry )
783            {
784                AttributeType attributeType = attribute.getAttributeType();
785
786                // Bypass the ref attribute, unless the ManageDSAIT control is present
787                if ( operationContext.isReferralThrown() && attributeType.equals( refType ) )
788                {
789                    continue;
790                }
791
792                entry.remove( entry.get( attributeType ) );
793            }
794
795            entry.removeAttributes( entryDnType );
796
797            return;
798        }
799
800        // If the user has requested all the User attributes ('*') only, we filter the entry's attribute to keep only
801        // the USER attributes, plus the Operational attributes in the returning list 
802        if ( operationContext.isAllUserAttributes() )
803        {
804            for ( Attribute attribute : originalEntry )
805            {
806                AttributeType attributeType = attribute.getAttributeType();
807
808                // Bypass the ref attribute, unless the ManageDSAIT control is present
809                if ( operationContext.isReferralThrown() && attributeType.equals( refType ) )
810                {
811                    continue;
812                }
813
814                if ( attributeType.isOperational() )
815                {
816                    if ( !operationContext.contains( schemaManager, attributeType ) )
817                    {
818                        entry.removeAttributes( attributeType );
819                    }
820                    else if ( typesOnly )
821                    {
822                        entry.get( attributeType ).clear();
823                    }
824                }
825                else if ( typesOnly )
826                {
827                    entry.get( attributeType ).clear();
828                }
829            }
830
831            // DIRSERVER-1953
832            if ( !operationContext.contains( schemaManager, entryDnType ) )
833            {
834                entry.removeAttributes( entryDnType );
835            }
836
837            return;
838        }
839
840        // If the user has requested all the Operational attributes ('+') only, we filter the entry's attribute to keep only
841        // the OPERATIONAL attributes, plus the User attributes in the returning list 
842        if ( operationContext.isAllOperationalAttributes() )
843        {
844            for ( Attribute attribute : originalEntry )
845            {
846                AttributeType attributeType = attribute.getAttributeType();
847
848                if ( attributeType.isUser() )
849                {
850                    if ( !operationContext.contains( schemaManager, attributeType ) )
851                    {
852                        entry.removeAttributes( attributeType );
853                    }
854                    else if ( typesOnly )
855                    {
856                        entry.get( attributeType ).clear();
857                    }
858                }
859                else if ( typesOnly )
860                {
861                    entry.get( attributeType ).clear();
862                }
863            }
864
865            if ( !operationContext.contains( schemaManager, entryDnType ) )
866            {
867                entry.removeAttributes( entryDnType );
868            }
869            else if ( typesOnly )
870            {
871                entry.get( entryDnType ).clear();
872            }
873
874            return;
875        }
876
877        // Last, not least, check if the attributes are in the returning list
878        if ( operationContext.getReturningAttributes() != null )
879        {
880            for ( Attribute attribute : originalEntry )
881            {
882                AttributeType attributeType = attribute.getAttributeType();
883
884                // Bypass the ref attribute, unless the ManageDSAIT control is present
885                if ( operationContext.isReferralThrown() && attributeType.equals( refType ) )
886                {
887                    continue;
888                }
889
890                if ( !operationContext.contains( schemaManager, attributeType ) )
891                {
892                    entry.removeAttributes( attributeType );
893                    continue;
894                }
895
896                boolean isNotRequested = true;
897
898                for ( AttributeTypeOptions attrOptions : operationContext.getReturningAttributes() )
899                {
900                    if ( attrOptions.getAttributeType().equals( attributeType )
901                        || attrOptions.getAttributeType().isAncestorOf( attributeType ) )
902                    {
903                        isNotRequested = false;
904                        break;
905                    }
906                }
907
908                if ( isNotRequested )
909                {
910                    entry.removeAttributes( attributeType );
911                }
912                else if ( typesOnly )
913                {
914                    entry.get( attributeType ).clear();
915                }
916            }
917
918            if ( !operationContext.contains( schemaManager, entryDnType ) )
919            {
920                entry.removeAttributes( entryDnType );
921            }
922            else if ( typesOnly )
923            {
924                entry.get( entryDnType ).clear();
925            }
926        }
927    }
928}