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.codec.controls.search.entryChange;
021
022
023import java.nio.ByteBuffer;
024
025import org.apache.directory.api.asn1.Asn1Object;
026import org.apache.directory.api.asn1.DecoderException;
027import org.apache.directory.api.asn1.EncoderException;
028import org.apache.directory.api.asn1.ber.Asn1Decoder;
029import org.apache.directory.api.asn1.ber.tlv.BerValue;
030import org.apache.directory.api.asn1.ber.tlv.TLV;
031import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
032import org.apache.directory.api.i18n.I18n;
033import org.apache.directory.api.ldap.codec.api.ControlDecorator;
034import org.apache.directory.api.ldap.codec.api.LdapApiService;
035import org.apache.directory.api.ldap.model.message.controls.ChangeType;
036import org.apache.directory.api.ldap.model.message.controls.EntryChange;
037import org.apache.directory.api.ldap.model.message.controls.EntryChangeImpl;
038import org.apache.directory.api.ldap.model.name.Dn;
039import org.apache.directory.api.util.Strings;
040
041
042/**
043 * An EntryChange implementation, that wraps and decorates the Control with codec
044 * specific functionality.
045 *
046 *
047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048 */
049public class EntryChangeDecorator extends ControlDecorator<EntryChange> implements EntryChange
050{
051    /** Default value when no change number is provided */
052    public static final int UNDEFINED_CHANGE_NUMBER = -1;
053
054    /** A temporary storage for the previous Dn */
055    private byte[] previousDnBytes = null;
056
057    /** The entry change global length */
058    private int eccSeqLength;
059
060    /** An instance of this decoder */
061    private static final Asn1Decoder DECODER = new Asn1Decoder();
062
063
064    /**
065     * Creates a new instance of EntryChangeDecoder wrapping a newly created
066     * EntryChange Control object.
067     * 
068     * @param codec The LDAP service instance
069     */
070    public EntryChangeDecorator( LdapApiService codec )
071    {
072        super( codec, new EntryChangeImpl() );
073    }
074
075
076    /**
077     * Creates a new instance of EntryChangeDecorator wrapping the supplied
078     * EntryChange Control.
079     *
080     * @param codec The LDAP service instance
081     * @param control The EntryChange Control to be decorated.
082     */
083    public EntryChangeDecorator( LdapApiService codec, EntryChange control )
084    {
085        super( codec, control );
086    }
087
088
089    /**
090     * Internally used to not have to cast the decorated Control.
091     *
092     * @return the decorated Control.
093     */
094    private EntryChange getEntryChange()
095    {
096        return getDecorated();
097    }
098
099
100    /**
101     * Compute the EntryChangeControl length 
102     * 
103     * <pre>
104     * 0x30 L1 
105     *   | 
106     *   +--&gt; 0x0A 0x0(1-4) [1|2|4|8] (changeType) 
107     *  [+--&gt; 0x04 L2 previousDN] 
108     *  [+--&gt; 0x02 0x0(1-4) [0..2^63-1] (changeNumber)]
109     *  </pre>
110     *  
111     * @return the control length.
112     */
113    @Override
114    public int computeLength()
115    {
116        int changeTypesLength = 1 + 1 + 1;
117
118        int previousDnLength = 0;
119        int changeNumberLength = 0;
120
121        if ( getPreviousDn() != null )
122        {
123            previousDnBytes = Strings.getBytesUtf8( getPreviousDn().getName() );
124            previousDnLength = 1 + TLV.getNbBytes( previousDnBytes.length ) + previousDnBytes.length;
125        }
126
127        if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER )
128        {
129            changeNumberLength = 1 + 1 + BerValue.getNbBytes( getChangeNumber() );
130        }
131
132        eccSeqLength = changeTypesLength + previousDnLength + changeNumberLength;
133        valueLength = 1 + TLV.getNbBytes( eccSeqLength ) + eccSeqLength;
134
135        return valueLength;
136    }
137
138
139    /**
140     * Encodes the entry change control.
141     * 
142     * @param buffer The encoded sink
143     * @return A ByteBuffer that contains the encoded PDU
144     * @throws EncoderException If anything goes wrong.
145     */
146    @Override
147    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
148    {
149        if ( buffer == null )
150        {
151            throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
152        }
153
154        buffer.put( UniversalTag.SEQUENCE.getValue() );
155        buffer.put( TLV.getBytes( eccSeqLength ) );
156
157        buffer.put( UniversalTag.ENUMERATED.getValue() );
158        buffer.put( ( byte ) 1 );
159        buffer.put( BerValue.getBytes( getChangeType().getValue() ) );
160
161        if ( getPreviousDn() != null )
162        {
163            BerValue.encode( buffer, previousDnBytes );
164        }
165
166        if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER )
167        {
168            BerValue.encode( buffer, getChangeNumber() );
169        }
170
171        return buffer;
172    }
173
174
175    /**
176     * {@inheritDoc}
177     */
178    @Override
179    public byte[] getValue()
180    {
181        if ( value == null )
182        {
183            try
184            {
185                computeLength();
186                ByteBuffer buffer = ByteBuffer.allocate( valueLength );
187
188                buffer.put( UniversalTag.SEQUENCE.getValue() );
189                buffer.put( TLV.getBytes( eccSeqLength ) );
190
191                buffer.put( UniversalTag.ENUMERATED.getValue() );
192                buffer.put( ( byte ) 1 );
193                buffer.put( BerValue.getBytes( getChangeType().getValue() ) );
194
195                if ( getPreviousDn() != null )
196                {
197                    BerValue.encode( buffer, previousDnBytes );
198                }
199
200                if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER )
201                {
202                    BerValue.encode( buffer, getChangeNumber() );
203                }
204
205                value = buffer.array();
206            }
207            catch ( Exception e )
208            {
209                return null;
210            }
211        }
212
213        return value;
214    }
215
216
217    /**
218     * {@inheritDoc}
219     */
220    @Override
221    public ChangeType getChangeType()
222    {
223        return getEntryChange().getChangeType();
224    }
225
226
227    /**
228     * {@inheritDoc}
229     */
230    @Override
231    public void setChangeType( ChangeType changeType )
232    {
233        getEntryChange().setChangeType( changeType );
234    }
235
236
237    /**
238     * {@inheritDoc}
239     */
240    @Override
241    public Dn getPreviousDn()
242    {
243        return getEntryChange().getPreviousDn();
244    }
245
246
247    /**
248     * {@inheritDoc}
249     */
250    @Override
251    public void setPreviousDn( Dn previousDn )
252    {
253        getEntryChange().setPreviousDn( previousDn );
254    }
255
256
257    /**
258     * {@inheritDoc}
259     */
260    @Override
261    public long getChangeNumber()
262    {
263        return getEntryChange().getChangeNumber();
264    }
265
266
267    /**
268     * {@inheritDoc}
269     */
270    @Override
271    public void setChangeNumber( long changeNumber )
272    {
273        getEntryChange().setChangeNumber( changeNumber );
274    }
275
276
277    /**
278     * {@inheritDoc}
279     */
280    @Override
281    public Asn1Object decode( byte[] controlBytes ) throws DecoderException
282    {
283        ByteBuffer bb = ByteBuffer.wrap( controlBytes );
284        EntryChangeContainer container = new EntryChangeContainer( getCodecService(), this );
285        DECODER.decode( bb, container );
286        return this;
287    }
288}