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     */
020    package org.apache.directory.shared.ldap.codec.controls.replication.syncInfoValue;
021    
022    import java.nio.ByteBuffer;
023    import java.util.ArrayList;
024    import java.util.List;
025    
026    import org.apache.directory.shared.asn1.AbstractAsn1Object;
027    import org.apache.directory.shared.asn1.ber.tlv.TLV;
028    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
029    import org.apache.directory.shared.asn1.ber.tlv.Value;
030    import org.apache.directory.shared.asn1.codec.EncoderException;
031    import org.apache.directory.shared.ldap.message.control.replication.SynchronizationInfoEnum;
032    import org.apache.directory.shared.ldap.util.StringTools;
033    
034    /**
035     * A syncInfoValue object, as defined in RFC 4533
036     * 
037     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038     * @version $Rev:$, $Date: 
039     */
040    public class SyncInfoValueControlCodec extends AbstractAsn1Object
041    {
042        /** The kind of syncInfoValue we are dealing with */
043        private SynchronizationInfoEnum type;
044        
045        /** The cookie */
046        private byte[] cookie;
047        
048        /** The refreshDone flag if we are dealing with refreshXXX syncInfo. Default to true */
049        private boolean refreshDone = true;
050        
051        /** The refreshDeletes flag if we are dealing with syncIdSet syncInfo. Defaluts to false */
052        private boolean refreshDeletes = false;
053        
054        /** The list of UUIDs if we are dealing with syncIdSet syncInfo */
055        private List<byte[]> syncUUIDs;
056        
057        /** The syncUUIDs cumulative lentgh */
058        private int syncUUIDsLength;
059        
060        
061        /**
062         * The constructor for this codec.
063         * @param type The kind of syncInfo we will store. Can be newCookie, 
064         * refreshPresent, refreshDelete or syncIdSet
065         */
066        public SyncInfoValueControlCodec( SynchronizationInfoEnum type )
067        {
068            this.type = type;
069            
070            // Initialize the arrayList if needed
071            if ( type == SynchronizationInfoEnum.SYNC_ID_SET )
072            {
073                syncUUIDs = new ArrayList<byte[]>();
074            }
075        }
076        
077        
078        /** The global length for this control */
079        private int syncInfoValueLength;
080    
081        /**
082         * Get the control type.
083         * 
084         * @return the type : one of newCookie, refreshDelete, refreshPresent or syncIdSet
085         */
086        public SynchronizationInfoEnum getType()
087        {
088            return type;
089        }
090    
091        
092        /**
093         * @param syncMode the syncMode to set
094         */
095        public void setType( SynchronizationInfoEnum type )
096        {
097            this.type = type;
098        }
099    
100        
101        /**
102         * @return the cookie
103         */
104        public byte[] getCookie()
105        {
106            return cookie;
107        }
108    
109        
110        /**
111         * @param cookie the cookie to set
112         */
113        public void setCookie( byte[] cookie )
114        {
115            this.cookie = cookie;
116        }
117    
118    
119        /**
120         * @return the refreshDone
121         */
122        public boolean isRefreshDone()
123        {
124            return refreshDone;
125        }
126    
127    
128        /**
129         * @param refreshDone the refreshDone to set
130         */
131        public void setRefreshDone( boolean refreshDone )
132        {
133            this.refreshDone = refreshDone;
134        }
135    
136    
137        /**
138         * @return the refreshDeletes
139         */
140        public boolean isRefreshDeletes()
141        {
142            return refreshDeletes;
143        }
144    
145    
146        /**
147         * @param refreshDeletes the refreshDeletes to set
148         */
149        public void setRefreshDeletes( boolean refreshDeletes )
150        {
151            this.refreshDeletes = refreshDeletes;
152        }
153    
154    
155        /**
156         * @return the syncUUIDs
157         */
158        public List<byte[]> getSyncUUIDs()
159        {
160            return syncUUIDs;
161        }
162    
163    
164        /**
165         * @param syncUUIDs the syncUUIDs to set
166         */
167        public void setSyncUUIDs( List<byte[]> syncUUIDs )
168        {
169            this.syncUUIDs = syncUUIDs;
170        }
171    
172        
173        /**
174         * Compute the SyncInfoValue length.
175         * 
176         * SyncInfoValue :
177         * 
178         * 0xA0 L1 abcd                   // newCookie
179         * 0xA1 L2                        // refreshDelete
180         *   |
181         *  [+--> 0x04 L3 abcd]           // cookie
182         *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDone
183         * 0xA2 L4                        // refreshPresent
184         *   |
185         *  [+--> 0x04 L5 abcd]           // cookie
186         *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDone
187         * 0xA3 L6                        // syncIdSet
188         *   |
189         *  [+--> 0x04 L7 abcd]           // cookie
190         *  [+--> 0x01 0x01 (0x00|0xFF)   // refreshDeletes
191         *   +--> 0x31 L8                 // SET OF syncUUIDs
192         *          |
193         *         [+--> 0x04 L9 abcd]    // syncUUID    public static final int AND_FILTER_TAG = 0xA0;
194    
195        public static final int OR_FILTER_TAG = 0xA1;
196    
197        public static final int NOT_FILTER_TAG = 0xA2;
198    
199        public static final int BIND_REQUEST_SASL_TAG = 0xA3;
200    
201         */
202        public int computeLength()
203        {
204            // The mode length
205            syncInfoValueLength = 0;
206            
207            switch ( type )
208            {
209                case NEW_COOKIE :
210                    if ( cookie != null )
211                    {
212                        syncInfoValueLength = 1 + TLV.getNbBytes( cookie.length ) + cookie.length;
213                    }
214                    else
215                    {
216                        syncInfoValueLength = 1 + 1;
217                    }
218                    
219                    return syncInfoValueLength;
220                    
221                case REFRESH_DELETE :
222                case REFRESH_PRESENT :
223                    if ( cookie != null )
224                    {
225                        syncInfoValueLength = 1 + TLV.getNbBytes( cookie.length ) + cookie.length;
226                    }
227                    
228                    // The refreshDone flag
229                    syncInfoValueLength += 1 + 1 + 1;
230                    break;
231                    
232                case SYNC_ID_SET :
233                    if ( cookie != null )
234                    {
235                        syncInfoValueLength = 1 + TLV.getNbBytes( cookie.length ) + cookie.length;
236                    }
237                    
238                    // The refreshDeletes flag
239                    syncInfoValueLength += 1 + 1 + 1;
240    
241                    // The syncUUIDs if any
242                    syncUUIDsLength = 0;
243    
244                    if ( syncUUIDs.size() != 0 )
245                    {
246                        for ( byte[] syncUUID:syncUUIDs )
247                        {
248                            int uuidLength = 1 + TLV.getNbBytes( syncUUID.length ) + syncUUID.length;
249                            
250                            syncUUIDsLength += 1 + TLV.getNbBytes( uuidLength ) + uuidLength;
251                        }
252                        
253                        // Add the tag and compute the length
254                        syncUUIDsLength += 1 + TLV.getNbBytes( syncUUIDsLength ) + syncUUIDsLength;
255                    }
256                    
257                    syncInfoValueLength += 1 + TLV.getNbBytes( syncUUIDsLength ) + syncUUIDsLength;
258    
259                    break;
260            }
261            
262            return 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength;
263        }
264        
265        
266        /**
267         * Encode the SyncInfoValue control
268         * 
269         * @param buffer The encoded sink
270         * @return A ByteBuffer that contains the encoded PDU
271         * @throws EncoderException If anything goes wrong.
272         */
273        public ByteBuffer encode( ByteBuffer bb ) throws EncoderException
274        {
275            // Allocate the bytes buffer.
276            if ( bb == null )
277            {
278                bb = ByteBuffer.allocate( computeLength() );
279            }
280            
281            switch ( type )
282            {
283                case NEW_COOKIE :
284                    // The first case : newCookie
285                    bb.put( (byte)SyncInfoValueTags.NEW_COOKIE_TAG.getValue() );
286    
287                    // As the OCTET_STRING is absorbed by the Application tag,
288                    // we have to store the L and V separately
289                    if ( ( cookie == null ) || ( cookie.length == 0 ) )
290                    {
291                        bb.put( ( byte ) 0 );
292                    }
293                    else
294                    {
295                        bb.put( TLV.getBytes( cookie.length ) );
296                        bb.put( cookie );
297                    }
298    
299                    break;
300                    
301                case REFRESH_DELETE :
302                    // The second case : refreshDelete
303                    bb.put( (byte)SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() );
304                    bb.put( TLV.getBytes( syncInfoValueLength ) );
305    
306                    // The cookie, if any
307                    if ( cookie != null )
308                    {
309                        Value.encode( bb, cookie );
310                    }
311                    
312                    // The refreshDone flag
313                    Value.encode( bb, refreshDone );
314                    break;
315                    
316                case REFRESH_PRESENT :
317                    // The third case : refreshPresent
318                    bb.put( (byte)SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() );
319                    bb.put( TLV.getBytes( syncInfoValueLength ) );
320    
321                    // The cookie, if any
322                    if ( cookie != null )
323                    {
324                        Value.encode( bb, cookie );
325                    }
326                    
327                    // The refreshDone flag
328                    Value.encode( bb, refreshDone );
329                    break;
330                    
331                case SYNC_ID_SET :
332                    // The last case : syncIdSet
333                    bb.put( (byte)SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() );
334                    bb.put( TLV.getBytes( syncInfoValueLength ) );
335    
336                    // The cookie, if any
337                    if ( cookie != null )
338                    {
339                        Value.encode( bb, cookie );
340                    }
341                    
342                    // The refreshDeletes flag
343                    Value.encode( bb, refreshDeletes );
344                    
345                    // The syncUUIDs
346                    bb.put( UniversalTag.SET_TAG );
347                    bb.put( TLV.getBytes( syncUUIDsLength ) );
348                    
349                    // Loop on the UUIDs if any
350                    if ( syncUUIDs.size() != 0 )
351                    {
352                        for ( byte[] syncUUID:syncUUIDs )
353                        {
354                            Value.encode( bb , syncUUID );
355                        }
356                    }
357            }
358    
359            return bb;
360        }
361        
362        
363        /**
364         * @see Object#toString()
365         */
366        public String toString()
367        {
368            StringBuilder sb = new StringBuilder();
369            
370            sb.append( "    SyncInfoValue control :\n" );
371            
372            switch ( type )
373            {
374                case NEW_COOKIE :
375                    sb.append( "        newCookie : '" ).
376                        append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
377                    break;
378                    
379                case REFRESH_DELETE :
380                    sb.append( "        refreshDelete : \n" );
381                    
382                    if ( cookie != null )
383                    {
384                        sb.append( "            cookie : '" ).
385                            append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
386                    }
387                    
388                    sb.append( "            refreshDone : " ).append(  refreshDone ).append( '\n' );
389                    break;
390                    
391                case REFRESH_PRESENT :
392                    sb.append( "        refreshPresent : \n" );
393                    
394                    if ( cookie != null )
395                    {
396                        sb.append( "            cookie : '" ).
397                            append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
398                    }
399                    
400                    sb.append( "            refreshDone : " ).append(  refreshDone ).append( '\n' );
401                    break;
402                    
403                case SYNC_ID_SET :
404                    sb.append( "        syncIdSet : \n" );
405                    
406                    if ( cookie != null )
407                    {
408                        sb.append( "            cookie : '" ).
409                            append( StringTools.dumpBytes( cookie ) ).append( "'\n" );
410                    }
411                    
412                    sb.append( "            refreshDeletes : " ).append(  refreshDeletes ).append( '\n' );
413                    sb.append(  "            syncUUIDS : " );
414    
415                    if ( syncUUIDs.size() != 0 )
416                    {
417                        boolean isFirst = true;
418                        
419                        for ( byte[] syncUUID:syncUUIDs )
420                        {
421                            if ( isFirst )
422                            {
423                                isFirst = false;
424                            }
425                            else
426                            {
427                                sb.append( ", " );
428                            }
429                            
430                            sb.append( syncUUID );
431                        }
432                        
433                        sb.append( '\n' );
434                    }
435                    else
436                    {
437                        sb.append(  "empty\n" );
438                    }
439                    
440                    break;
441            }
442            
443            return sb.toString();
444        }
445    }