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.server.kerberos.shared.messages.value;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.List;
028    
029    import org.apache.directory.shared.asn1.AbstractAsn1Object;
030    import org.apache.directory.shared.asn1.ber.tlv.TLV;
031    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
032    import org.apache.directory.shared.asn1.codec.EncoderException;
033    import org.apache.directory.shared.ldap.util.StringTools;
034    import org.slf4j.Logger;
035    import org.slf4j.LoggerFactory;
036    
037    
038    /**
039     * Store a list of addresses.
040     * 
041     * The ASN.1 grammar is :
042     * 
043     * -- NOTE: HostAddresses is always used as an OPTIONAL field and
044     * -- should not be empty.
045     * HostAddresses   -- NOTE: subtly different from rfc1510,
046     *                 -- but has a value mapping and encodes the same
047     *         ::= SEQUENCE OF HostAddress
048     *
049     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050     * @version $Rev: 642496 $, $Date: 2008-03-29 05:09:22 +0200 (Sat, 29 Mar 2008) $
051     */
052    public class HostAddresses extends AbstractAsn1Object
053    {
054        /** The logger */
055        private static final Logger LOG = LoggerFactory.getLogger( HostAddresses.class );
056    
057        /** Speedup for logs */
058        private static final boolean IS_DEBUG = LOG.isDebugEnabled();
059    
060        /** List of all HostAddress stored */
061        private List<HostAddress> addresses;
062    
063        // Storage for computed lengths
064        private transient int addressesLength;
065    
066    
067        /**
068         * Creates a new instance of HostAddresses.
069         */
070        public HostAddresses()
071        {
072            this.addresses = new ArrayList<HostAddress>();
073        }
074        
075    
076        /**
077         * Creates a new instance of HostAddresses.
078         *
079         * @param addresses The associated addresses
080         */
081        public HostAddresses( HostAddress[] addresses )
082        {
083            if ( addresses == null )
084            {
085                this.addresses = new ArrayList<HostAddress>();
086            }
087            else
088            {
089                this.addresses = Arrays.asList( addresses );
090            }
091        }
092    
093    
094        public void addHostAddress( HostAddress hostAddress )
095        {
096            addresses.add( hostAddress );
097        }
098    
099    
100        /**
101         * Returns true if this {@link HostAddresses} contains a specified {@link HostAddress}.
102         *
103         * @param address
104         * @return true if this {@link HostAddresses} contains a specified {@link HostAddress}.
105         */
106        public boolean contains( HostAddress address )
107        {
108            if ( addresses != null )
109            {
110                return addresses.contains( address );
111            }
112    
113            return false;
114        }
115    
116    
117        /**
118         * Returns true if two {@link HostAddresses} are equal.
119         *
120         * @param that
121         * @return true if two {@link HostAddresses} are equal.
122         */
123        public boolean equals( HostAddresses that )
124        {
125            if ( ( addresses == null && that.addresses != null )
126                || ( addresses != null && that.addresses == null ) )
127            {
128                return false;
129            }
130    
131            if ( addresses != null && that.addresses != null )
132            {
133                if ( addresses.size() != that.addresses.size() )
134                {
135                    return false;
136                }
137    
138                HostAddress[] thisHostAddresses = ( HostAddress[] ) addresses.toArray();
139                HostAddress[] thatHostAddresses = ( HostAddress[] ) that.addresses.toArray();
140    
141                for ( int i = 0; i < thisHostAddresses.length; i++ )
142                {
143                    if ( !thisHostAddresses[i].equals( thatHostAddresses[i] ) )
144                    {
145                        return false;
146                    }
147                }
148            }
149    
150            return true;
151        }
152    
153    
154        /**
155         * Returns the contained {@link HostAddress}s as an array.
156         *
157         * @return An array of {@link HostAddress}s.
158         */
159        public HostAddress[] getAddresses()
160        {
161            return ( HostAddress[] ) addresses.toArray();
162        }
163    
164    
165        /**
166         * Compute the hostAddresses length
167         * 
168         * HostAddresses :
169         * 
170         * 0x30 L1 hostAddresses sequence of HostAddresses
171         *  |
172         *  +--> 0x30 L2[1] Hostaddress[1]
173         *  |
174         *  +--> 0x30 L2[2] Hostaddress[2]
175         *  |
176         *  ...
177         *  |
178         *  +--> 0x30 L2[n] Hostaddress[n]
179         *        
180         *  where L1 = sum( L2[1], l2[2], ..., L2[n] )
181         */
182        public int computeLength()
183        {
184            // Compute the addresses length.
185            addressesLength = 0;
186    
187            if ( ( addresses != null ) && ( addresses.size() != 0 ) )
188            {
189                for ( HostAddress hostAddress : addresses )
190                {
191                    int length = hostAddress.computeLength();
192                    addressesLength += length;
193                }
194            }
195    
196            return 1 + TLV.getNbBytes( addressesLength ) + addressesLength;
197        }
198    
199    
200        /**
201         * Encode the HostAddress message to a PDU. 
202         * 
203         * HostAddress :
204         * 
205         * 0x30 LL
206         *   0x30 LL hostaddress[1] 
207         *   0x30 LL hostaddress[1]
208         *   ... 
209         *   0x30 LL hostaddress[1] 
210         * 
211         * @param buffer The buffer where to put the PDU. It should have been allocated
212         * before, with the right size.
213         * @return The constructed PDU.
214         */
215        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
216        {
217            if ( buffer == null )
218            {
219                throw new EncoderException( "Cannot put a PDU in a null buffer !" );
220            }
221    
222            try
223            {
224                // The HostAddresses SEQ Tag
225                buffer.put( UniversalTag.SEQUENCE_TAG );
226                buffer.put( TLV.getBytes( addressesLength ) );
227    
228                // The hostAddress list, if it's not empty
229                if ( ( addresses != null ) && ( addresses.size() != 0 ) )
230                {
231                    for ( HostAddress hostAddress : addresses )
232                    {
233                        hostAddress.encode( buffer );
234                    }
235                }
236            }
237            catch ( BufferOverflowException boe )
238            {
239                LOG.error(
240                    "Cannot encode the HostAddresses object, the PDU size is {} when only {} bytes has been allocated", 1
241                        + TLV.getNbBytes( addressesLength ) + addressesLength, buffer.capacity() );
242                throw new EncoderException( "The PDU buffer size is too small !" );
243            }
244    
245            if ( IS_DEBUG )
246            {
247                LOG.debug( "HostAddresses encoding : {}", StringTools.dumpBytes( buffer.array() ) );
248                LOG.debug( "HostAddresses initial value : {}", toString() );
249            }
250    
251            return buffer;
252        }
253    
254    
255        /**
256         * @see Object#toString()
257         */
258        public String toString()
259        {
260            StringBuilder sb = new StringBuilder();
261            boolean isFirst = true;
262    
263            for ( HostAddress hostAddress : addresses )
264            {
265                if ( isFirst )
266                {
267                    isFirst = false;
268                }
269                else
270                {
271                    sb.append( ", " );
272                }
273    
274                sb.append( hostAddress.toString() );
275            }
276    
277            return sb.toString();
278        }
279    }