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.asn1.ber;
021
022
023import java.nio.ByteBuffer;
024
025import org.apache.directory.api.asn1.DecoderException;
026import org.apache.directory.api.asn1.ber.tlv.BerValue;
027import org.apache.directory.api.asn1.ber.tlv.TLV;
028import org.apache.directory.api.asn1.ber.tlv.TLVBerDecoderMBean;
029import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum;
030import org.apache.directory.api.asn1.util.Asn1StringUtils;
031import org.apache.directory.api.i18n.I18n;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035
036/**
037 * A BER TLV Tag component decoder. This decoder instantiate a Tag. The tag
038 * won't be implementations should not copy the handle to the Tag object
039 * delivered but should copy the data if they need it over the long term.
040 * 
041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042*/
043public final class Asn1Decoder implements TLVBerDecoderMBean
044{
045    /** The logger */
046    private static final Logger LOG = LoggerFactory.getLogger( Asn1Decoder.class );
047
048    /** This flag is used to indicate that there are more bytes in the stream */
049    private static final boolean MORE = true;
050
051    /** This flag is used to indicate that there are no more bytes in the stream */
052    private static final boolean END = false;
053
054    /** Flag that is used to allow/disallow the indefinite form of Length */
055    private boolean indefiniteLengthAllowed;
056
057    /** The maximum number of bytes that could be used to encode the Length */
058    private int maxLengthLength;
059
060    /** The maximum number of bytes that could be used to encode the Tag */
061    private int maxTagLength;
062
063
064    /**
065     * A public constructor of an Asn1 Decoder.
066     */
067    private Asn1Decoder()
068    {
069        indefiniteLengthAllowed = false;
070        maxLengthLength = 1;
071        maxTagLength = 1;
072    }
073
074
075    /**
076     * Treat the start of a TLV. It reads the tag and get its value.
077     * 
078     * @param stream The ByteBuffer containing the PDU to decode
079     * @param container The container that stores the current state,
080     * the result and other informations.
081     * @return <code>true</code> if there are more bytes to read, <code>false
082     * </code> otherwise
083     */
084    private static  boolean treatTagStartState( ByteBuffer stream, Asn1Container container )
085    {
086        if ( stream.hasRemaining() )
087        {
088            byte octet = stream.get();
089
090            TLV tlv = new TLV( container.getNewTlvId() );
091            tlv.setTag( octet );
092
093            // Store the current TLV in the container.
094            container.setCurrentTLV( tlv );
095
096            // Create a link between the current TLV with its parent
097            tlv.setParent( container.getParentTLV() );
098
099            // Switch to the next state, which is the Length decoding
100            container.setState( TLVStateEnum.LENGTH_STATE_START );
101
102            if ( LOG.isDebugEnabled() )
103            {
104                byte tag = container.getCurrentTLV().getTag();
105                LOG.debug( I18n.msg( I18n.MSG_01000_TAG_DECODED, Asn1StringUtils.dumpByte( tag ) ) );
106            }
107
108            return MORE;
109        }
110        else
111        {
112            // The stream has been exhausted
113            return END;
114        }
115    }
116
117
118    /**
119     * Dump the current TLV tree
120     * 
121     * @param container The container
122     */
123    private static void dumpTLVTree( Asn1Container container )
124    {
125        StringBuilder sb = new StringBuilder();
126        TLV current = container.getCurrentTLV();
127
128        sb.append( "TLV" ).append( Asn1StringUtils.dumpByte( current.getTag() ) ).append( "(" ).append(
129            current.getExpectedLength() ).append( ")" );
130
131        current = current.getParent();
132
133        while ( current != null )
134        {
135            sb.append( "-TLV" ).append( Asn1StringUtils.dumpByte( current.getTag() ) ).append( "(" ).append(
136                current.getExpectedLength() ).append( ")" );
137            current = current.getParent();
138        }
139
140        if ( LOG.isDebugEnabled() )
141        {
142            LOG.debug( I18n.msg( I18n.MSG_01001_TLV_TREE, sb.toString() ) );
143        }
144    }
145
146
147    /**
148     * Check if the TLV tree is fully decoded
149     * 
150     * @param container The container
151     * @return <code>true</code> if the TLV has been decoded
152     */
153    private static boolean isTLVDecoded( Asn1Container container )
154    {
155        TLV current = container.getCurrentTLV();
156        TLV parent = current.getParent();
157
158        while ( parent != null )
159        {
160            if ( parent.getExpectedLength() != 0 )
161            {
162                return false;
163            }
164
165            parent = parent.getParent();
166        }
167
168        BerValue value = current.getValue();
169
170        if ( ( value != null ) && ( value.getData() != null ) )
171        {
172            return current.getExpectedLength() == value.getData().length;
173        }
174        else
175        {
176            return current.getExpectedLength() == 0;
177        }
178    }
179
180
181    /**
182     * Treat the Length start. The tag has been decoded, so we have to deal with
183     * the LENGTH, which can be multi-bytes.
184     * 
185     * @param stream  The ByteBuffer containing the PDU to decode
186     * @param container The container that stores the current state,
187     * the result and other informations.
188     * @return <code>true</code> if there are more bytes to read, <code>false
189     * </code> otherwise
190     * @throws DecoderException Thrown if anything went wrong
191     */
192    private static boolean treatLengthStartState( ByteBuffer stream, Asn1Container container ) throws DecoderException
193    {
194        if ( stream.hasRemaining() )
195        {
196            byte octet = stream.get();
197            TLV tlv = container.getCurrentTLV();
198
199            if ( ( octet & TLV.LENGTH_LONG_FORM ) == 0 )
200            {
201                // We don't have a long form. The Length of the Value part is
202                // given by this byte.
203                tlv.setLength( octet );
204                tlv.setLengthNbBytes( 1 );
205
206                container.setState( TLVStateEnum.LENGTH_STATE_END );
207            }
208            else if ( ( octet & TLV.LENGTH_EXTENSION_RESERVED ) != TLV.LENGTH_EXTENSION_RESERVED )
209            {
210                int expectedLength = octet & TLV.LENGTH_SHORT_MASK;
211
212                if ( expectedLength > 4 )
213                {
214                    String msg = I18n.err( I18n.ERR_01000_LENGTH_OVERFLOW );
215                    LOG.error( msg );
216                    throw new DecoderException( msg );
217                }
218
219                tlv.setLength( 0 );
220                tlv.setLengthNbBytes( 1 + expectedLength );
221                tlv.setLengthBytesRead( 1 );
222                container.setState( TLVStateEnum.LENGTH_STATE_PENDING );
223            }
224            else
225            {
226                String msg = I18n.err( I18n.ERR_01001_LENGTH_EXTENSION_RESERVED );
227                LOG.error( msg );
228                throw new DecoderException( msg );
229            }
230
231            return MORE;
232        }
233        else
234        {
235            return END;
236        }
237    }
238
239
240    /**
241     * This function is called when a Length is in the process of being decoded,
242     * but the lack of bytes in the buffer stopped the process.
243     * 
244     * @param stream The ByteBuffer containing the PDU to decode
245     * @param container The container that stores the current state,
246     * the result and other informations.
247     * @return <code>true</code> if there are more bytes to read, <code>false
248     * </code> otherwise
249     */
250    private static boolean treatLengthPendingState( ByteBuffer stream, Asn1Container container )
251    {
252        if ( stream.hasRemaining() )
253        {
254            TLV tlv = container.getCurrentTLV();
255            int length = tlv.getLength();
256
257            while ( tlv.getLengthBytesRead() < tlv.getLengthNbBytes() )
258            {
259                byte octet = stream.get();
260
261                if ( LOG.isDebugEnabled() )
262                {
263                    LOG.debug( I18n.msg( I18n.MSG_01002_CURRENT_BYTE, Asn1StringUtils.dumpByte( octet ) ) );
264                }
265
266                tlv.incLengthBytesRead();
267                length = ( length << 8 ) | ( octet & 0x00FF );
268
269                if ( !stream.hasRemaining() )
270                {
271                    tlv.setLength( length );
272
273                    if ( tlv.getLengthBytesRead() < tlv.getLengthNbBytes() )
274                    {
275                        container.setState( TLVStateEnum.LENGTH_STATE_PENDING );
276                        return END;
277                    }
278                    else
279                    {
280                        container.setState( TLVStateEnum.LENGTH_STATE_END );
281                        return MORE;
282                    }
283                }
284            }
285
286            tlv.setLength( length );
287            container.setState( TLVStateEnum.LENGTH_STATE_END );
288
289            return MORE;
290        }
291        else
292        {
293
294            return END;
295        }
296    }
297
298
299    /**
300     * A debug function used to dump the expected length stack.
301     * 
302     * @param tlv The current TLV.
303     * @return A string which represent the expected length stack.
304     */
305    private static String getParentLength( TLV tlv )
306    {
307        StringBuilder buffer = new StringBuilder();
308
309        buffer.append( "TLV expected length stack : " );
310        TLV currentTlv = tlv;
311
312        while ( true )
313        {
314            if ( currentTlv == null )
315            {
316                buffer.append( " - null" );
317                break;
318            }
319            else
320            {
321                buffer.append( " - " ).append( currentTlv.getExpectedLength() );
322            }
323
324            currentTlv = currentTlv.getParent();
325        }
326
327        return buffer.toString();
328    }
329
330
331    /**
332     * The Length is fully decoded. We have to call an action to check the size.
333     * 
334     * @param container The container that stores the current state,
335     * the result and other informations.
336     * @throws DecoderException Thrown if anything went wrong
337     */
338    private static void treatLengthEndState( Asn1Container container ) throws DecoderException
339    {
340        TLV tlv = container.getCurrentTLV();
341
342        if ( tlv == null )
343        {
344            String msg = I18n.err( I18n.ERR_01002_TLV_NULL );
345            LOG.error( msg );
346            throw new DecoderException( msg );
347        }
348
349        int length = tlv.getLength();
350
351        // We will check the length here. What we must control is
352        // that the enclosing constructed TLV expected length is not
353        // exceeded by the current TLV.
354        TLV parentTLV = container.getParentTLV();
355
356        if ( LOG.isDebugEnabled() )
357        {
358            LOG.debug( I18n.msg( I18n.MSG_01003_PARENT_LENGTH, getParentLength( parentTLV ) ) );
359        }
360
361        if ( parentTLV == null )
362        {
363            // This is the first TLV, so we can't check anything. We will
364            // just store this TLV as the root of the PDU
365            tlv.setExpectedLength( length );
366            container.setParentTLV( tlv );
367
368            if ( LOG.isDebugEnabled() )
369            {
370                LOG.debug( I18n.msg( I18n.MSG_01004_ROOT_TLV, Integer.valueOf( length ) ) );
371            }
372        }
373        else
374        {
375            // We have a parent, so we will check that its expected length is
376            // not exceeded.
377            int expectedLength = parentTLV.getExpectedLength();
378            int currentLength = tlv.getSize();
379
380            if ( expectedLength < currentLength )
381            {
382                // The expected length is lower than the Value length of the
383                // current TLV. This is an error...
384                if ( LOG.isDebugEnabled() )
385                {
386                    LOG.debug( I18n.msg( I18n.MSG_01005_TLV, 
387                                Integer.valueOf( expectedLength ), 
388                                Integer.valueOf( currentLength ) ) );
389                }
390                
391                throw new DecoderException( I18n.err( I18n.ERR_01003_VALUE_LENGTH_ABOVE_EXPECTED_LENGTH, Integer
392                    .valueOf( currentLength ), Integer.valueOf( expectedLength ) ) );
393            }
394
395            // deal with the particular case where expected length equal
396            // the current length, which means that the parentTLV has been
397            // completed.
398            if ( expectedLength == currentLength )
399            {
400                parentTLV.setExpectedLength( 0 );
401
402                // We also have to check that the current TLV is a constructed
403                // one.
404                // In this case, we have to switch from this parent TLV
405                // to the parent's parent TLV.
406                if ( tlv.isConstructed() )
407                {
408                    // here, we also have another special case : a
409                    // zero length TLV. We must then unstack all
410                    // the parents which length is null.
411                    if ( length == 0 )
412                    {
413                        // We will set the parent to the first parentTLV which
414                        // expectedLength
415                        // is not null, and it will become the new parent TLV
416                        while ( parentTLV != null )
417                        {
418                            if ( parentTLV.getExpectedLength() != 0 )
419                            {
420                                // ok, we have an incomplete parent. we will
421                                // stop the recursion right here
422                                break;
423                            }
424                            else
425                            {
426                                parentTLV = parentTLV.getParent();
427                            }
428                        }
429
430                        container.setParentTLV( parentTLV );
431                    }
432                    else
433                    {
434                        // The new Parent TLV is this Constructed TLV
435                        container.setParentTLV( tlv );
436                    }
437
438                    tlv.setParent( parentTLV );
439                    tlv.setExpectedLength( length );
440                }
441                else
442                {
443                    tlv.setExpectedLength( length );
444
445                    // It's over, the parent TLV has been completed.
446                    // Go back to the parent's parent TLV until we find
447                    // a tlv which is not complete.
448                    while ( parentTLV != null )
449                    {
450                        if ( parentTLV.getExpectedLength() != 0 )
451                        {
452                            // ok, we have an incomplete parent. we will
453                            // stop the recursion right here
454                            break;
455                        }
456                        else
457                        {
458                            parentTLV = parentTLV.getParent();
459                        }
460                    }
461
462                    container.setParentTLV( parentTLV );
463                }
464            }
465            else
466            {
467                // Renew the expected Length.
468                parentTLV.setExpectedLength( expectedLength - currentLength );
469                tlv.setExpectedLength( length );
470
471                if ( tlv.isConstructed() )
472                {
473                    // We have a constructed tag, so we must switch the
474                    // parentTLV
475                    tlv.setParent( parentTLV );
476                    container.setParentTLV( tlv );
477                }
478            }
479
480        }
481
482        if ( LOG.isDebugEnabled() )
483        {
484            LOG.debug( I18n.msg( I18n.MSG_01006_LENGTH_DECODED, Integer.valueOf( length ) ) );
485        }
486
487        if ( length == 0 )
488        {
489            // The length is 0, so we can't expect a value.
490            container.setState( TLVStateEnum.TLV_STATE_DONE );
491        }
492        else
493        {
494            // Go ahead and decode the value part
495            container.setState( TLVStateEnum.VALUE_STATE_START );
496        }
497    }
498
499
500    /**
501     * Treat the Value part. We will distinguish two cases : - if the Tag is a
502     * Primitive one, we will get the value. - if the Tag is a Constructed one,
503     * nothing will be done.
504     * 
505     * @param stream The ByteBuffer containing the PDU to decode
506     * @param container The container that stores the current state,
507     * the result and other informations.
508     * @return <code>true</code> if there are more bytes to read, <code>false
509     * </code> otherwise
510     */
511    private static  boolean treatValueStartState( ByteBuffer stream, Asn1Container container )
512    {
513        TLV currentTlv = container.getCurrentTLV();
514
515        if ( TLV.isConstructed( currentTlv.getTag() ) && !container.isGathering() )
516        {
517            container.setState( TLVStateEnum.TLV_STATE_DONE );
518
519            return MORE;
520        }
521        else
522        {
523            int length = currentTlv.getLength();
524            int nbBytes = stream.remaining();
525
526            if ( nbBytes < length )
527            {
528                currentTlv.getValue().init( length );
529                currentTlv.getValue().setData( stream );
530                container.setState( TLVStateEnum.VALUE_STATE_PENDING );
531
532                return END;
533            }
534            else
535            {
536                currentTlv.getValue().init( length );
537                stream.get( currentTlv.getValue().getData(), 0, length );
538                container.setState( TLVStateEnum.TLV_STATE_DONE );
539
540                return MORE;
541            }
542        }
543    }
544
545
546    /**
547     * Treat a pending Value when we get more bytes in the buffer.
548     * 
549     * @param stream The ByteBuffer containing the PDU to decode
550     * @param container The container that stores the current state,
551     * the result and other informations.
552     * @return <code>MORE</code> if some bytes remain in the buffer when the
553     * value has been decoded, <code>END</code> if whe still need to get some
554     * more bytes.
555     */
556    private static boolean treatValuePendingState( ByteBuffer stream, Asn1Container container )
557    {
558        TLV currentTlv = container.getCurrentTLV();
559
560        int length = currentTlv.getLength();
561        int currentLength = currentTlv.getValue().getCurrentLength();
562        int nbBytes = stream.remaining();
563
564        if ( ( currentLength + nbBytes ) < length )
565        {
566            currentTlv.getValue().addData( stream );
567            container.setState( TLVStateEnum.VALUE_STATE_PENDING );
568
569            return END;
570        }
571        else
572        {
573            int remaining = length - currentLength;
574            byte[] data = new byte[remaining];
575            stream.get( data, 0, remaining );
576            currentTlv.getValue().addData( data );
577            container.setState( TLVStateEnum.TLV_STATE_DONE );
578
579            return MORE;
580        }
581    }
582
583
584    /**
585     * When the TLV has been fully decoded, we have to execute the associated
586     * action and switch to the next TLV, which will start with a Tag.
587     * 
588     * @param stream The ByteBuffer containing the PDU to decode
589     * @param container The container that stores the current state,
590     * the result and other informations.
591     * @return <code>true</code> if there are more bytes to read, <code>false
592     * </code> otherwise
593     * @throws DecoderException Thrown if anything went wrong
594     */
595    @SuppressWarnings("unchecked")
596    private static boolean treatTLVDoneState( ByteBuffer stream, Asn1Container container ) throws DecoderException
597    {
598        if ( LOG.isDebugEnabled() )
599        {
600            dumpTLVTree( container );
601        }
602
603        // First, we have to execute the associated action
604        container.getGrammar().executeAction( container );
605
606        // Check if the PDU has been fully decoded.
607        if ( isTLVDecoded( container ) )
608        {
609            if ( container.getState() == TLVStateEnum.GRAMMAR_END )
610            {
611                // Change the state to DECODED
612                container.setState( TLVStateEnum.PDU_DECODED );
613            }
614            else
615            {
616                if ( container.isGrammarEndAllowed() )
617                {
618                    // Change the state to DECODED
619                    container.setState( TLVStateEnum.PDU_DECODED );
620                }
621                else
622                {
623                    LOG.error( I18n.err( I18n.ERR_01004_MORE_TLV_EXPECTED ) );
624                    throw new DecoderException( I18n.err( I18n.ERR_01005_TRUNCATED_PDU ) );
625                }
626            }
627        }
628        else
629        {
630            // Then we switch to the Start tag state and free the current TLV
631            container.setState( TLVStateEnum.TAG_STATE_START );
632        }
633
634        return stream.hasRemaining();
635    }
636
637
638    /**
639     * The decoder main function. This is where we read bytes from the stream
640     * and go through the automaton. It's an inifnite loop which stop when no
641     * more bytes are to be read. It can occurs if the ByteBuffer is exhausted
642     * or if the PDU has been fully decoded.
643     * 
644     * @param stream The ByteBuffer containing the PDU to decode
645     * @param container The container that store the state, the result
646     * and other elements.
647     * @throws DecoderException Thrown if anything went wrong!
648     */
649    public static void decode( ByteBuffer stream, Asn1Container container ) throws DecoderException
650    {
651        /*
652         * We have to deal with the current state. This is an infinite loop,
653         * which will stop for any of these reasons :
654         * - STATE_END has been reached (hopefully, the most frequent case)
655         * - buffer is empty (it could happen)
656         * - STATE_OVERFLOW : bad situation ! The PDU may be a
657         * malevolous hand crafted ones, that try to "kill" our decoder. We
658         * must log it with all information to track back this case, and punish
659         * the guilty !
660         */
661        boolean hasRemaining = stream.hasRemaining();
662
663        // Increment the PDU size counter.
664        container.incrementDecodedBytes( stream.remaining() );
665
666        if ( container.getDecodedBytes() > container.getMaxPDUSize() )
667        {
668            String message = I18n.err( I18n.ERR_01007_PDU_SIZE_TOO_LONG, container.getDecodedBytes(), container
669                .getMaxPDUSize() );
670            LOG.error( message );
671            throw new DecoderException( message );
672        }
673
674        if ( LOG.isDebugEnabled() )
675        {
676            LOG.debug( I18n.msg( I18n.MSG_01007_LINE_SEPARATOR1 ) );
677            LOG.debug( I18n.msg( I18n.MSG_01011_DECODING_PDU ) );
678            LOG.debug( I18n.msg( I18n.MSG_01008_LINE_SEPARATOR2 ) );
679        }
680
681        while ( hasRemaining )
682        {
683            if ( LOG.isDebugEnabled() )
684            {
685                LOG.debug( I18n.msg( I18n.MSG_01012_STATE, container.getState() ) );
686
687                if ( stream.hasRemaining() )
688                {
689                    byte octet = stream.get( stream.position() );
690
691                    LOG.debug( I18n.msg( I18n.MSG_01013_CURRENT_BYTE, Asn1StringUtils.dumpByte( octet ) ) );
692                }
693                else
694                {
695                    LOG.debug( I18n.msg( I18n.MSG_01014_NO_MORE_BYTE ) );
696                }
697            }
698
699            switch ( container.getState() )
700            {
701                case TAG_STATE_START:
702                    // Reset the GrammarEnd flag first
703                    container.setGrammarEndAllowed( false );
704                    hasRemaining = treatTagStartState( stream, container );
705
706                    break;
707
708                case LENGTH_STATE_START:
709                    hasRemaining = treatLengthStartState( stream, container );
710
711                    break;
712
713                case LENGTH_STATE_PENDING:
714                    hasRemaining = treatLengthPendingState( stream, container );
715
716                    break;
717
718                case LENGTH_STATE_END:
719                    treatLengthEndState( container );
720
721                    break;
722
723                case VALUE_STATE_START:
724                    hasRemaining = treatValueStartState( stream, container );
725
726                    break;
727
728                case VALUE_STATE_PENDING:
729                    hasRemaining = treatValuePendingState( stream, container );
730
731                    break;
732
733                case VALUE_STATE_END:
734                    hasRemaining = stream.hasRemaining();
735
736                    // Nothing to do. We will never reach this state
737                    break;
738
739                case TLV_STATE_DONE:
740                    hasRemaining = treatTLVDoneState( stream, container );
741
742                    break;
743
744                case PDU_DECODED:
745                    // We have to deal with the case where there are
746                    // more bytes in the buffer, but the PDU has been decoded.
747                    if ( LOG.isDebugEnabled() )
748                    {
749                        LOG.debug( I18n.err( I18n.ERR_01008_REMAINING_BYTES_FOR_DECODED_PDU ) );
750                    }
751
752                    hasRemaining = false;
753
754                    break;
755
756                default:
757                    break;
758            }
759        }
760
761        if ( LOG.isDebugEnabled() )
762        {
763            LOG.debug( I18n.msg( I18n.MSG_01009_LINE_SEPARATOR3 ) );
764
765            if ( container.getState() == TLVStateEnum.PDU_DECODED )
766            {
767                if ( container.getCurrentTLV() != null )
768                {
769                    LOG.debug( I18n.msg( I18n.MSG_01015_STOP_DECODING, container.getCurrentTLV().toString() ) );
770                }
771                else
772                {
773                    LOG.debug( I18n.msg( I18n.MSG_01016_STOP_DECODING_NULL_TLV ) );
774                }
775            }
776            else
777            {
778                if ( container.getCurrentTLV() != null )
779                {
780                    LOG.debug( I18n.msg( I18n.MSG_01017_END_DECODING, container.getCurrentTLV().toString() ) );
781                }
782                else
783                {
784                    LOG.debug( I18n.msg( I18n.MSG_01018_END_DECODING_NULL_TLV ) );
785                }
786            }
787
788            LOG.debug( I18n.msg( I18n.MSG_01010_LINE_SEPARATOR4 ) );
789        }
790    }
791
792
793    /**
794     * {@inheritDoc}
795     */
796    @Override
797    public int getMaxLengthLength()
798    {
799        return maxLengthLength;
800    }
801
802
803    /**
804     * {@inheritDoc}
805     */
806    @Override
807    public int getMaxTagLength()
808    {
809        return maxTagLength;
810    }
811
812
813    /**
814     * {@inheritDoc}
815     */
816    @Override
817    public boolean isIndefiniteLengthAllowed()
818    {
819
820        return indefiniteLengthAllowed;
821    }
822}