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 ETYPE-INFO2.
041 * 
042 * The ASN.1 grammar is :
043 * <pre>
044 * ETYPE-INFO2              ::= SEQUENCE SIZE (1..MAX) OF ETYPE-INFO2-ENTRY
045 *</pre>
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 */
048public class ETypeInfo2 implements Asn1Object
049{
050    /** The logger */
051    private static final Logger LOG = LoggerFactory.getLogger( ETypeInfo2.class );
052
053    /** Speedup for logs */
054    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
055
056    /** List of all ETYPE-INFO2-ENTRY stored */
057    private List<ETypeInfo2Entry> etypeInfo2Entries;
058
059    // Storage for computed lengths
060    private int etypeInfo2Length;
061
062
063    /**
064     * Creates a new instance of ETypeInfo2.
065     */
066    public ETypeInfo2()
067    {
068        this.etypeInfo2Entries = new ArrayList<>();
069    }
070
071
072    /**
073     * Creates a new instance of ETypeInfo2.
074     *
075     * @param etypeInfo2Entries The associated etypeInfo2Entries
076     */
077    public ETypeInfo2( ETypeInfo2Entry[] etypeInfo2Entries )
078    {
079        if ( etypeInfo2Entries == null )
080        {
081            this.etypeInfo2Entries = new ArrayList<>();
082        }
083        else
084        {
085            this.etypeInfo2Entries = Arrays.asList( etypeInfo2Entries );
086        }
087    }
088
089
090    /**
091     * Adds an {@link ETypeInfo2Entry} to the list
092     * @param etypeInfo2Entry The ETypeInfo2Entry to add
093     */
094    public void addETypeInfo2Entry( ETypeInfo2Entry etypeInfo2Entry )
095    {
096        etypeInfo2Entries.add( etypeInfo2Entry );
097    }
098
099
100    /**
101     * Returns true if this {@link ETypeInfo2Entry} contains a specified {@link ETypeInfo2Entry}.
102     *
103     * @param etypeInfo2Entry The etypeInfo2Entry we are looking for in the existing list
104     * @return true if this {@link ETypeInfo2Entry} contains a specified {@link ETypeInfo2Entry}.
105     */
106    public boolean contains( ETypeInfo2Entry etypeInfo2Entry )
107    {
108        if ( etypeInfo2Entries != null )
109        {
110            return etypeInfo2Entries.contains( etypeInfo2Entry );
111        }
112
113        return false;
114    }
115
116
117    /**
118     * {@inheritDoc}
119     */
120    @Override
121    public int hashCode()
122    {
123        int hash = 37;
124
125        if ( etypeInfo2Entries != null )
126        {
127            hash = hash * 17 + etypeInfo2Entries.size();
128
129            for ( ETypeInfo2Entry etypeInfo2Entry : etypeInfo2Entries )
130            {
131                hash = hash * 17 + etypeInfo2Entry.hashCode();
132            }
133        }
134
135        return hash;
136    }
137
138
139    /**
140     * Returns true if two {@link ETypeInfo2} are equal.
141     *
142     * @param that The {@link ETypeInfo2} we want to compare with the current one
143     * @return true if two {@link ETypeInfo2} are equal.
144     */
145    public boolean equals( ETypeInfo2 that )
146    {
147        if ( that == null )
148        {
149            return false;
150        }
151
152        // infoEntries can't be null after creation
153        if ( etypeInfo2Entries.size() != that.etypeInfo2Entries.size() )
154        {
155            return false;
156        }
157
158        for ( int i = 0; i < etypeInfo2Entries.size(); i++ )
159        {
160            if ( !etypeInfo2Entries.get( i ).equals( that.etypeInfo2Entries.get( i ) ) )
161            {
162                return false;
163            }
164        }
165
166        return true;
167    }
168
169
170    /**
171     * Returns the contained {@link ETypeInfo2Entry}s as an array.
172     *
173     * @return An array of {@link ETypeInfo2Entry}s.
174     */
175    public ETypeInfo2Entry[] getETypeInfo2Entries()
176    {
177        return etypeInfo2Entries.toArray( new ETypeInfo2Entry[0] );
178    }
179
180
181    /**
182     * Compute the ETypeInfo2 length
183     * <pre>
184     * ETypeInfo2 :
185     * 
186     * 0x30 L1 ETypeInfo2 sequence of ETypeInfo2Entry
187     *  |
188     *  +--&gt; 0x30 L2[1] ETypeInfo2Entry[1]
189     *  |
190     *  +--&gt; 0x30 L2[2] ETypeInfo2Entry[2]
191     *  |
192     *  ...
193     *  |
194     *  +--&gt; 0x30 L2[n] ETypeInfo2Entry[n]
195     *        
196     *  where L1 = sum( L2[1], l2[2], ..., L2[n] )
197     * </pre>
198     */
199    public int computeLength()
200    {
201        // Compute the ETypeInfo2 length.
202        etypeInfo2Length = 0;
203
204        if ( ( etypeInfo2Entries != null ) && !etypeInfo2Entries.isEmpty() )
205        {
206            for ( ETypeInfo2Entry info2Entry : etypeInfo2Entries )
207            {
208                int length = info2Entry.computeLength();
209                etypeInfo2Length += length;
210            }
211        }
212
213        return 1 + TLV.getNbBytes( etypeInfo2Length ) + etypeInfo2Length;
214    }
215
216
217    /**
218     * Encode the ETypeInfo2 message to a PDU. 
219     * <pre>
220     * ETypeInfo2 :
221     * 
222     * 0x30 LL
223     *   0x30 LL ETypeInfo2Entry[1] 
224     *   0x30 LL ETypeInfo2Entry[1]
225     *   ... 
226     *   0x30 LL ETypeInfo2Entry[1] 
227     * </pre>
228     * @param buffer The buffer where to put the PDU. It should have been allocated
229     * before, with the right size.
230     * @return The constructed PDU.
231     */
232    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
233    {
234        if ( buffer == null )
235        {
236            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
237        }
238
239        try
240        {
241            // The ETypeInfo2Entry SEQ Tag
242            buffer.put( UniversalTag.SEQUENCE.getValue() );
243            buffer.put( TLV.getBytes( etypeInfo2Length ) );
244
245            // The ETypeInfo2Entry list, if it's not empty
246            if ( ( etypeInfo2Entries != null ) && !etypeInfo2Entries.isEmpty() )
247            {
248                for ( ETypeInfo2Entry info2Entry : etypeInfo2Entries )
249                {
250                    info2Entry.encode( buffer );
251                }
252            }
253        }
254        catch ( BufferOverflowException boe )
255        {
256            LOG.error( I18n.err( I18n.ERR_144, 1 + TLV.getNbBytes( etypeInfo2Length )
257                + etypeInfo2Length, buffer.capacity() ) );
258            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
259        }
260
261        if ( IS_DEBUG )
262        {
263            LOG.debug( "ETYPE-INFO encoding : {}", Strings.dumpBytes( buffer.array() ) );
264            LOG.debug( "ETYPE-INFO initial value : {}", this );
265        }
266
267        return buffer;
268    }
269
270
271    /**
272     * @see Object#toString()
273     */
274    public String toString()
275    {
276        StringBuilder sb = new StringBuilder();
277        boolean isFirst = true;
278
279        for ( ETypeInfo2Entry info2Entry : etypeInfo2Entries )
280        {
281            if ( isFirst )
282            {
283                isFirst = false;
284            }
285            else
286            {
287                sb.append( ", " );
288            }
289
290            sb.append( info2Entry.toString() );
291        }
292
293        return sb.toString();
294    }
295}