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.ldap.extras.controls.syncrepl_impl;
021
022
023import java.nio.ByteBuffer;
024import java.util.ArrayList;
025import java.util.List;
026
027import org.apache.directory.api.asn1.Asn1Object;
028import org.apache.directory.api.asn1.DecoderException;
029import org.apache.directory.api.asn1.EncoderException;
030import org.apache.directory.api.asn1.ber.Asn1Decoder;
031import org.apache.directory.api.asn1.ber.tlv.BerValue;
032import org.apache.directory.api.asn1.ber.tlv.TLV;
033import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
034import org.apache.directory.api.i18n.I18n;
035import org.apache.directory.api.ldap.codec.api.ControlDecorator;
036import org.apache.directory.api.ldap.codec.api.LdapApiService;
037import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SyncInfoValue;
038import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SyncInfoValueImpl;
039import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SynchronizationInfoEnum;
040import org.apache.directory.api.util.Strings;
041
042
043/**
044 * A syncInfoValue object, as defined in RFC 4533
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 */
048public class SyncInfoValueDecorator extends ControlDecorator<SyncInfoValue> implements SyncInfoValue
049{
050    /** The syncUUIDs cumulative length */
051    private int syncUUIDsLength;
052
053    /** An instance of this decoder */
054    private static final Asn1Decoder decoder = new Asn1Decoder();
055
056
057    /**
058     * The constructor for this codec. Dont't forget to set the type.
059     */
060    public SyncInfoValueDecorator( LdapApiService codec )
061    {
062        super( codec, new SyncInfoValueImpl() );
063    }
064
065
066    /**
067     * The constructor for this codec. Dont't forget to set the type.
068     */
069    public SyncInfoValueDecorator( LdapApiService codec, SyncInfoValue control )
070    {
071        super( codec, control );
072    }
073
074
075    /**
076     * The constructor for this codec.
077     * @param type The kind of syncInfo we will store. Can be newCookie,
078     * refreshPresent, refreshDelete or syncIdSet
079     */
080    public SyncInfoValueDecorator( LdapApiService codec, SynchronizationInfoEnum type )
081    {
082        this( codec );
083
084        setType( type );
085    }
086
087    /** The global length for this control */
088    private int syncInfoValueLength;
089
090
091    /**
092     * {@inheritDoc}
093     */
094    public SynchronizationInfoEnum getType()
095    {
096        return getDecorated().getType();
097    }
098
099
100    /**
101     * {@inheritDoc}
102     */
103    public void setType( SynchronizationInfoEnum type )
104    {
105        this.getDecorated().setType( type );
106
107        // Initialize the arrayList if needed
108        if ( ( type == SynchronizationInfoEnum.SYNC_ID_SET ) && ( getDecorated().getSyncUUIDs() == null ) )
109        {
110            getDecorated().setSyncUUIDs( new ArrayList<byte[]>() );
111        }
112    }
113
114
115    /**
116     * {@inheritDoc}
117     */
118    public byte[] getCookie()
119    {
120        return getDecorated().getCookie();
121    }
122
123
124    /**
125     * {@inheritDoc}
126     */
127    public void setCookie( byte[] cookie )
128    {
129        // Copy the bytes
130        if ( !Strings.isEmpty( cookie ) )
131        {
132            byte[] copy = new byte[cookie.length];
133            System.arraycopy( cookie, 0, copy, 0, cookie.length );
134            getDecorated().setCookie( copy );
135        }
136        else
137        {
138            getDecorated().setCookie( null );
139        }
140    }
141
142
143    /**
144     * {@inheritDoc}
145     */
146    public boolean isRefreshDone()
147    {
148        return getDecorated().isRefreshDone();
149    }
150
151
152    /**
153     * {@inheritDoc}
154     */
155    public void setRefreshDone( boolean refreshDone )
156    {
157        getDecorated().setRefreshDone( refreshDone );
158    }
159
160
161    /**
162     * {@inheritDoc}
163     */
164    public boolean isRefreshDeletes()
165    {
166        return getDecorated().isRefreshDeletes();
167    }
168
169
170    /**
171     * {@inheritDoc}
172     */
173    public void setRefreshDeletes( boolean refreshDeletes )
174    {
175        getDecorated().setRefreshDeletes( refreshDeletes );
176    }
177
178
179    /**
180     * {@inheritDoc}
181     */
182    public List<byte[]> getSyncUUIDs()
183    {
184        return getDecorated().getSyncUUIDs();
185    }
186
187
188    /**
189     * {@inheritDoc}
190     */
191    public void setSyncUUIDs( List<byte[]> syncUUIDs )
192    {
193        getDecorated().setSyncUUIDs( syncUUIDs );
194    }
195
196
197    /**
198     * {@inheritDoc}
199     */
200    public void addSyncUUID( byte[] syncUUID )
201    {
202        getDecorated().addSyncUUID( syncUUID );
203    }
204
205
206    /**
207     * Compute the SyncInfoValue length.
208     *
209     * SyncInfoValue :
210     *
211     * 0xA0 L1 abcd                   // newCookie
212     * 0xA1 L2                        // refreshDelete
213     *   |
214     *  [+--> 0x04 L3 abcd]           // cookie
215     *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDone
216     * 0xA2 L4                        // refreshPresent
217     *   |
218     *  [+--> 0x04 L5 abcd]           // cookie
219     *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDone
220     * 0xA3 L6                        // syncIdSet
221     *   |
222     *  [+--> 0x04 L7 abcd]           // cookie
223     *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDeletes
224     *   +--> 0x31 L8                 // SET OF syncUUIDs
225     *          |
226     *         [+--> 0x04 L9 abcd]    // syncUUID    public static final int AND_FILTER_TAG = 0xA0;
227
228    public static final int OR_FILTER_TAG = 0xA1;
229
230    public static final int NOT_FILTER_TAG = 0xA2;
231
232    public static final int BIND_REQUEST_SASL_TAG = 0xA3;
233
234     */
235    @Override
236    public int computeLength()
237    {
238        // The mode length
239        syncInfoValueLength = 0;
240
241        switch ( getType() )
242        {
243            case NEW_COOKIE:
244                if ( getCookie() != null )
245                {
246                    syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
247                }
248                else
249                {
250                    syncInfoValueLength = 1 + 1;
251                }
252
253                valueLength = syncInfoValueLength;
254
255                // Call the super class to compute the global control length
256                return valueLength;
257
258            case REFRESH_DELETE:
259            case REFRESH_PRESENT:
260                if ( getCookie() != null )
261                {
262                    syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
263                }
264
265                // The refreshDone flag, only if not true, as it default to true
266                if ( !isRefreshDone() )
267                {
268                    syncInfoValueLength += 1 + 1 + 1;
269                }
270
271                valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
272
273                // Call the super class to compute the global control length
274                return valueLength;
275
276            case SYNC_ID_SET:
277                if ( getCookie() != null )
278                {
279                    syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
280                }
281
282                // The refreshDeletes flag, default to false
283                if ( isRefreshDeletes() )
284                {
285                    syncInfoValueLength += 1 + 1 + 1;
286                }
287
288                // The syncUUIDs if any
289                syncUUIDsLength = 0;
290
291                if ( getSyncUUIDs().size() != 0 )
292                {
293                    for ( byte[] syncUUID : getSyncUUIDs() )
294                    {
295                        int uuidLength = 1 + TLV.getNbBytes( syncUUID.length ) + syncUUID.length;
296
297                        syncUUIDsLength += uuidLength;
298                    }
299                }
300
301                syncInfoValueLength += 1 + TLV.getNbBytes( syncUUIDsLength ) + syncUUIDsLength;
302                valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
303
304                // Call the super class to compute the global control length
305                return valueLength;
306
307            default:
308
309        }
310
311        return 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
312    }
313
314
315    /**
316     * Encode the SyncInfoValue control
317     *
318     * @param buffer The encoded sink
319     * @return A ByteBuffer that contains the encoded PDU
320     * @throws EncoderException If anything goes wrong.
321     */
322    @Override
323    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
324    {
325        if ( buffer == null )
326        {
327            throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
328        }
329
330        switch ( getType() )
331        {
332            case NEW_COOKIE:
333                // The first case : newCookie
334                buffer.put( ( byte ) SyncInfoValueTags.NEW_COOKIE_TAG.getValue() );
335
336                // As the OCTET_STRING is absorbed by the Application tag,
337                // we have to store the L and V separately
338                if ( ( getCookie() == null ) || ( getCookie().length == 0 ) )
339                {
340                    buffer.put( ( byte ) 0 );
341                }
342                else
343                {
344                    buffer.put( TLV.getBytes( getCookie().length ) );
345                    buffer.put( getCookie() );
346                }
347
348                break;
349
350            case REFRESH_DELETE:
351                // The second case : refreshDelete
352                buffer.put( ( byte ) SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() );
353                buffer.put( TLV.getBytes( syncInfoValueLength ) );
354
355                // The cookie, if any
356                if ( getCookie() != null )
357                {
358                    BerValue.encode( buffer, getCookie() );
359                }
360
361                // The refreshDone flag
362                if ( !isRefreshDone() )
363                {
364                    BerValue.encode( buffer, isRefreshDone() );
365                }
366
367                break;
368
369            case REFRESH_PRESENT:
370                // The third case : refreshPresent
371                buffer.put( ( byte ) SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() );
372                buffer.put( TLV.getBytes( syncInfoValueLength ) );
373
374                // The cookie, if any
375                if ( getCookie() != null )
376                {
377                    BerValue.encode( buffer, getCookie() );
378                }
379
380                // The refreshDone flag
381                if ( !isRefreshDone() )
382                {
383                    BerValue.encode( buffer, isRefreshDone() );
384                }
385
386                break;
387
388            case SYNC_ID_SET:
389                // The last case : syncIdSet
390                buffer.put( ( byte ) SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() );
391                buffer.put( TLV.getBytes( syncInfoValueLength ) );
392
393                // The cookie, if any
394                if ( getCookie() != null )
395                {
396                    BerValue.encode( buffer, getCookie() );
397                }
398
399                // The refreshDeletes flag if not false
400                if ( isRefreshDeletes() )
401                {
402                    BerValue.encode( buffer, isRefreshDeletes() );
403                }
404
405                // The syncUUIDs
406                buffer.put( UniversalTag.SET.getValue() );
407                buffer.put( TLV.getBytes( syncUUIDsLength ) );
408
409                // Loop on the UUIDs if any
410                if ( getSyncUUIDs().size() != 0 )
411                {
412                    for ( byte[] syncUUID : getSyncUUIDs() )
413                    {
414                        BerValue.encode( buffer, syncUUID );
415                    }
416                }
417        }
418
419        return buffer;
420    }
421
422
423    /**
424     * {@inheritDoc}
425     */
426    @Override
427    public byte[] getValue()
428    {
429        if ( value == null )
430        {
431            try
432            {
433                computeLength();
434                ByteBuffer buffer = ByteBuffer.allocate( valueLength );
435
436                switch ( getType() )
437                {
438                    case NEW_COOKIE:
439                        // The first case : newCookie
440                        buffer.put( ( byte ) SyncInfoValueTags.NEW_COOKIE_TAG.getValue() );
441
442                        // As the OCTET_STRING is absorbed by the Application tag,
443                        // we have to store the L and V separately
444                        if ( ( getCookie() == null ) || ( getCookie().length == 0 ) )
445                        {
446                            buffer.put( ( byte ) 0 );
447                        }
448                        else
449                        {
450                            buffer.put( TLV.getBytes( getCookie().length ) );
451                            buffer.put( getCookie() );
452                        }
453
454                        break;
455
456                    case REFRESH_DELETE:
457                        // The second case : refreshDelete
458                        buffer.put( ( byte ) SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() );
459                        buffer.put( TLV.getBytes( syncInfoValueLength ) );
460
461                        // The cookie, if any
462                        if ( getCookie() != null )
463                        {
464                            BerValue.encode( buffer, getCookie() );
465                        }
466
467                        // The refreshDone flag
468                        if ( !isRefreshDone() )
469                        {
470                            BerValue.encode( buffer, isRefreshDone() );
471                        }
472
473                        break;
474
475                    case REFRESH_PRESENT:
476                        // The third case : refreshPresent
477                        buffer.put( ( byte ) SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() );
478                        buffer.put( TLV.getBytes( syncInfoValueLength ) );
479
480                        // The cookie, if any
481                        if ( getCookie() != null )
482                        {
483                            BerValue.encode( buffer, getCookie() );
484                        }
485
486                        // The refreshDone flag
487                        if ( !isRefreshDone() )
488                        {
489                            BerValue.encode( buffer, isRefreshDone() );
490                        }
491
492                        break;
493
494                    case SYNC_ID_SET:
495                        // The last case : syncIdSet
496                        buffer.put( ( byte ) SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() );
497                        buffer.put( TLV.getBytes( syncInfoValueLength ) );
498
499                        // The cookie, if any
500                        if ( getCookie() != null )
501                        {
502                            BerValue.encode( buffer, getCookie() );
503                        }
504
505                        // The refreshDeletes flag if not false
506                        if ( isRefreshDeletes() )
507                        {
508                            BerValue.encode( buffer, isRefreshDeletes() );
509                        }
510
511                        // The syncUUIDs
512                        buffer.put( UniversalTag.SET.getValue() );
513                        buffer.put( TLV.getBytes( syncUUIDsLength ) );
514
515                        // Loop on the UUIDs if any
516                        if ( getSyncUUIDs().size() != 0 )
517                        {
518                            for ( byte[] syncUUID : getSyncUUIDs() )
519                            {
520                                BerValue.encode( buffer, syncUUID );
521                            }
522                        }
523                }
524
525                value = buffer.array();
526            }
527            catch ( EncoderException e )
528            {
529                return null;
530            }
531        }
532
533        return value;
534    }
535
536
537    /**
538     * {@inheritDoc}
539     */
540    public Asn1Object decode( byte[] controlBytes ) throws DecoderException
541    {
542        ByteBuffer bb = ByteBuffer.wrap( controlBytes );
543        SyncInfoValueContainer container = new SyncInfoValueContainer( getCodecService(), this );
544        decoder.decode( bb, container );
545        return this;
546    }
547
548
549    /**
550     * @see Object#toString()
551     */
552    public String toString()
553    {
554        return getDecorated().toString();
555    }
556}