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