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.shared.kerberos.components;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.List;
028
029import org.apache.directory.api.asn1.Asn1Object;
030import org.apache.directory.api.asn1.EncoderException;
031import org.apache.directory.api.asn1.ber.tlv.TLV;
032import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
033import org.apache.directory.api.util.Strings;
034import org.apache.directory.server.i18n.I18n;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038
039/**
040 * Store a list of addresses.
041 * 
042 * The ASN.1 grammar is :
043 * <pre>
044 * -- NOTE: HostAddresses is always used as an OPTIONAL field and
045 * -- should not be empty.
046 * HostAddresses   -- NOTE: subtly different from rfc1510,
047 *                 -- but has a value mapping and encodes the same
048 *         ::= SEQUENCE OF HostAddress
049 *</pre>
050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051 */
052public class HostAddresses implements Asn1Object
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 int addressesLength;
065
066
067    /**
068     * Creates a new instance of HostAddresses.
069     */
070    public HostAddresses()
071    {
072        this.addresses = new ArrayList<>();
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<>();
086        }
087        else
088        {
089            this.addresses = Arrays.asList( addresses );
090        }
091    }
092
093
094    /**
095     * Adds an {@link HostAddresses} to the list
096     * @param hostAddress The address to add
097     */
098    public void addHostAddress( HostAddress hostAddress )
099    {
100        addresses.add( hostAddress );
101    }
102
103
104    /**
105     * Returns true if this {@link HostAddresses} contains a specified {@link HostAddress}.
106     *
107     * @param address The address we are looking for in the existing list
108     * @return true if this {@link HostAddresses} contains a specified {@link HostAddress}.
109     */
110    public boolean contains( HostAddress address )
111    {
112        if ( addresses != null )
113        {
114            return addresses.contains( address );
115        }
116
117        return false;
118    }
119
120
121    /**
122     * {@inheritDoc}
123     */
124    @Override
125    public int hashCode()
126    {
127        int hash = 37;
128
129        if ( addresses != null )
130        {
131            hash = hash * 17 + addresses.size();
132            hash = hash * 17 + addresses.hashCode();
133        }
134
135        return hash;
136    }
137
138
139    /**
140     * Returns true if two {@link HostAddresses} are equal.
141     *
142     * @param obj The {@link HostAddresses} we want to compare with the current one
143     * @return true if two {@link HostAddresses} are equal.
144     */
145    @Override
146    public boolean equals( Object obj )
147    {
148        if ( this == obj )
149        {
150            return true;
151        }
152
153        if ( !( obj instanceof HostAddresses ) )
154        {
155            return false;
156        }
157
158        HostAddresses that = ( HostAddresses ) obj;
159
160        // Addresses can't be null after creation
161        if ( addresses.size() != that.addresses.size() )
162        {
163            return false;
164        }
165
166        for ( int i = 0; i < addresses.size(); i++ )
167        {
168            if ( !addresses.get( i ).equals( that.addresses.get( i ) ) )
169            {
170                return false;
171            }
172        }
173
174        return true;
175    }
176
177
178    /**
179     * Returns the contained {@link HostAddress}s as an array.
180     *
181     * @return An array of {@link HostAddress}s.
182     */
183    public HostAddress[] getAddresses()
184    {
185        return addresses.toArray( new HostAddress[0] );
186    }
187
188
189    /**
190     * Compute the hostAddresses length
191     * <pre>
192     * HostAddresses :
193     * 
194     * 0x30 L1 hostAddresses sequence of HostAddresses
195     *  |
196     *  +--&gt; 0x30 L2[1] Hostaddress[1]
197     *  |
198     *  +--&gt; 0x30 L2[2] Hostaddress[2]
199     *  |
200     *  ...
201     *  |
202     *  +--&gt; 0x30 L2[n] Hostaddress[n]
203     *        
204     *  where L1 = sum( L2[1], l2[2], ..., L2[n] )
205     * </pre>
206     */
207    public int computeLength()
208    {
209        // Compute the addresses length.
210        addressesLength = 0;
211
212        if ( ( addresses != null ) && !addresses.isEmpty() )
213        {
214            for ( HostAddress hostAddress : addresses )
215            {
216                int length = hostAddress.computeLength();
217                addressesLength += length;
218            }
219        }
220
221        return 1 + TLV.getNbBytes( addressesLength ) + addressesLength;
222    }
223
224
225    /**
226     * Encode the HostAddress message to a PDU. 
227     * <pre>
228     * HostAddress :
229     * 
230     * 0x30 LL
231     *   0x30 LL hostaddress[1] 
232     *   0x30 LL hostaddress[1]
233     *   ... 
234     *   0x30 LL hostaddress[1] 
235     * </pre>
236     * @param buffer The buffer where to put the PDU. It should have been allocated
237     * before, with the right size.
238     * @return The constructed PDU.
239     */
240    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
241    {
242        if ( buffer == null )
243        {
244            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
245        }
246
247        try
248        {
249            // The HostAddresses SEQ Tag
250            buffer.put( UniversalTag.SEQUENCE.getValue() );
251            buffer.put( TLV.getBytes( addressesLength ) );
252
253            // The hostAddress list, if it's not empty
254            if ( ( addresses != null ) && !addresses.isEmpty() )
255            {
256                for ( HostAddress hostAddress : addresses )
257                {
258                    hostAddress.encode( buffer );
259                }
260            }
261        }
262        catch ( BufferOverflowException boe )
263        {
264            LOG.error( I18n.err( I18n.ERR_144, 1 + TLV.getNbBytes( addressesLength )
265                + addressesLength, buffer.capacity() ) );
266            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
267        }
268
269        if ( IS_DEBUG )
270        {
271            LOG.debug( "HostAddresses encoding : {}", Strings.dumpBytes( buffer.array() ) );
272            LOG.debug( "HostAddresses initial value : {}", this );
273        }
274
275        return buffer;
276    }
277
278
279    /**
280     * @see Object#toString()
281     */
282    public String toString()
283    {
284        StringBuilder sb = new StringBuilder();
285        boolean isFirst = true;
286
287        for ( HostAddress hostAddress : addresses )
288        {
289            if ( isFirst )
290            {
291                isFirst = false;
292            }
293            else
294            {
295                sb.append( ", " );
296            }
297
298            sb.append( hostAddress.toString() );
299        }
300
301        return sb.toString();
302    }
303}