View Javadoc
1   /*
2    * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   *
25   * THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC.
26   */
27  
28  package com.sun.xml.internal.fastinfoset;
29  
30  import com.sun.xml.internal.fastinfoset.algorithm.BuiltInEncodingAlgorithmFactory;
31  import com.sun.xml.internal.fastinfoset.org.apache.xerces.util.XMLChar;
32  import com.sun.xml.internal.fastinfoset.util.CharArrayIntMap;
33  import com.sun.xml.internal.fastinfoset.util.KeyIntMap;
34  import com.sun.xml.internal.fastinfoset.util.LocalNameQualifiedNamesMap;
35  import com.sun.xml.internal.fastinfoset.util.StringIntMap;
36  import com.sun.xml.internal.fastinfoset.vocab.SerializerVocabulary;
37  import java.io.IOException;
38  import java.io.OutputStream;
39  import java.util.HashMap;
40  import java.util.Map;
41  import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithm;
42  import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmException;
43  import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes;
44  import com.sun.xml.internal.org.jvnet.fastinfoset.ExternalVocabulary;
45  import com.sun.xml.internal.org.jvnet.fastinfoset.FastInfosetException;
46  import com.sun.xml.internal.org.jvnet.fastinfoset.FastInfosetSerializer;
47  import com.sun.xml.internal.org.jvnet.fastinfoset.RestrictedAlphabet;
48  import com.sun.xml.internal.org.jvnet.fastinfoset.VocabularyApplicationData;
49  import org.xml.sax.helpers.DefaultHandler;
50  
51  /**
52   * Abstract encoder for developing concrete encoders.
53   *
54   * Concrete implementations extending Encoder will utilize methods on Encoder
55   * to encode XML infoset according to the Fast Infoset standard. It is the
56   * responsibility of the concrete implementation to ensure that methods are
57   * invoked in the correct order to produce a valid fast infoset document.
58   *
59   * <p>
60   * This class extends org.sax.xml.DefaultHandler so that concrete SAX
61   * implementations can be used with javax.xml.parsers.SAXParser and the parse
62   * methods that take org.sax.xml.DefaultHandler as a parameter.
63   *
64   * <p>
65   * Buffering of octets that are written to an {@link java.io.OutputStream} is
66   * supported in a similar manner to a {@link java.io.BufferedOutputStream}.
67   * Combining buffering with encoding enables better performance.
68   *
69   * <p>
70   * More than one fast infoset document may be encoded to the
71   * {@link java.io.OutputStream}.
72   *
73   */
74  public abstract class Encoder extends DefaultHandler implements FastInfosetSerializer {
75  
76      /**
77       * Character encoding scheme system property for the encoding
78       * of content and attribute values.
79       */
80      public static final String CHARACTER_ENCODING_SCHEME_SYSTEM_PROPERTY =
81          "com.sun.xml.internal.fastinfoset.serializer.character-encoding-scheme";
82  
83      /**
84       * Default character encoding scheme system property for the encoding
85       * of content and attribute values.
86       */
87      protected static final String _characterEncodingSchemeSystemDefault = getDefaultEncodingScheme();
88  
89      private static String getDefaultEncodingScheme() {
90          String p = System.getProperty(CHARACTER_ENCODING_SCHEME_SYSTEM_PROPERTY,
91              UTF_8);
92          if (p.equals(UTF_16BE)) {
93              return UTF_16BE;
94          } else {
95              return UTF_8;
96          }
97      }
98  
99      private static int[] NUMERIC_CHARACTERS_TABLE;
100 
101     private static int[] DATE_TIME_CHARACTERS_TABLE;
102 
103     static {
104         NUMERIC_CHARACTERS_TABLE = new int[maxCharacter(RestrictedAlphabet.NUMERIC_CHARACTERS) + 1];
105         DATE_TIME_CHARACTERS_TABLE = new int[maxCharacter(RestrictedAlphabet.DATE_TIME_CHARACTERS) + 1];
106 
107         for (int i = 0; i < NUMERIC_CHARACTERS_TABLE.length ; i++) {
108             NUMERIC_CHARACTERS_TABLE[i] = -1;
109         }
110         for (int i = 0; i < DATE_TIME_CHARACTERS_TABLE.length ; i++) {
111             DATE_TIME_CHARACTERS_TABLE[i] = -1;
112         }
113 
114         for (int i = 0; i < RestrictedAlphabet.NUMERIC_CHARACTERS.length() ; i++) {
115             NUMERIC_CHARACTERS_TABLE[RestrictedAlphabet.NUMERIC_CHARACTERS.charAt(i)] = i;
116         }
117         for (int i = 0; i < RestrictedAlphabet.DATE_TIME_CHARACTERS.length() ; i++) {
118             DATE_TIME_CHARACTERS_TABLE[RestrictedAlphabet.DATE_TIME_CHARACTERS.charAt(i)] = i;
119         }
120     }
121 
122     private static int maxCharacter(String alphabet) {
123         int c = 0;
124         for (int i = 0; i < alphabet.length() ; i++) {
125             if (c < alphabet.charAt(i)) {
126                 c = alphabet.charAt(i);
127             }
128         }
129 
130         return c;
131     }
132 
133     /**
134      * True if DTD and internal subset shall be ignored.
135      */
136     private boolean _ignoreDTD;
137 
138     /**
139      * True if comments shall be ignored.
140      */
141     private boolean _ignoreComments;
142 
143     /**
144      * True if procesing instructions shall be ignored.
145      */
146     private boolean _ignoreProcessingInstructions;
147 
148     /**
149      * True if white space characters for text content shall be ignored.
150      */
151     private boolean _ignoreWhiteSpaceTextContent;
152 
153     /**
154      * True, if the local name string is used as the key to find the
155      * associated set of qualified names.
156      * <p>
157      * False,  if the <prefix>:<local name> string is used as the key
158      * to find the associated set of qualified names.
159      */
160     private boolean _useLocalNameAsKeyForQualifiedNameLookup;
161 
162     /**
163      * True if strings for text content and attribute values will be
164      * UTF-8 encoded otherwise they will be UTF-16 encoded.
165      */
166     private boolean _encodingStringsAsUtf8 = true;
167 
168     /**
169      * Encoding constant generated from the string encoding.
170      */
171     private int _nonIdentifyingStringOnThirdBitCES;
172 
173     /**
174      * Encoding constant generated from the string encoding.
175      */
176     private int _nonIdentifyingStringOnFirstBitCES;
177 
178     /**
179      * The map of URIs to algorithms.
180      */
181     private Map _registeredEncodingAlgorithms = new HashMap();
182 
183     /**
184      * The vocabulary that is used by the encoder
185      */
186     protected SerializerVocabulary _v;
187 
188     /**
189      * The vocabulary application data that is used by the encoder
190      */
191     protected VocabularyApplicationData _vData;
192 
193     /**
194      * True if the vocubulary is internal to the encoder
195      */
196     private boolean _vIsInternal;
197 
198     /**
199      * True if terminatation of an information item is required
200      */
201     protected boolean _terminate = false;
202 
203     /**
204      * The current octet that is to be written.
205      */
206     protected int _b;
207 
208     /**
209      * The {@link java.io.OutputStream} that the encoded XML infoset (the
210      * fast infoset document) is written to.
211      */
212     protected OutputStream _s;
213 
214     /**
215      * The internal buffer of characters used for the UTF-8 or UTF-16 encoding
216      * of characters.
217      */
218     protected char[] _charBuffer = new char[512];
219 
220     /**
221      * The internal buffer of bytes.
222      */
223     protected byte[] _octetBuffer = new byte[1024];
224 
225     /**
226      * The current position in the internal buffer.
227      */
228     protected int _octetBufferIndex;
229 
230     /**
231      * The current mark in the internal buffer.
232      *
233      * <p>
234      * If the value of the mark is < 0 then the mark is not set.
235      */
236     protected int _markIndex = -1;
237 
238     /**
239      * The minimum size of [normalized value] of Attribute Information
240      * Items that will be indexed.
241      */
242     protected int minAttributeValueSize = FastInfosetSerializer.MIN_ATTRIBUTE_VALUE_SIZE;
243 
244     /**
245      * The maximum size of [normalized value] of Attribute Information
246      * Items that will be indexed.
247      */
248     protected int maxAttributeValueSize = FastInfosetSerializer.MAX_ATTRIBUTE_VALUE_SIZE;
249 
250     /**
251      * The limit on the size of indexed Map for attribute values
252      * Limit is measured in characters number
253      */
254     protected int attributeValueMapTotalCharactersConstraint = FastInfosetSerializer.ATTRIBUTE_VALUE_MAP_MEMORY_CONSTRAINT / 2;
255 
256     /**
257      * The minimum size of character content chunks
258      * of Character Information Items or Comment Information Items that
259      * will be indexed.
260      */
261     protected int minCharacterContentChunkSize = FastInfosetSerializer.MIN_CHARACTER_CONTENT_CHUNK_SIZE;
262 
263     /**
264      * The maximum size of character content chunks
265      * of Character Information Items or Comment Information Items that
266      * will be indexed.
267      */
268     protected int maxCharacterContentChunkSize = FastInfosetSerializer.MAX_CHARACTER_CONTENT_CHUNK_SIZE;
269 
270     /**
271      * The limit on the size of indexed Map for character content chunks
272      * Limit is measured in characters number
273      */
274     protected int characterContentChunkMapTotalCharactersConstraint = FastInfosetSerializer.CHARACTER_CONTENT_CHUNK_MAP_MEMORY_CONSTRAINT / 2;
275 
276     /**
277      * Default constructor for the Encoder.
278      */
279     protected Encoder() {
280         setCharacterEncodingScheme(_characterEncodingSchemeSystemDefault);
281     }
282 
283     protected Encoder(boolean useLocalNameAsKeyForQualifiedNameLookup) {
284         setCharacterEncodingScheme(_characterEncodingSchemeSystemDefault);
285         _useLocalNameAsKeyForQualifiedNameLookup = useLocalNameAsKeyForQualifiedNameLookup;
286     }
287 
288 
289     // FastInfosetSerializer interface
290 
291     /**
292      * {@inheritDoc}
293      */
294     public final void setIgnoreDTD(boolean ignoreDTD) {
295         _ignoreDTD = ignoreDTD;
296     }
297 
298     /**
299      * {@inheritDoc}
300      */
301     public final boolean getIgnoreDTD() {
302         return _ignoreDTD;
303     }
304 
305     /**
306      * {@inheritDoc}
307      */
308     public final void setIgnoreComments(boolean ignoreComments) {
309         _ignoreComments = ignoreComments;
310     }
311 
312     /**
313      * {@inheritDoc}
314      */
315     public final boolean getIgnoreComments() {
316         return _ignoreComments;
317     }
318 
319     /**
320      * {@inheritDoc}
321      */
322     public final void setIgnoreProcesingInstructions(boolean
323             ignoreProcesingInstructions) {
324         _ignoreProcessingInstructions = ignoreProcesingInstructions;
325     }
326 
327     /**
328      * {@inheritDoc}
329      */
330     public final boolean getIgnoreProcesingInstructions() {
331         return _ignoreProcessingInstructions;
332     }
333 
334     /**
335      * {@inheritDoc}
336      */
337     public final void setIgnoreWhiteSpaceTextContent(boolean ignoreWhiteSpaceTextContent) {
338         _ignoreWhiteSpaceTextContent = ignoreWhiteSpaceTextContent;
339     }
340 
341     /**
342      * {@inheritDoc}
343      */
344     public final boolean getIgnoreWhiteSpaceTextContent() {
345         return _ignoreWhiteSpaceTextContent;
346     }
347 
348     /**
349      * {@inheritDoc}
350      */
351     public void setCharacterEncodingScheme(String characterEncodingScheme) {
352         if (characterEncodingScheme.equals(UTF_16BE)) {
353             _encodingStringsAsUtf8 = false;
354             _nonIdentifyingStringOnThirdBitCES = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_UTF_16_FLAG;
355             _nonIdentifyingStringOnFirstBitCES = EncodingConstants.NISTRING_UTF_16_FLAG;
356         } else {
357             _encodingStringsAsUtf8 = true;
358             _nonIdentifyingStringOnThirdBitCES = EncodingConstants.CHARACTER_CHUNK;
359             _nonIdentifyingStringOnFirstBitCES = 0;
360         }
361     }
362 
363     /**
364      * {@inheritDoc}
365      */
366     public String getCharacterEncodingScheme() {
367         return (_encodingStringsAsUtf8) ? UTF_8 : UTF_16BE;
368     }
369 
370     /**
371      * {@inheritDoc}
372      */
373     public void setRegisteredEncodingAlgorithms(Map algorithms) {
374         _registeredEncodingAlgorithms = algorithms;
375         if (_registeredEncodingAlgorithms == null) {
376             _registeredEncodingAlgorithms = new HashMap();
377         }
378     }
379 
380     /**
381      * {@inheritDoc}
382      */
383     public Map getRegisteredEncodingAlgorithms() {
384         return _registeredEncodingAlgorithms;
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     public int getMinCharacterContentChunkSize() {
391         return minCharacterContentChunkSize;
392     }
393 
394     /**
395      * {@inheritDoc}
396      */
397     public void setMinCharacterContentChunkSize(int size) {
398         if (size < 0 ) {
399             size = 0;
400         }
401 
402         minCharacterContentChunkSize = size;
403     }
404 
405     /**
406      * {@inheritDoc}
407      */
408     public int getMaxCharacterContentChunkSize() {
409         return maxCharacterContentChunkSize;
410     }
411 
412     /**
413      * {@inheritDoc}
414      */
415     public void setMaxCharacterContentChunkSize(int size) {
416         if (size < 0 ) {
417             size = 0;
418         }
419 
420         maxCharacterContentChunkSize = size;
421     }
422 
423     /**
424      * {@inheritDoc}
425      */
426     public int getCharacterContentChunkMapMemoryLimit() {
427         return characterContentChunkMapTotalCharactersConstraint * 2;
428     }
429 
430     /**
431      * {@inheritDoc}
432      */
433     public void setCharacterContentChunkMapMemoryLimit(int size) {
434         if (size < 0 ) {
435             size = 0;
436         }
437 
438         characterContentChunkMapTotalCharactersConstraint = size / 2;
439     }
440 
441     /**
442      * Checks whether character content chunk (its length) matches length limit
443      *
444      * @param length the length of character content chunk is checking to be added to Map.
445      * @return whether character content chunk length matches limit
446      */
447     public boolean isCharacterContentChunkLengthMatchesLimit(int length) {
448         return length >= minCharacterContentChunkSize &&
449                 length < maxCharacterContentChunkSize;
450     }
451 
452     /**
453      * Checks whether character content table has enough memory to
454      * store character content chunk with the given length
455      *
456      * @param length the length of character content chunk is checking to be added to Map.
457      * @param map the custom CharArrayIntMap, which memory limits will be checked.
458      * @return whether character content map has enough memory
459      */
460     public boolean canAddCharacterContentToTable(int length, CharArrayIntMap map) {
461         return map.getTotalCharacterCount() + length <
462                         characterContentChunkMapTotalCharactersConstraint;
463     }
464 
465     /**
466      * {@inheritDoc}
467      */
468     public int getMinAttributeValueSize() {
469         return minAttributeValueSize;
470     }
471 
472     /**
473      * {@inheritDoc}
474      */
475     public void setMinAttributeValueSize(int size) {
476         if (size < 0 ) {
477             size = 0;
478         }
479 
480         minAttributeValueSize = size;
481     }
482 
483     /**
484      * {@inheritDoc}
485      */
486     public int getMaxAttributeValueSize() {
487         return maxAttributeValueSize;
488     }
489 
490     /**
491      * {@inheritDoc}
492      */
493     public void setMaxAttributeValueSize(int size) {
494         if (size < 0 ) {
495             size = 0;
496         }
497 
498         maxAttributeValueSize = size;
499     }
500 
501     /**
502      * {@inheritDoc}
503      */
504     public void setAttributeValueMapMemoryLimit(int size) {
505         if (size < 0 ) {
506             size = 0;
507         }
508 
509         attributeValueMapTotalCharactersConstraint = size / 2;
510 
511     }
512 
513     /**
514      * {@inheritDoc}
515      */
516     public int getAttributeValueMapMemoryLimit() {
517         return attributeValueMapTotalCharactersConstraint * 2;
518     }
519 
520     /**
521      * Checks whether attribute value (its length) matches length limit
522      *
523      * @param length the length of attribute
524      * @return whether attribute value matches limit
525      */
526     public boolean isAttributeValueLengthMatchesLimit(int length) {
527         return length >= minAttributeValueSize &&
528                 length < maxAttributeValueSize;
529     }
530 
531     /**
532      * Checks whether attribute table has enough memory to
533      * store attribute value with the given length
534      *
535      * @param length the length of attribute value is checking to be added to Map.
536      * @return whether attribute map has enough memory
537      */
538     public boolean canAddAttributeToTable(int length) {
539         return _v.attributeValue.getTotalCharacterCount() + length <
540                         attributeValueMapTotalCharactersConstraint;
541     }
542 
543     /**
544      * {@inheritDoc}
545      */
546     public void setExternalVocabulary(ExternalVocabulary v) {
547         // Create internal serializer vocabulary
548         _v = new SerializerVocabulary();
549         // Set the external vocabulary
550         SerializerVocabulary ev = new SerializerVocabulary(v.vocabulary,
551                 _useLocalNameAsKeyForQualifiedNameLookup);
552         _v.setExternalVocabulary(v.URI,
553                 ev, false);
554 
555         _vIsInternal = true;
556     }
557 
558     /**
559      * {@inheritDoc}
560      */
561     public void setVocabularyApplicationData(VocabularyApplicationData data) {
562         _vData = data;
563     }
564 
565     /**
566      * {@inheritDoc}
567      */
568     public VocabularyApplicationData getVocabularyApplicationData() {
569         return _vData;
570     }
571 
572     // End of FastInfosetSerializer interface
573 
574     /**
575      * Reset the encoder for reuse encoding another XML infoset.
576      */
577     public void reset() {
578         _terminate = false;
579     }
580 
581     /**
582      * Set the OutputStream to encode the XML infoset to a
583      * fast infoset document.
584      *
585      * @param s the OutputStream where the fast infoset document is written to.
586      */
587     public void setOutputStream(OutputStream s) {
588         _octetBufferIndex = 0;
589         _markIndex = -1;
590         _s = s;
591     }
592 
593     /**
594      * Set the SerializerVocabulary to be used for encoding.
595      *
596      * @param vocabulary the vocabulary to be used for encoding.
597      */
598     public void setVocabulary(SerializerVocabulary vocabulary) {
599         _v = vocabulary;
600         _vIsInternal = false;
601     }
602 
603     /**
604      * Encode the header of a fast infoset document.
605      *
606      * @param encodeXmlDecl true if the XML declaration should be encoded.
607      */
608     protected final void encodeHeader(boolean encodeXmlDecl) throws IOException {
609         if (encodeXmlDecl) {
610             _s.write(EncodingConstants.XML_DECLARATION_VALUES[0]);
611         }
612         _s.write(EncodingConstants.BINARY_HEADER);
613     }
614 
615     /**
616      * Encode the initial vocabulary of a fast infoset document.
617      *
618      */
619     protected final void encodeInitialVocabulary() throws IOException {
620         if (_v == null) {
621             _v = new SerializerVocabulary();
622             _vIsInternal = true;
623         } else if (_vIsInternal) {
624             _v.clear();
625             if (_vData != null)
626                 _vData.clear();
627         }
628 
629         if (!_v.hasInitialVocabulary() && !_v.hasExternalVocabulary()) {
630             write(0);
631         } else if (_v.hasInitialVocabulary()) {
632             _b = EncodingConstants.DOCUMENT_INITIAL_VOCABULARY_FLAG;
633             write(_b);
634 
635             SerializerVocabulary initialVocabulary = _v.getReadOnlyVocabulary();
636 
637             // TODO check for contents of vocabulary to assign bits
638             if (initialVocabulary.hasExternalVocabulary()) {
639                 _b = EncodingConstants.INITIAL_VOCABULARY_EXTERNAL_VOCABULARY_FLAG;
640                 write(_b);
641                 write(0);
642             }
643 
644             if (initialVocabulary.hasExternalVocabulary()) {
645                 encodeNonEmptyOctetStringOnSecondBit(_v.getExternalVocabularyURI());
646             }
647 
648             // TODO check for contents of vocabulary to encode values
649         } else if (_v.hasExternalVocabulary()) {
650             _b = EncodingConstants.DOCUMENT_INITIAL_VOCABULARY_FLAG;
651             write(_b);
652 
653             _b = EncodingConstants.INITIAL_VOCABULARY_EXTERNAL_VOCABULARY_FLAG;
654             write(_b);
655             write(0);
656 
657             encodeNonEmptyOctetStringOnSecondBit(_v.getExternalVocabularyURI());
658         }
659     }
660 
661     /**
662      * Encode the termination of the Document Information Item.
663      *
664      */
665     protected final void encodeDocumentTermination() throws IOException {
666         encodeElementTermination();
667         encodeTermination();
668         _flush();
669         _s.flush();
670     }
671 
672     /**
673      * Encode the termination of an Element Information Item.
674      *
675      */
676     protected final void encodeElementTermination() throws IOException {
677         _terminate = true;
678         switch (_b) {
679             case EncodingConstants.TERMINATOR:
680                 _b = EncodingConstants.DOUBLE_TERMINATOR;
681                 break;
682             case EncodingConstants.DOUBLE_TERMINATOR:
683                 write(EncodingConstants.DOUBLE_TERMINATOR);
684             default:
685                 _b = EncodingConstants.TERMINATOR;
686         }
687     }
688 
689     /**
690      * Encode a termination if required.
691      *
692      */
693     protected final void encodeTermination() throws IOException {
694         if (_terminate) {
695             write(_b);
696             _b = 0;
697             _terminate = false;
698         }
699     }
700 
701     /**
702      * Encode a Attribute Information Item that is a namespace declaration.
703      *
704      * @param prefix the prefix of the namespace declaration,
705      * if "" then there is no prefix for the namespace declaration.
706      * @param uri the URI of the namespace declaration,
707      * if "" then there is no URI for the namespace declaration.
708      */
709     protected final void encodeNamespaceAttribute(String prefix, String uri) throws IOException {
710         _b = EncodingConstants.NAMESPACE_ATTRIBUTE;
711         if (prefix.length() > 0) {
712             _b |= EncodingConstants.NAMESPACE_ATTRIBUTE_PREFIX_FLAG;
713         }
714         if (uri.length() > 0) {
715             _b |= EncodingConstants.NAMESPACE_ATTRIBUTE_NAME_FLAG;
716         }
717 
718         // NOTE a prefix with out a namespace name is an undeclaration
719         // of the namespace bound to the prefix
720         // TODO needs to investigate how the startPrefixMapping works in
721         // relation to undeclaration
722 
723         write(_b);
724 
725         if (prefix.length() > 0) {
726             encodeIdentifyingNonEmptyStringOnFirstBit(prefix, _v.prefix);
727         }
728         if (uri.length() > 0) {
729             encodeIdentifyingNonEmptyStringOnFirstBit(uri, _v.namespaceName);
730         }
731     }
732 
733     /**
734      * Encode a chunk of Character Information Items.
735      *
736      * @param ch the array of characters.
737      * @param offset the offset into the array of characters.
738      * @param length the length of characters.
739      * @throws ArrayIndexOutOfBoundsException.
740      */
741     protected final void encodeCharacters(char[] ch, int offset, int length) throws IOException {
742         final boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
743         encodeNonIdentifyingStringOnThirdBit(ch, offset, length, _v.characterContentChunk, addToTable, true);
744     }
745 
746     /**
747      * Encode a chunk of Character Information Items.
748      *
749      * If the array of characters is to be indexed (as determined by
750      * {@link Encoder#characterContentChunkSizeContraint}) then the array is not cloned
751      * when adding the array to the vocabulary.
752      *
753      * @param ch the array of characters.
754      * @param offset the offset into the array of characters.
755      * @param length the length of characters.
756      * @throws ArrayIndexOutOfBoundsException.
757      */
758     protected final void encodeCharactersNoClone(char[] ch, int offset, int length) throws IOException {
759         final boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
760         encodeNonIdentifyingStringOnThirdBit(ch, offset, length, _v.characterContentChunk, addToTable, false);
761     }
762 
763     /**
764      * Encode a chunk of Character Information Items using a numeric
765      * alphabet that results in the encoding of a character in 4 bits
766      * (or two characters per octet).
767      *
768      * @param id the restricted alphabet identifier.
769      * @param table the table mapping characters to 4 bit values.
770      * @param ch the array of characters.
771      * @param offset the offset into the array of characters.
772      * @param length the length of characters.
773      * @param addToTable if characters should be added to table.
774      * @throws ArrayIndexOutOfBoundsException.
775      */
776     protected final void encodeNumericFourBitCharacters(char[] ch, int offset, int length,
777             boolean addToTable) throws FastInfosetException, IOException {
778         encodeFourBitCharacters(RestrictedAlphabet.NUMERIC_CHARACTERS_INDEX,
779                 NUMERIC_CHARACTERS_TABLE, ch, offset, length, addToTable);
780     }
781 
782     /**
783      * Encode a chunk of Character Information Items using a date-time
784      * alphabet that results in the encoding of a character in 4 bits
785      * (or two characters per octet).
786      *
787      * @param id the restricted alphabet identifier.
788      * @param table the table mapping characters to 4 bit values.
789      * @param ch the array of characters.
790      * @param offset the offset into the array of characters.
791      * @param length the length of characters.
792      * @param addToTable if characters should be added to table.
793      * @throws ArrayIndexOutOfBoundsException.
794      */
795     protected final void encodeDateTimeFourBitCharacters(char[] ch, int offset, int length,
796             boolean addToTable) throws FastInfosetException, IOException {
797         encodeFourBitCharacters(RestrictedAlphabet.DATE_TIME_CHARACTERS_INDEX,
798                 DATE_TIME_CHARACTERS_TABLE, ch, offset, length, addToTable);
799     }
800 
801     /**
802      * Encode a chunk of Character Information Items using a restricted
803      * alphabet that results in the encoding of a character in 4 bits
804      * (or two characters per octet).
805      *
806      * @param id the restricted alphabet identifier.
807      * @param table the table mapping characters to 4 bit values.
808      * @param ch the array of characters.
809      * @param offset the offset into the array of characters.
810      * @param length the length of characters.
811      * @param addToTable if characters should be added to table.
812      * @throws ArrayIndexOutOfBoundsException.
813      */
814     protected final void encodeFourBitCharacters(int id, int[] table, char[] ch, int offset, int length,
815             boolean addToTable) throws FastInfosetException, IOException {
816         if (addToTable) {
817             // if char array could be added to table
818             boolean canAddCharacterContentToTable =
819                     canAddCharacterContentToTable(length, _v.characterContentChunk);
820 
821             // obtain/get index
822             int index = canAddCharacterContentToTable ?
823                 _v.characterContentChunk.obtainIndex(ch, offset, length, true) :
824                 _v.characterContentChunk.get(ch, offset, length);
825 
826             if (index != KeyIntMap.NOT_PRESENT) {
827                 // if char array is in table
828                 _b = EncodingConstants.CHARACTER_CHUNK | 0x20;
829                 encodeNonZeroIntegerOnFourthBit(index);
830                 return;
831             } else if (canAddCharacterContentToTable) {
832                 // if char array is not in table, but could be added
833                 _b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG | EncodingConstants.CHARACTER_CHUNK_ADD_TO_TABLE_FLAG;
834             } else {
835                 // if char array is not in table and could not be added
836                 _b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
837             }
838         } else {
839             _b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
840         }
841 
842         write (_b);
843 
844         // Encode bottom 6 bits of enoding algorithm id
845         _b = id << 2;
846 
847         encodeNonEmptyFourBitCharacterStringOnSeventhBit(table, ch, offset, length);
848     }
849 
850     /**
851      * Encode a chunk of Character Information Items using a restricted
852      * alphabet table.
853      *
854      * @param alphabet the alphabet defining the mapping between characters and
855      *        integer values.
856      * @param ch the array of characters.
857      * @param offset the offset into the array of characters.
858      * @param length the length of characters.
859      * @param addToTable if characters should be added to table
860      * @throws ArrayIndexOutOfBoundsException.
861      * @throws FastInfosetException if the alphabet is not present in the
862      *         vocabulary.
863      */
864     protected final void encodeAlphabetCharacters(String alphabet, char[] ch, int offset, int length,
865             boolean addToTable) throws FastInfosetException, IOException {
866         if (addToTable) {
867             // if char array could be added to table
868             boolean canAddCharacterContentToTable =
869                     canAddCharacterContentToTable(length, _v.characterContentChunk);
870 
871             // obtain/get index
872             int index = canAddCharacterContentToTable ?
873                 _v.characterContentChunk.obtainIndex(ch, offset, length, true) :
874                 _v.characterContentChunk.get(ch, offset, length);
875 
876             if (index != KeyIntMap.NOT_PRESENT) {
877                 // if char array is in table
878                 _b = EncodingConstants.CHARACTER_CHUNK | 0x20;
879                 encodeNonZeroIntegerOnFourthBit(index);
880                 return;
881             } else if (canAddCharacterContentToTable) {
882                 // if char array is not in table, but could be added
883                 _b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG | EncodingConstants.CHARACTER_CHUNK_ADD_TO_TABLE_FLAG;
884             } else {
885                 // if char array is not in table and could not be added
886                 _b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
887             }
888         } else {
889             _b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
890         }
891 
892         int id = _v.restrictedAlphabet.get(alphabet);
893         if (id == KeyIntMap.NOT_PRESENT) {
894             throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.restrictedAlphabetNotPresent"));
895         }
896         id += EncodingConstants.RESTRICTED_ALPHABET_APPLICATION_START;
897 
898         _b |= (id & 0xC0) >> 6;
899         write(_b);
900 
901         // Encode bottom 6 bits of enoding algorithm id
902         _b = (id & 0x3F) << 2;
903 
904         encodeNonEmptyNBitCharacterStringOnSeventhBit(alphabet, ch, offset, length);
905     }
906 
907     /**
908      * Encode a Processing Instruction Information Item.
909      *
910      * @param target the target of the processing instruction.
911      * @param data the data of the processing instruction.
912      */
913     protected final void encodeProcessingInstruction(String target, String data) throws IOException {
914         write(EncodingConstants.PROCESSING_INSTRUCTION);
915 
916         // Target
917         encodeIdentifyingNonEmptyStringOnFirstBit(target, _v.otherNCName);
918 
919         // Data
920         boolean addToTable = isCharacterContentChunkLengthMatchesLimit(data.length());
921         encodeNonIdentifyingStringOnFirstBit(data, _v.otherString, addToTable);
922     }
923 
924     /**
925      * Encode a Document Type Declaration.
926      *
927      * @param systemId the system identifier of the external subset.
928      * @param publicId the public identifier of the external subset.
929      */
930     protected final void encodeDocumentTypeDeclaration(String systemId, String publicId) throws IOException {
931         _b = EncodingConstants.DOCUMENT_TYPE_DECLARATION;
932         if (systemId != null && systemId.length() > 0) {
933             _b |= EncodingConstants.DOCUMENT_TYPE_SYSTEM_IDENTIFIER_FLAG;
934         }
935         if (publicId != null && publicId.length() > 0) {
936             _b |= EncodingConstants.DOCUMENT_TYPE_PUBLIC_IDENTIFIER_FLAG;
937         }
938         write(_b);
939 
940         if (systemId != null && systemId.length() > 0) {
941             encodeIdentifyingNonEmptyStringOnFirstBit(systemId, _v.otherURI);
942         }
943         if (publicId != null && publicId.length() > 0) {
944             encodeIdentifyingNonEmptyStringOnFirstBit(publicId, _v.otherURI);
945         }
946     }
947 
948     /**
949      * Encode a Comment Information Item.
950      *
951      * @param ch the array of characters that is as comment.
952      * @param offset the offset into the array of characters.
953      * @param length the length of characters.
954      * @throws ArrayIndexOutOfBoundsException.
955      */
956     protected final void encodeComment(char[] ch, int offset, int length) throws IOException {
957         write(EncodingConstants.COMMENT);
958 
959         boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
960         encodeNonIdentifyingStringOnFirstBit(ch, offset, length, _v.otherString, addToTable, true);
961     }
962 
963     /**
964      * Encode a Comment Information Item.
965      *
966      * If the array of characters that is a comment is to be indexed (as
967      * determined by {@link Encoder#characterContentChunkSizeContraint}) then
968      * the array is not cloned when adding the array to the vocabulary.
969      *
970      * @param ch the array of characters.
971      * @param offset the offset into the array of characters.
972      * @param length the length of characters.
973      * @throws ArrayIndexOutOfBoundsException.
974      */
975     protected final void encodeCommentNoClone(char[] ch, int offset, int length) throws IOException {
976         write(EncodingConstants.COMMENT);
977 
978         boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
979         encodeNonIdentifyingStringOnFirstBit(ch, offset, length, _v.otherString, addToTable, false);
980     }
981 
982     /**
983      * Encode a qualified name of an Element Informaiton Item on the third bit
984      * of an octet.
985      * Implementation of clause C.18 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
986      *
987      * <p>
988      * The index of the qualified name will be encoded if the name is present
989      * in the vocabulary otherwise the qualified name will be encoded literally
990      * (see {@link #encodeLiteralElementQualifiedNameOnThirdBit}).
991      *
992      * @param namespaceURI the namespace URI of the qualified name.
993      * @param prefix the prefix of the qualified name.
994      * @param localName the local name of the qualified name.
995      */
996     protected final void encodeElementQualifiedNameOnThirdBit(String namespaceURI, String prefix, String localName) throws IOException {
997         LocalNameQualifiedNamesMap.Entry entry = _v.elementName.obtainEntry(localName);
998         if (entry._valueIndex > 0) {
999             QualifiedName[] names = entry._value;
1000             for (int i = 0; i < entry._valueIndex; i++) {
1001                 if ((prefix == names[i].prefix || prefix.equals(names[i].prefix))
1002                         && (namespaceURI == names[i].namespaceName || namespaceURI.equals(names[i].namespaceName))) {
1003                     encodeNonZeroIntegerOnThirdBit(names[i].index);
1004                     return;
1005                 }
1006             }
1007         }
1008 
1009         encodeLiteralElementQualifiedNameOnThirdBit(namespaceURI, prefix,
1010                 localName, entry);
1011     }
1012 
1013     /**
1014      * Encode a literal qualified name of an Element Informaiton Item on the
1015      * third bit of an octet.
1016      * Implementation of clause C.18 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1017      *
1018      * @param namespaceURI the namespace URI of the qualified name.
1019      * @param prefix the prefix of the qualified name.
1020      * @param localName the local name of the qualified name.
1021      */
1022     protected final void encodeLiteralElementQualifiedNameOnThirdBit(String namespaceURI, String prefix, String localName,
1023             LocalNameQualifiedNamesMap.Entry entry) throws IOException {
1024         QualifiedName name = new QualifiedName(prefix, namespaceURI, localName, "", _v.elementName.getNextIndex());
1025         entry.addQualifiedName(name);
1026 
1027         int namespaceURIIndex = KeyIntMap.NOT_PRESENT;
1028         int prefixIndex = KeyIntMap.NOT_PRESENT;
1029         if (namespaceURI.length() > 0) {
1030             namespaceURIIndex = _v.namespaceName.get(namespaceURI);
1031             if (namespaceURIIndex == KeyIntMap.NOT_PRESENT) {
1032                 throw new IOException(CommonResourceBundle.getInstance().getString("message.namespaceURINotIndexed", new Object[]{namespaceURI}));
1033             }
1034 
1035             if (prefix.length() > 0) {
1036                 prefixIndex = _v.prefix.get(prefix);
1037                 if (prefixIndex == KeyIntMap.NOT_PRESENT) {
1038                     throw new IOException(CommonResourceBundle.getInstance().getString("message.prefixNotIndexed", new Object[]{prefix}));
1039                 }
1040             }
1041         }
1042 
1043         int localNameIndex = _v.localName.obtainIndex(localName);
1044 
1045         _b |= EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG;
1046         if (namespaceURIIndex >= 0) {
1047             _b |= EncodingConstants.LITERAL_QNAME_NAMESPACE_NAME_FLAG;
1048             if (prefixIndex >= 0) {
1049                 _b |= EncodingConstants.LITERAL_QNAME_PREFIX_FLAG;
1050             }
1051         }
1052         write(_b);
1053 
1054         if (namespaceURIIndex >= 0) {
1055             if (prefixIndex >= 0) {
1056                 encodeNonZeroIntegerOnSecondBitFirstBitOne(prefixIndex);
1057             }
1058             encodeNonZeroIntegerOnSecondBitFirstBitOne(namespaceURIIndex);
1059         }
1060 
1061         if (localNameIndex >= 0) {
1062             encodeNonZeroIntegerOnSecondBitFirstBitOne(localNameIndex);
1063         } else {
1064             encodeNonEmptyOctetStringOnSecondBit(localName);
1065         }
1066     }
1067 
1068     /**
1069      * Encode a qualified name of an Attribute Informaiton Item on the third bit
1070      * of an octet.
1071      * Implementation of clause C.17 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1072      *
1073      * <p>
1074      * The index of the qualified name will be encoded if the name is present
1075      * in the vocabulary otherwise the qualified name will be encoded literally
1076      * (see {@link #encodeLiteralAttributeQualifiedNameOnSecondBit}).
1077      *
1078      * @param namespaceURI the namespace URI of the qualified name.
1079      * @param prefix the prefix of the qualified name.
1080      * @param localName the local name of the qualified name.
1081      */
1082     protected final void encodeAttributeQualifiedNameOnSecondBit(String namespaceURI, String prefix, String localName) throws IOException {
1083         LocalNameQualifiedNamesMap.Entry entry = _v.attributeName.obtainEntry(localName);
1084         if (entry._valueIndex > 0) {
1085             QualifiedName[] names = entry._value;
1086             for (int i = 0; i < entry._valueIndex; i++) {
1087                 if ((prefix == names[i].prefix || prefix.equals(names[i].prefix))
1088                         && (namespaceURI == names[i].namespaceName || namespaceURI.equals(names[i].namespaceName))) {
1089                     encodeNonZeroIntegerOnSecondBitFirstBitZero(names[i].index);
1090                     return;
1091                 }
1092             }
1093         }
1094 
1095         encodeLiteralAttributeQualifiedNameOnSecondBit(namespaceURI, prefix,
1096                 localName, entry);
1097     }
1098 
1099     /**
1100      * Encode a literal qualified name of an Attribute Informaiton Item on the
1101      * third bit of an octet.
1102      * Implementation of clause C.17 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1103      *
1104      * @param namespaceURI the namespace URI of the qualified name.
1105      * @param prefix the prefix of the qualified name.
1106      * @param localName the local name of the qualified name.
1107      */
1108     protected final boolean encodeLiteralAttributeQualifiedNameOnSecondBit(String namespaceURI, String prefix, String localName,
1109                 LocalNameQualifiedNamesMap.Entry entry) throws IOException {
1110         int namespaceURIIndex = KeyIntMap.NOT_PRESENT;
1111         int prefixIndex = KeyIntMap.NOT_PRESENT;
1112         if (namespaceURI.length() > 0) {
1113             namespaceURIIndex = _v.namespaceName.get(namespaceURI);
1114             if (namespaceURIIndex == KeyIntMap.NOT_PRESENT) {
1115                 if (namespaceURI == EncodingConstants.XMLNS_NAMESPACE_NAME ||
1116                         namespaceURI.equals(EncodingConstants.XMLNS_NAMESPACE_NAME)) {
1117                     return false;
1118                 } else {
1119                     throw new IOException(CommonResourceBundle.getInstance().getString("message.namespaceURINotIndexed", new Object[]{namespaceURI}));
1120                 }
1121             }
1122 
1123             if (prefix.length() > 0) {
1124                 prefixIndex = _v.prefix.get(prefix);
1125                 if (prefixIndex == KeyIntMap.NOT_PRESENT) {
1126                     throw new IOException(CommonResourceBundle.getInstance().getString("message.prefixNotIndexed", new Object[]{prefix}));
1127                 }
1128             }
1129         }
1130 
1131         int localNameIndex = _v.localName.obtainIndex(localName);
1132 
1133         QualifiedName name = new QualifiedName(prefix, namespaceURI, localName, "", _v.attributeName.getNextIndex());
1134         entry.addQualifiedName(name);
1135 
1136         _b = EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG;
1137         if (namespaceURI.length() > 0) {
1138             _b |= EncodingConstants.LITERAL_QNAME_NAMESPACE_NAME_FLAG;
1139             if (prefix.length() > 0) {
1140                 _b |= EncodingConstants.LITERAL_QNAME_PREFIX_FLAG;
1141             }
1142         }
1143 
1144         write(_b);
1145 
1146         if (namespaceURIIndex >= 0) {
1147             if (prefixIndex >= 0) {
1148                 encodeNonZeroIntegerOnSecondBitFirstBitOne(prefixIndex);
1149             }
1150             encodeNonZeroIntegerOnSecondBitFirstBitOne(namespaceURIIndex);
1151         } else if (namespaceURI != "") {
1152             // XML prefix and namespace name
1153             encodeNonEmptyOctetStringOnSecondBit("xml");
1154             encodeNonEmptyOctetStringOnSecondBit("http://www.w3.org/XML/1998/namespace");
1155         }
1156 
1157         if (localNameIndex >= 0) {
1158             encodeNonZeroIntegerOnSecondBitFirstBitOne(localNameIndex);
1159         } else {
1160             encodeNonEmptyOctetStringOnSecondBit(localName);
1161         }
1162 
1163         return true;
1164     }
1165 
1166     /**
1167      * Encode a non identifying string on the first bit of an octet.
1168      * Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1169      *
1170      * @param s the string to encode
1171      * @param map the vocabulary table of strings to indexes.
1172      * @param addToTable true if the string could be added to the vocabulary
1173      *                   table (if table has enough memory)
1174      * @param mustBeAddedToTable true if the string must be added to the vocabulary
1175      *                   table (if not already present in the table).
1176      */
1177     protected final void encodeNonIdentifyingStringOnFirstBit(String s, StringIntMap map,
1178             boolean addToTable, boolean mustBeAddedToTable) throws IOException {
1179         if (s == null || s.length() == 0) {
1180             // C.26 an index (first bit '1') with seven '1' bits for an empty string
1181             write(0xFF);
1182         } else {
1183             if (addToTable || mustBeAddedToTable) {
1184                 // if attribute value could be added to table
1185                 boolean canAddAttributeToTable = mustBeAddedToTable ||
1186                         canAddAttributeToTable(s.length());
1187 
1188                 // obtain/get index
1189                 int index = canAddAttributeToTable ?
1190                     map.obtainIndex(s) :
1191                     map.get(s);
1192 
1193                 if (index != KeyIntMap.NOT_PRESENT) {
1194                     // if attribute value is in table
1195                     encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
1196                 } else if (canAddAttributeToTable) {
1197                     // if attribute value is not in table, but could be added
1198                     _b = EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG |
1199                             _nonIdentifyingStringOnFirstBitCES;
1200                     encodeNonEmptyCharacterStringOnFifthBit(s);
1201                 } else {
1202                     // if attribute value is not in table and could not be added
1203                     _b = _nonIdentifyingStringOnFirstBitCES;
1204                     encodeNonEmptyCharacterStringOnFifthBit(s);
1205                 }
1206             } else {
1207                 _b = _nonIdentifyingStringOnFirstBitCES;
1208                 encodeNonEmptyCharacterStringOnFifthBit(s);
1209             }
1210         }
1211     }
1212 
1213     /**
1214      * Encode a non identifying string on the first bit of an octet.
1215      * Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1216      *
1217      * @param s the string to encode
1218      * @param map the vocabulary table of character arrays to indexes.
1219      * @param addToTable true if the string should be added to the vocabulary
1220      *                   table (if not already present in the table).
1221      */
1222     protected final void encodeNonIdentifyingStringOnFirstBit(String s, CharArrayIntMap map, boolean addToTable) throws IOException {
1223         if (s == null || s.length() == 0) {
1224             // C.26 an index (first bit '1') with seven '1' bits for an empty string
1225             write(0xFF);
1226         } else {
1227             if (addToTable) {
1228                 final char[] ch = s.toCharArray();
1229                 final int length = s.length();
1230 
1231                 // if char array could be added to table
1232                 boolean canAddCharacterContentToTable =
1233                         canAddCharacterContentToTable(length, map);
1234 
1235                 // obtain/get index
1236                 int index = canAddCharacterContentToTable ?
1237                     map.obtainIndex(ch, 0, length, false) :
1238                     map.get(ch, 0, length);
1239 
1240                 if (index != KeyIntMap.NOT_PRESENT) {
1241                     // if char array is in table
1242                     encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
1243                 } else if (canAddCharacterContentToTable) {
1244                     // if char array is not in table, but could be added
1245                     _b = EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG |
1246                             _nonIdentifyingStringOnFirstBitCES;
1247                     encodeNonEmptyCharacterStringOnFifthBit(ch, 0, length);
1248                 } else {
1249                     // if char array is not in table and could not be added
1250                     _b = _nonIdentifyingStringOnFirstBitCES;
1251                     encodeNonEmptyCharacterStringOnFifthBit(s);
1252                 }
1253             } else {
1254                 _b = _nonIdentifyingStringOnFirstBitCES;
1255                 encodeNonEmptyCharacterStringOnFifthBit(s);
1256             }
1257         }
1258     }
1259 
1260     /**
1261      * Encode a non identifying string on the first bit of an octet.
1262      * Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1263      *
1264      * @param ch the array of characters.
1265      * @param offset the offset into the array of characters.
1266      * @param length the length of characters.
1267      * @param map the vocabulary table of character arrays to indexes.
1268      * @param addToTable true if the string should be added to the vocabulary
1269      *                   table (if not already present in the table).
1270      * @param clone true if the array of characters should be cloned if added
1271      *              to the vocabulary table.
1272      */
1273     protected final void encodeNonIdentifyingStringOnFirstBit(char[] ch, int offset, int length, CharArrayIntMap map,
1274             boolean addToTable, boolean clone) throws IOException {
1275         if (length == 0) {
1276             // C.26 an index (first bit '1') with seven '1' bits for an empty string
1277             write(0xFF);
1278         } else {
1279             if (addToTable) {
1280                 // if char array could be added to table
1281                 boolean canAddCharacterContentToTable =
1282                         canAddCharacterContentToTable(length, map);
1283 
1284                 // obtain/get index
1285                 int index = canAddCharacterContentToTable ?
1286                     map.obtainIndex(ch, offset, length, clone) :
1287                     map.get(ch, offset, length);
1288 
1289                 if (index != KeyIntMap.NOT_PRESENT) {
1290                     // if char array is in table
1291                     encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
1292                 } else if (canAddCharacterContentToTable) {
1293                     // if char array is not in table, but could be added
1294                     _b = EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG |
1295                             _nonIdentifyingStringOnFirstBitCES;
1296                     encodeNonEmptyCharacterStringOnFifthBit(ch, offset, length);
1297                 } else {
1298                     // if char array is not in table and could not be added
1299                     _b = _nonIdentifyingStringOnFirstBitCES;
1300                     encodeNonEmptyCharacterStringOnFifthBit(ch, offset, length);
1301                 }
1302             } else {
1303                 _b = _nonIdentifyingStringOnFirstBitCES;
1304                 encodeNonEmptyCharacterStringOnFifthBit(ch, offset, length);
1305             }
1306         }
1307     }
1308 
1309     protected final void encodeNumericNonIdentifyingStringOnFirstBit(
1310             String s, boolean addToTable, boolean mustBeAddedToTable)
1311             throws IOException, FastInfosetException {
1312         encodeNonIdentifyingStringOnFirstBit(
1313                                     RestrictedAlphabet.NUMERIC_CHARACTERS_INDEX,
1314                                     NUMERIC_CHARACTERS_TABLE, s, addToTable,
1315                                     mustBeAddedToTable);
1316     }
1317 
1318     protected final void encodeDateTimeNonIdentifyingStringOnFirstBit(
1319             String s, boolean addToTable, boolean mustBeAddedToTable)
1320             throws IOException, FastInfosetException {
1321         encodeNonIdentifyingStringOnFirstBit(
1322                                     RestrictedAlphabet.DATE_TIME_CHARACTERS_INDEX,
1323                                     DATE_TIME_CHARACTERS_TABLE, s, addToTable,
1324                                     mustBeAddedToTable);
1325     }
1326 
1327     protected final void encodeNonIdentifyingStringOnFirstBit(int id, int[] table,
1328             String s, boolean addToTable, boolean mustBeAddedToTable)
1329             throws IOException, FastInfosetException {
1330         if (s == null || s.length() == 0) {
1331             // C.26 an index (first bit '1') with seven '1' bits for an empty string
1332             write(0xFF);
1333             return;
1334         }
1335 
1336         if (addToTable || mustBeAddedToTable) {
1337             // if attribute value could be added to table
1338             boolean canAddAttributeToTable = mustBeAddedToTable ||
1339                     canAddAttributeToTable(s.length());
1340 
1341             // obtain/get index
1342             int index = canAddAttributeToTable ?
1343                 _v.attributeValue.obtainIndex(s) :
1344                 _v.attributeValue.get(s);
1345 
1346             if (index != KeyIntMap.NOT_PRESENT) {
1347                 // if attribute value is in table
1348                 encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
1349                 return;
1350             } else if (canAddAttributeToTable) {
1351                 // if attribute value is not in table, but could be added
1352                 _b = EncodingConstants.NISTRING_RESTRICTED_ALPHABET_FLAG |
1353                         EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG;
1354             } else {
1355                 // if attribute value is not in table and could not be added
1356                 _b = EncodingConstants.NISTRING_RESTRICTED_ALPHABET_FLAG;
1357             }
1358         } else {
1359             _b = EncodingConstants.NISTRING_RESTRICTED_ALPHABET_FLAG;
1360         }
1361 
1362         // Encode identification and top four bits of alphabet id
1363         write (_b | ((id & 0xF0) >> 4));
1364         // Encode bottom 4 bits of alphabet id
1365         _b = (id & 0x0F) << 4;
1366 
1367         final int length = s.length();
1368         final int octetPairLength = length / 2;
1369         final int octetSingleLength = length % 2;
1370         encodeNonZeroOctetStringLengthOnFifthBit(octetPairLength + octetSingleLength);
1371         encodeNonEmptyFourBitCharacterString(table, s.toCharArray(), 0, octetPairLength, octetSingleLength);
1372     }
1373 
1374     /**
1375      * Encode a non identifying string on the first bit of an octet as binary
1376      * data using an encoding algorithm.
1377      * Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1378      *
1379      * @param URI the encoding algorithm URI. If the URI == null then the
1380      *            encoding algorithm identifier takes precendence.
1381      * @param id the encoding algorithm identifier.
1382      * @param data the data to be encoded using an encoding algorithm.
1383      * @throws EncodingAlgorithmException if the encoding algorithm URI is not
1384      *         present in the vocabulary, or the encoding algorithm identifier
1385      *         is not with the required range.
1386      */
1387     protected final void encodeNonIdentifyingStringOnFirstBit(String URI, int id, Object data) throws FastInfosetException, IOException {
1388         if (URI != null) {
1389             id = _v.encodingAlgorithm.get(URI);
1390             if (id == KeyIntMap.NOT_PRESENT) {
1391                 throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.EncodingAlgorithmURI", new Object[]{URI}));
1392             }
1393             id += EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START;
1394 
1395             EncodingAlgorithm ea = (EncodingAlgorithm)_registeredEncodingAlgorithms.get(URI);
1396             if (ea != null) {
1397                 encodeAIIObjectAlgorithmData(id, data, ea);
1398             } else {
1399                 if (data instanceof byte[]) {
1400                     byte[] d = (byte[])data;
1401                     encodeAIIOctetAlgorithmData(id, d, 0, d.length);
1402                 } else {
1403                     throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
1404                 }
1405             }
1406         } else if (id <= EncodingConstants.ENCODING_ALGORITHM_BUILTIN_END) {
1407             int length = 0;
1408             switch(id) {
1409                 case EncodingAlgorithmIndexes.HEXADECIMAL:
1410                 case EncodingAlgorithmIndexes.BASE64:
1411                     length = ((byte[])data).length;
1412                     break;
1413                 case EncodingAlgorithmIndexes.SHORT:
1414                     length = ((short[])data).length;
1415                     break;
1416                 case EncodingAlgorithmIndexes.INT:
1417                     length = ((int[])data).length;
1418                     break;
1419                 case EncodingAlgorithmIndexes.LONG:
1420                 case EncodingAlgorithmIndexes.UUID:
1421                     length = ((long[])data).length;
1422                     break;
1423                 case EncodingAlgorithmIndexes.BOOLEAN:
1424                     length = ((boolean[])data).length;
1425                     break;
1426                 case EncodingAlgorithmIndexes.FLOAT:
1427                     length = ((float[])data).length;
1428                     break;
1429                 case EncodingAlgorithmIndexes.DOUBLE:
1430                     length = ((double[])data).length;
1431                     break;
1432                 case EncodingAlgorithmIndexes.CDATA:
1433                     throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.CDATA"));
1434                 default:
1435                     throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.UnsupportedBuiltInAlgorithm", new Object[]{Integer.valueOf(id)}));
1436             }
1437             encodeAIIBuiltInAlgorithmData(id, data, 0, length);
1438         } else if (id >= EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START) {
1439             if (data instanceof byte[]) {
1440                 byte[] d = (byte[])data;
1441                 encodeAIIOctetAlgorithmData(id, d, 0, d.length);
1442             } else {
1443                 throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
1444             }
1445         } else {
1446             throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.identifiers10to31Reserved"));
1447         }
1448     }
1449 
1450     /**
1451      * Encode the [normalized value] of an Attribute Information Item using
1452      * using an encoding algorithm.
1453      * Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1454      *
1455      * @param id the encoding algorithm identifier.
1456      * @param d the data, as an array of bytes, to be encoded.
1457      * @param offset the offset into the array of bytes.
1458      * @param length the length of bytes.
1459      */
1460     protected final void encodeAIIOctetAlgorithmData(int id, byte[] d, int offset, int length) throws IOException {
1461         // Encode identification and top four bits of encoding algorithm id
1462         write (EncodingConstants.NISTRING_ENCODING_ALGORITHM_FLAG |
1463                 ((id & 0xF0) >> 4));
1464 
1465         // Encode bottom 4 bits of enoding algorithm id
1466         _b = (id & 0x0F) << 4;
1467 
1468         // Encode the length
1469         encodeNonZeroOctetStringLengthOnFifthBit(length);
1470 
1471         write(d, offset, length);
1472     }
1473 
1474     /**
1475      * Encode the [normalized value] of an Attribute Information Item using
1476      * using an encoding algorithm.
1477      * Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1478      *
1479      * @param id the encoding algorithm identifier.
1480      * @param data the data to be encoded using an encoding algorithm.
1481      * @param ea the encoding algorithm to use to encode the data into an
1482      *           array of bytes.
1483      */
1484     protected final void encodeAIIObjectAlgorithmData(int id, Object data, EncodingAlgorithm ea) throws FastInfosetException, IOException {
1485         // Encode identification and top four bits of encoding algorithm id
1486         write (EncodingConstants.NISTRING_ENCODING_ALGORITHM_FLAG |
1487                 ((id & 0xF0) >> 4));
1488 
1489         // Encode bottom 4 bits of enoding algorithm id
1490         _b = (id & 0x0F) << 4;
1491 
1492         _encodingBufferOutputStream.reset();
1493         ea.encodeToOutputStream(data, _encodingBufferOutputStream);
1494         encodeNonZeroOctetStringLengthOnFifthBit(_encodingBufferIndex);
1495         write(_encodingBuffer, _encodingBufferIndex);
1496     }
1497 
1498     /**
1499      * Encode the [normalized value] of an Attribute Information Item using
1500      * using a built in encoding algorithm.
1501      * Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1502      *
1503      * @param id the built in encoding algorithm identifier.
1504      * @param data the data to be encoded using an encoding algorithm. The data
1505      *        represents an array of items specified by the encoding algorithm
1506      *        identifier
1507      * @param offset the offset into the array of bytes.
1508      * @param length the length of bytes.
1509      */
1510     protected final void encodeAIIBuiltInAlgorithmData(int id, Object data, int offset, int length) throws IOException {
1511         // Encode identification and top four bits of encoding algorithm id
1512         write (EncodingConstants.NISTRING_ENCODING_ALGORITHM_FLAG |
1513                 ((id & 0xF0) >> 4));
1514 
1515         // Encode bottom 4 bits of enoding algorithm id
1516         _b = (id & 0x0F) << 4;
1517 
1518         final int octetLength = BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
1519                     getOctetLengthFromPrimitiveLength(length);
1520 
1521         encodeNonZeroOctetStringLengthOnFifthBit(octetLength);
1522 
1523         ensureSize(octetLength);
1524         BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
1525                 encodeToBytes(data, offset, length, _octetBuffer, _octetBufferIndex);
1526         _octetBufferIndex += octetLength;
1527     }
1528 
1529     /**
1530      * Encode a non identifying string on the third bit of an octet.
1531      * Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1532      *
1533      * @param ch the array of characters.
1534      * @param offset the offset into the array of characters.
1535      * @param length the length of characters.
1536      * @param map the vocabulary table of character arrays to indexes.
1537      * @param addToTable true if the array of characters should be added to the vocabulary
1538      *                   table (if not already present in the table).
1539      * @param clone true if the array of characters should be cloned if added
1540      *              to the vocabulary table.
1541      */
1542     protected final void encodeNonIdentifyingStringOnThirdBit(char[] ch, int offset, int length,
1543             CharArrayIntMap map, boolean addToTable, boolean clone) throws IOException {
1544         // length cannot be zero since sequence of CIIs has to be > 0
1545 
1546         if (addToTable) {
1547             // if char array could be added to table
1548             boolean canAddCharacterContentToTable =
1549                     canAddCharacterContentToTable(length, map);
1550 
1551             // obtain/get index
1552             int index = canAddCharacterContentToTable ?
1553                 map.obtainIndex(ch, offset, length, clone) :
1554                 map.get(ch, offset, length);
1555 
1556             if (index != KeyIntMap.NOT_PRESENT) {
1557                 // if char array is in table
1558                 _b = EncodingConstants.CHARACTER_CHUNK | 0x20;
1559                 encodeNonZeroIntegerOnFourthBit(index);
1560             } else if (canAddCharacterContentToTable) {
1561                 // if char array is not in table, but could be added
1562                 _b = EncodingConstants.CHARACTER_CHUNK_ADD_TO_TABLE_FLAG |
1563                         _nonIdentifyingStringOnThirdBitCES;
1564                 encodeNonEmptyCharacterStringOnSeventhBit(ch, offset, length);
1565             } else {
1566                 // if char array is not in table and could not be added
1567                     _b = _nonIdentifyingStringOnThirdBitCES;
1568                     encodeNonEmptyCharacterStringOnSeventhBit(ch, offset, length);
1569             }
1570         } else {
1571             // char array will not be added to map
1572             _b = _nonIdentifyingStringOnThirdBitCES;
1573             encodeNonEmptyCharacterStringOnSeventhBit(ch, offset, length);
1574         }
1575     }
1576 
1577     /**
1578      * Encode a non identifying string on the third bit of an octet as binary
1579      * data using an encoding algorithm.
1580      * Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1581      *
1582      * @param URI the encoding algorithm URI. If the URI == null then the
1583      *            encoding algorithm identifier takes precendence.
1584      * @param id the encoding algorithm identifier.
1585      * @param data the data to be encoded using an encoding algorithm.
1586      * @throws EncodingAlgorithmException if the encoding algorithm URI is not
1587      *         present in the vocabulary, or the encoding algorithm identifier
1588      *         is not with the required range.
1589      */
1590     protected final void encodeNonIdentifyingStringOnThirdBit(String URI, int id, Object data) throws FastInfosetException, IOException {
1591         if (URI != null) {
1592             id = _v.encodingAlgorithm.get(URI);
1593             if (id == KeyIntMap.NOT_PRESENT) {
1594                 throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.EncodingAlgorithmURI", new Object[]{URI}));
1595             }
1596             id += EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START;
1597 
1598             EncodingAlgorithm ea = (EncodingAlgorithm)_registeredEncodingAlgorithms.get(URI);
1599             if (ea != null) {
1600                 encodeCIIObjectAlgorithmData(id, data, ea);
1601             } else {
1602                 if (data instanceof byte[]) {
1603                     byte[] d = (byte[])data;
1604                     encodeCIIOctetAlgorithmData(id, d, 0, d.length);
1605                 } else {
1606                     throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
1607                 }
1608             }
1609         } else if (id <= EncodingConstants.ENCODING_ALGORITHM_BUILTIN_END) {
1610             int length = 0;
1611             switch(id) {
1612                 case EncodingAlgorithmIndexes.HEXADECIMAL:
1613                 case EncodingAlgorithmIndexes.BASE64:
1614                     length = ((byte[])data).length;
1615                     break;
1616                 case EncodingAlgorithmIndexes.SHORT:
1617                     length = ((short[])data).length;
1618                     break;
1619                 case EncodingAlgorithmIndexes.INT:
1620                     length = ((int[])data).length;
1621                     break;
1622                 case EncodingAlgorithmIndexes.LONG:
1623                 case EncodingAlgorithmIndexes.UUID:
1624                     length = ((long[])data).length;
1625                     break;
1626                 case EncodingAlgorithmIndexes.BOOLEAN:
1627                     length = ((boolean[])data).length;
1628                     break;
1629                 case EncodingAlgorithmIndexes.FLOAT:
1630                     length = ((float[])data).length;
1631                     break;
1632                 case EncodingAlgorithmIndexes.DOUBLE:
1633                     length = ((double[])data).length;
1634                     break;
1635                 case EncodingAlgorithmIndexes.CDATA:
1636                     throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.CDATA"));
1637                 default:
1638                     throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.UnsupportedBuiltInAlgorithm", new Object[]{Integer.valueOf(id)}));
1639             }
1640             encodeCIIBuiltInAlgorithmData(id, data, 0, length);
1641         } else if (id >= EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START) {
1642             if (data instanceof byte[]) {
1643                 byte[] d = (byte[])data;
1644                 encodeCIIOctetAlgorithmData(id, d, 0, d.length);
1645             } else {
1646                 throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
1647             }
1648         } else {
1649             throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.identifiers10to31Reserved"));
1650         }
1651     }
1652 
1653     /**
1654      * Encode a non identifying string on the third bit of an octet as binary
1655      * data using an encoding algorithm.
1656      * Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1657      *
1658      * @param URI the encoding algorithm URI. If the URI == null then the
1659      *            encoding algorithm identifier takes precendence.
1660      * @param id the encoding algorithm identifier.
1661      * @param d the data, as an array of bytes, to be encoded.
1662      * @param offset the offset into the array of bytes.
1663      * @param length the length of bytes.
1664      * @throws EncodingAlgorithmException if the encoding algorithm URI is not
1665      *         present in the vocabulary.
1666      */
1667     protected final void encodeNonIdentifyingStringOnThirdBit(String URI, int id, byte[] d, int offset, int length) throws FastInfosetException, IOException {
1668         if (URI != null) {
1669             id = _v.encodingAlgorithm.get(URI);
1670             if (id == KeyIntMap.NOT_PRESENT) {
1671                 throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.EncodingAlgorithmURI", new Object[]{URI}));
1672             }
1673             id += EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START;
1674         }
1675 
1676         encodeCIIOctetAlgorithmData(id, d, offset, length);
1677     }
1678 
1679     /**
1680      * Encode a chunk of Character Information Items using
1681      * using an encoding algorithm.
1682      * Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1683      *
1684      * @param id the encoding algorithm identifier.
1685      * @param d the data, as an array of bytes, to be encoded.
1686      * @param offset the offset into the array of bytes.
1687      * @param length the length of bytes.
1688      */
1689     protected final void encodeCIIOctetAlgorithmData(int id, byte[] d, int offset, int length) throws IOException {
1690         // Encode identification and top two bits of encoding algorithm id
1691         write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG |
1692                 ((id & 0xC0) >> 6));
1693 
1694         // Encode bottom 6 bits of enoding algorithm id
1695         _b = (id & 0x3F) << 2;
1696 
1697         // Encode the length
1698         encodeNonZeroOctetStringLengthOnSenventhBit(length);
1699 
1700         write(d, offset, length);
1701     }
1702 
1703     /**
1704      * Encode a chunk of Character Information Items using
1705      * using an encoding algorithm.
1706      * Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1707      *
1708      * @param id the encoding algorithm identifier.
1709      * @param data the data to be encoded using an encoding algorithm.
1710      * @param ea the encoding algorithm to use to encode the data into an
1711      *           array of bytes.
1712      */
1713     protected final void encodeCIIObjectAlgorithmData(int id, Object data, EncodingAlgorithm ea) throws FastInfosetException, IOException {
1714         // Encode identification and top two bits of encoding algorithm id
1715         write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG |
1716                 ((id & 0xC0) >> 6));
1717 
1718         // Encode bottom 6 bits of enoding algorithm id
1719         _b = (id & 0x3F) << 2;
1720 
1721         _encodingBufferOutputStream.reset();
1722         ea.encodeToOutputStream(data, _encodingBufferOutputStream);
1723         encodeNonZeroOctetStringLengthOnSenventhBit(_encodingBufferIndex);
1724         write(_encodingBuffer, _encodingBufferIndex);
1725     }
1726 
1727     /**
1728      * Encode a chunk of Character Information Items using
1729      * using an encoding algorithm.
1730      * Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1731      *
1732      * @param id the built in encoding algorithm identifier.
1733      * @param data the data to be encoded using an encoding algorithm. The data
1734      *        represents an array of items specified by the encoding algorithm
1735      *        identifier
1736      * @param offset the offset into the array of bytes.
1737      * @param length the length of bytes.
1738      */
1739     protected final void encodeCIIBuiltInAlgorithmData(int id, Object data, int offset, int length) throws FastInfosetException, IOException {
1740         // Encode identification and top two bits of encoding algorithm id
1741         write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG |
1742                 ((id & 0xC0) >> 6));
1743 
1744         // Encode bottom 6 bits of enoding algorithm id
1745         _b = (id & 0x3F) << 2;
1746 
1747         final int octetLength = BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
1748                     getOctetLengthFromPrimitiveLength(length);
1749 
1750         encodeNonZeroOctetStringLengthOnSenventhBit(octetLength);
1751 
1752         ensureSize(octetLength);
1753         BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
1754                 encodeToBytes(data, offset, length, _octetBuffer, _octetBufferIndex);
1755         _octetBufferIndex += octetLength;
1756     }
1757 
1758     /**
1759      * Encode a chunk of Character Information Items using
1760      * using the CDATA built in encoding algorithm.
1761      * Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1762      *
1763      * @param ch the array of characters.
1764      * @param offset the offset into the array of characters.
1765      * @param length the length of characters.
1766      */
1767     protected final void encodeCIIBuiltInAlgorithmDataAsCDATA(char[] ch, int offset, int length) throws FastInfosetException, IOException {
1768         // Encode identification and top two bits of encoding algorithm id
1769         write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG);
1770 
1771         // Encode bottom 6 bits of enoding algorithm id
1772         _b = EncodingAlgorithmIndexes.CDATA << 2;
1773 
1774 
1775         length = encodeUTF8String(ch, offset, length);
1776         encodeNonZeroOctetStringLengthOnSenventhBit(length);
1777         write(_encodingBuffer, length);
1778     }
1779 
1780     /**
1781      * Encode a non empty identifying string on the first bit of an octet.
1782      * Implementation of clause C.13 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1783      *
1784      * @param s the identifying string.
1785      * @param map the vocabulary table to use to determin the index of the
1786      *        identifying string
1787      */
1788     protected final void encodeIdentifyingNonEmptyStringOnFirstBit(String s, StringIntMap map) throws IOException {
1789         int index = map.obtainIndex(s);
1790         if (index == KeyIntMap.NOT_PRESENT) {
1791             // _b = 0;
1792             encodeNonEmptyOctetStringOnSecondBit(s);
1793         } else {
1794             // _b = 0x80;
1795             encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
1796         }
1797     }
1798 
1799     /**
1800      * Encode a non empty string on the second bit of an octet using the UTF-8
1801      * encoding.
1802      * Implementation of clause C.22 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1803      *
1804      * @param s the string.
1805      */
1806     protected final void encodeNonEmptyOctetStringOnSecondBit(String s) throws IOException {
1807         final int length = encodeUTF8String(s);
1808         encodeNonZeroOctetStringLengthOnSecondBit(length);
1809         write(_encodingBuffer, length);
1810     }
1811 
1812     /**
1813      * Encode the length of a UTF-8 encoded string on the second bit of an octet.
1814      * Implementation of clause C.22 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1815      *
1816      * @param length the length to encode.
1817      */
1818     protected final void encodeNonZeroOctetStringLengthOnSecondBit(int length) throws IOException {
1819         if (length < EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_SMALL_LIMIT) {
1820             // [1, 64]
1821             write(length - 1);
1822         } else if (length < EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_MEDIUM_LIMIT) {
1823             // [65, 320]
1824             write(EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_MEDIUM_FLAG); // 010 00000
1825             write(length - EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_SMALL_LIMIT);
1826         } else {
1827             // [321, 4294967296]
1828             write(EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_LARGE_FLAG); // 0110 0000
1829             length -= EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_MEDIUM_LIMIT;
1830             write(length >>> 24);
1831             write((length >> 16) & 0xFF);
1832             write((length >> 8) & 0xFF);
1833             write(length & 0xFF);
1834         }
1835     }
1836 
1837     /**
1838      * Encode a non empty string on the fifth bit of an octet using the UTF-8
1839      * or UTF-16 encoding.
1840      * Implementation of clause C.23 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1841      *
1842      * @param s the string.
1843      */
1844     protected final void encodeNonEmptyCharacterStringOnFifthBit(String s) throws IOException {
1845         final int length = (_encodingStringsAsUtf8) ? encodeUTF8String(s) : encodeUtf16String(s);
1846         encodeNonZeroOctetStringLengthOnFifthBit(length);
1847         write(_encodingBuffer, length);
1848     }
1849 
1850     /**
1851      * Encode a non empty string on the fifth bit of an octet using the UTF-8
1852      * or UTF-16 encoding.
1853      * Implementation of clause C.23 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1854      *
1855      * @param ch the array of characters.
1856      * @param offset the offset into the array of characters.
1857      * @param length the length of characters.
1858      */
1859     protected final void encodeNonEmptyCharacterStringOnFifthBit(char[] ch, int offset, int length) throws IOException {
1860         length = (_encodingStringsAsUtf8) ? encodeUTF8String(ch, offset, length) : encodeUtf16String(ch, offset, length);
1861         encodeNonZeroOctetStringLengthOnFifthBit(length);
1862         write(_encodingBuffer, length);
1863     }
1864 
1865     /**
1866      * Encode the length of a UTF-8 or UTF-16 encoded string on the fifth bit
1867      * of an octet.
1868      * Implementation of clause C.23 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1869      *
1870      * @param length the length to encode.
1871      */
1872     protected final void encodeNonZeroOctetStringLengthOnFifthBit(int length) throws IOException {
1873         if (length < EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_SMALL_LIMIT) {
1874             // [1, 8]
1875             write(_b | (length - 1));
1876         } else if (length < EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_MEDIUM_LIMIT) {
1877             // [9, 264]
1878             write(_b | EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_MEDIUM_FLAG); // 000010 00
1879             write(length - EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_SMALL_LIMIT);
1880         } else {
1881             // [265, 4294967296]
1882             write(_b | EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_LARGE_FLAG); // 000011 00
1883             length -= EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_MEDIUM_LIMIT;
1884             write(length >>> 24);
1885             write((length >> 16) & 0xFF);
1886             write((length >> 8) & 0xFF);
1887             write(length & 0xFF);
1888         }
1889     }
1890 
1891     /**
1892      * Encode a non empty string on the seventh bit of an octet using the UTF-8
1893      * or UTF-16 encoding.
1894      * Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1895      *
1896      * @param ch the array of characters.
1897      * @param offset the offset into the array of characters.
1898      * @param length the length of characters.
1899      */
1900     protected final void encodeNonEmptyCharacterStringOnSeventhBit(char[] ch, int offset, int length) throws IOException {
1901         length = (_encodingStringsAsUtf8) ? encodeUTF8String(ch, offset, length) : encodeUtf16String(ch, offset, length);
1902         encodeNonZeroOctetStringLengthOnSenventhBit(length);
1903         write(_encodingBuffer, length);
1904     }
1905 
1906     /**
1907      * Encode a non empty string on the seventh bit of an octet using a restricted
1908      * alphabet that results in the encoding of a character in 4 bits
1909      * (or two characters per octet).
1910      * Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1911      *
1912      * @param table the table mapping characters to 4 bit values.
1913      * @param ch the array of characters.
1914      * @param offset the offset into the array of characters.
1915      * @param length the length of characters.
1916      */
1917     protected final void encodeNonEmptyFourBitCharacterStringOnSeventhBit(int[] table, char[] ch, int offset, int length) throws FastInfosetException, IOException {
1918         final int octetPairLength = length / 2;
1919         final int octetSingleLength = length % 2;
1920 
1921         // Encode the length
1922         encodeNonZeroOctetStringLengthOnSenventhBit(octetPairLength + octetSingleLength);
1923         encodeNonEmptyFourBitCharacterString(table, ch, offset, octetPairLength, octetSingleLength);
1924     }
1925 
1926     protected final void encodeNonEmptyFourBitCharacterString(int[] table, char[] ch, int offset,
1927             int octetPairLength, int octetSingleLength) throws FastInfosetException, IOException {
1928         ensureSize(octetPairLength + octetSingleLength);
1929         // Encode all pairs
1930         int v = 0;
1931         for (int i = 0; i < octetPairLength; i++) {
1932             v = (table[ch[offset++]] << 4) | table[ch[offset++]];
1933             if (v < 0) {
1934                 throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.characterOutofAlphabetRange"));
1935             }
1936             _octetBuffer[_octetBufferIndex++] = (byte)v;
1937         }
1938         // Encode single character at end with termination bits
1939         if (octetSingleLength == 1) {
1940             v = (table[ch[offset]] << 4) | 0x0F;
1941             if (v < 0) {
1942                 throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.characterOutofAlphabetRange"));
1943             }
1944             _octetBuffer[_octetBufferIndex++] = (byte)v;
1945         }
1946     }
1947 
1948     /**
1949      * Encode a non empty string on the seventh bit of an octet using a restricted
1950      * alphabet table.
1951      * Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
1952      *
1953      * @param alphabet the alphabet defining the mapping between characters and
1954      *        integer values.
1955      * @param ch the array of characters.
1956      * @param offset the offset into the array of characters.
1957      * @param length the length of characters.
1958      */
1959     protected final void encodeNonEmptyNBitCharacterStringOnSeventhBit(String alphabet, char[] ch, int offset, int length) throws FastInfosetException, IOException {
1960         int bitsPerCharacter = 1;
1961         while ((1 << bitsPerCharacter) <= alphabet.length()) {
1962             bitsPerCharacter++;
1963         }
1964 
1965         final int bits = length * bitsPerCharacter;
1966         final int octets = bits / 8;
1967         final int bitsOfLastOctet = bits % 8;
1968         final int totalOctets = octets + ((bitsOfLastOctet > 0) ? 1 : 0);
1969 
1970         // Encode the length
1971         encodeNonZeroOctetStringLengthOnSenventhBit(totalOctets);
1972 
1973         resetBits();
1974         ensureSize(totalOctets);
1975         int v = 0;
1976         for (int i = 0; i < length; i++) {
1977             final char c = ch[offset + i];
1978             // This is grotesquely slow, need to use hash table of character to int value
1979             for (v = 0; v < alphabet.length(); v++) {
1980                 if (c == alphabet.charAt(v)) {
1981                     break;
1982                 }
1983             }
1984             if (v == alphabet.length()) {
1985                 throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.characterOutofAlphabetRange"));
1986             }
1987             writeBits(bitsPerCharacter, v);
1988         }
1989 
1990         if (bitsOfLastOctet > 0) {
1991             _b |= (1 << (8 - bitsOfLastOctet)) - 1;
1992             write(_b);
1993         }
1994     }
1995 
1996     private int _bitsLeftInOctet;
1997 
1998     private final void resetBits() {
1999         _bitsLeftInOctet = 8;
2000         _b = 0;
2001     }
2002 
2003     private final void writeBits(int bits, int v) throws IOException {
2004         while (bits > 0) {
2005             final int bit = (v & (1 << --bits)) > 0 ? 1 : 0;
2006             _b |= bit << (--_bitsLeftInOctet);
2007             if (_bitsLeftInOctet == 0) {
2008                 write(_b);
2009                 _bitsLeftInOctet = 8;
2010                 _b = 0;
2011             }
2012         }
2013     }
2014 
2015     /**
2016      * Encode the length of a encoded string on the seventh bit
2017      * of an octet.
2018      * Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
2019      *
2020      * @param length the length to encode.
2021      */
2022     protected final void encodeNonZeroOctetStringLengthOnSenventhBit(int length) throws IOException {
2023         if (length < EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_SMALL_LIMIT) {
2024             // [1, 2]
2025             write(_b | (length - 1));
2026         } else if (length < EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_MEDIUM_LIMIT) {
2027             // [3, 258]
2028             write(_b | EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_MEDIUM_FLAG); // 00000010
2029             write(length - EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_SMALL_LIMIT);
2030         } else {
2031             // [259, 4294967296]
2032             write(_b | EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_LARGE_FLAG); // 00000011
2033             length -= EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_MEDIUM_LIMIT;
2034             write(length >>> 24);
2035             write((length >> 16) & 0xFF);
2036             write((length >> 8) & 0xFF);
2037             write(length & 0xFF);
2038         }
2039     }
2040 
2041     /**
2042      * Encode a non zero integer on the second bit of an octet, setting
2043      * the first bit to 1.
2044      * Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
2045      *
2046      * <p>
2047      * The first bit of the first octet is set, as specified in clause C.13 of
2048      * ITU-T Rec. X.891 | ISO/IEC 24824-1
2049      *
2050      * @param i The integer to encode, which is a member of the interval
2051      *          [0, 1048575]. In the specification the interval is [1, 1048576]
2052      *
2053      */
2054     protected final void encodeNonZeroIntegerOnSecondBitFirstBitOne(int i) throws IOException {
2055         if (i < EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT) {
2056             // [1, 64] ( [0, 63] ) 6 bits
2057             write(0x80 | i);
2058         } else if (i < EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT) {
2059             // [65, 8256] ( [64, 8255] ) 13 bits
2060             i -= EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT;
2061             _b = (0x80 | EncodingConstants.INTEGER_2ND_BIT_MEDIUM_FLAG) | (i >> 8); // 010 00000
2062             // _b = 0xC0 | (i >> 8); // 010 00000
2063             write(_b);
2064             write(i & 0xFF);
2065         } else if (i < EncodingConstants.INTEGER_2ND_BIT_LARGE_LIMIT) {
2066             // [8257, 1048576] ( [8256, 1048575] ) 20 bits
2067             i -= EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT;
2068             _b = (0x80 | EncodingConstants.INTEGER_2ND_BIT_LARGE_FLAG) | (i >> 16); // 0110 0000
2069             // _b = 0xE0 | (i >> 16); // 0110 0000
2070             write(_b);
2071             write((i >> 8) & 0xFF);
2072             write(i & 0xFF);
2073         } else {
2074             throw new IOException(
2075                     CommonResourceBundle.getInstance().getString("message.integerMaxSize",
2076                     new Object[]{Integer.valueOf(EncodingConstants.INTEGER_2ND_BIT_LARGE_LIMIT)}));
2077         }
2078     }
2079 
2080     /**
2081      * Encode a non zero integer on the second bit of an octet, setting
2082      * the first bit to 0.
2083      * Implementation of clause C.25 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
2084      *
2085      * <p>
2086      * The first bit of the first octet is set, as specified in clause C.13 of
2087      * ITU-T Rec. X.891 | ISO/IEC 24824-1
2088      *
2089      * @param i The integer to encode, which is a member of the interval
2090      *          [0, 1048575]. In the specification the interval is [1, 1048576]
2091      *
2092      */
2093     protected final void encodeNonZeroIntegerOnSecondBitFirstBitZero(int i) throws IOException {
2094         if (i < EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT) {
2095             // [1, 64] ( [0, 63] ) 6 bits
2096             write(i);
2097         } else if (i < EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT) {
2098             // [65, 8256] ( [64, 8255] ) 13 bits
2099             i -= EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT;
2100             _b = EncodingConstants.INTEGER_2ND_BIT_MEDIUM_FLAG | (i >> 8); // 010 00000
2101             write(_b);
2102             write(i & 0xFF);
2103         } else {
2104             // [8257, 1048576] ( [8256, 1048575] ) 20 bits
2105             i -= EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT;
2106             _b = EncodingConstants.INTEGER_2ND_BIT_LARGE_FLAG | (i >> 16); // 0110 0000
2107             write(_b);
2108             write((i >> 8) & 0xFF);
2109             write(i & 0xFF);
2110         }
2111     }
2112 
2113     /**
2114      * Encode a non zero integer on the third bit of an octet.
2115      * Implementation of clause C.27 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
2116      *
2117      * @param i The integer to encode, which is a member of the interval
2118      *          [0, 1048575]. In the specification the interval is [1, 1048576]
2119      *
2120      */
2121     protected final void encodeNonZeroIntegerOnThirdBit(int i) throws IOException {
2122         if (i < EncodingConstants.INTEGER_3RD_BIT_SMALL_LIMIT) {
2123             // [1, 32] ( [0, 31] ) 5 bits
2124             write(_b | i);
2125         } else if (i < EncodingConstants.INTEGER_3RD_BIT_MEDIUM_LIMIT) {
2126             // [33, 2080] ( [32, 2079] ) 11 bits
2127             i -= EncodingConstants.INTEGER_3RD_BIT_SMALL_LIMIT;
2128             _b |= EncodingConstants.INTEGER_3RD_BIT_MEDIUM_FLAG | (i >> 8); // 00100 000
2129             write(_b);
2130             write(i & 0xFF);
2131         } else if (i < EncodingConstants.INTEGER_3RD_BIT_LARGE_LIMIT) {
2132             // [2081, 526368] ( [2080, 526367] ) 19 bits
2133             i -= EncodingConstants.INTEGER_3RD_BIT_MEDIUM_LIMIT;
2134             _b |= EncodingConstants.INTEGER_3RD_BIT_LARGE_FLAG | (i >> 16); // 00101 000
2135             write(_b);
2136             write((i >> 8) & 0xFF);
2137             write(i & 0xFF);
2138         } else {
2139             // [526369, 1048576] ( [526368, 1048575] ) 20 bits
2140             i -= EncodingConstants.INTEGER_3RD_BIT_LARGE_LIMIT;
2141             _b |= EncodingConstants.INTEGER_3RD_BIT_LARGE_LARGE_FLAG; // 00110 000
2142             write(_b);
2143             write(i >> 16);
2144             write((i >> 8) & 0xFF);
2145             write(i & 0xFF);
2146         }
2147     }
2148 
2149     /**
2150      * Encode a non zero integer on the fourth bit of an octet.
2151      * Implementation of clause C.28 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
2152      *
2153      * @param i The integer to encode, which is a member of the interval
2154      *          [0, 1048575]. In the specification the interval is [1, 1048576]
2155      *
2156      */
2157     protected final void encodeNonZeroIntegerOnFourthBit(int i) throws IOException {
2158         if (i < EncodingConstants.INTEGER_4TH_BIT_SMALL_LIMIT) {
2159             // [1, 16] ( [0, 15] ) 4 bits
2160             write(_b | i);
2161         } else if (i < EncodingConstants.INTEGER_4TH_BIT_MEDIUM_LIMIT) {
2162             // [17, 1040] ( [16, 1039] ) 10 bits
2163             i -= EncodingConstants.INTEGER_4TH_BIT_SMALL_LIMIT;
2164             _b |= EncodingConstants.INTEGER_4TH_BIT_MEDIUM_FLAG | (i >> 8); // 000 100 00
2165             write(_b);
2166             write(i & 0xFF);
2167         } else if (i < EncodingConstants.INTEGER_4TH_BIT_LARGE_LIMIT) {
2168             // [1041, 263184] ( [1040, 263183] ) 18 bits
2169             i -= EncodingConstants.INTEGER_4TH_BIT_MEDIUM_LIMIT;
2170             _b |= EncodingConstants.INTEGER_4TH_BIT_LARGE_FLAG | (i >> 16); // 000 101 00
2171             write(_b);
2172             write((i >> 8) & 0xFF);
2173             write(i & 0xFF);
2174         } else {
2175             // [263185, 1048576] ( [263184, 1048575] ) 20 bits
2176             i -= EncodingConstants.INTEGER_4TH_BIT_LARGE_LIMIT;
2177             _b |= EncodingConstants.INTEGER_4TH_BIT_LARGE_LARGE_FLAG; // 000 110 00
2178             write(_b);
2179             write(i >> 16);
2180             write((i >> 8) & 0xFF);
2181             write(i & 0xFF);
2182         }
2183     }
2184 
2185     /**
2186      * Encode a non empty string using the UTF-8 encoding.
2187      *
2188      * @param b the current octet that is being written.
2189      * @param s the string to be UTF-8 encoded.
2190      * @param constants the array of constants to use when encoding to determin
2191      *        how the length of the UTF-8 encoded string is encoded.
2192      */
2193     protected final void encodeNonEmptyUTF8StringAsOctetString(int b, String s, int[] constants) throws IOException {
2194         final char[] ch = s.toCharArray();
2195         encodeNonEmptyUTF8StringAsOctetString(b, ch, 0, ch.length, constants);
2196     }
2197 
2198     /**
2199      * Encode a non empty string using the UTF-8 encoding.
2200      *
2201      * @param b the current octet that is being written.
2202      * @param ch the array of characters.
2203      * @param offset the offset into the array of characters.
2204      * @param length the length of characters.
2205      *        how the length of the UTF-8 encoded string is encoded.
2206      * @param constants the array of constants to use when encoding to determin
2207      *        how the length of the UTF-8 encoded string is encoded.
2208      */
2209     protected final void encodeNonEmptyUTF8StringAsOctetString(int b, char ch[], int offset, int length, int[] constants) throws IOException {
2210         length = encodeUTF8String(ch, offset, length);
2211         encodeNonZeroOctetStringLength(b, length, constants);
2212         write(_encodingBuffer, length);
2213     }
2214 
2215     /**
2216      * Encode the length of non empty UTF-8 encoded string.
2217      *
2218      * @param b the current octet that is being written.
2219      * @param length the length of the UTF-8 encoded string.
2220      *        how the length of the UTF-8 encoded string is encoded.
2221      * @param constants the array of constants to use when encoding to determin
2222      *        how the length of the UTF-8 encoded string is encoded.
2223      */
2224     protected final void encodeNonZeroOctetStringLength(int b, int length, int[] constants) throws IOException {
2225         if (length < constants[EncodingConstants.OCTET_STRING_LENGTH_SMALL_LIMIT]) {
2226             write(b | (length - 1));
2227         } else if (length < constants[EncodingConstants.OCTET_STRING_LENGTH_MEDIUM_LIMIT]) {
2228             write(b | constants[EncodingConstants.OCTET_STRING_LENGTH_MEDIUM_FLAG]);
2229             write(length - constants[EncodingConstants.OCTET_STRING_LENGTH_SMALL_LIMIT]);
2230         } else {
2231             write(b | constants[EncodingConstants.OCTET_STRING_LENGTH_LARGE_FLAG]);
2232             length -= constants[EncodingConstants.OCTET_STRING_LENGTH_MEDIUM_LIMIT];
2233             write(length >>> 24);
2234             write((length >> 16) & 0xFF);
2235             write((length >> 8) & 0xFF);
2236             write(length & 0xFF);
2237         }
2238     }
2239 
2240     /**
2241      * Encode a non zero integer.
2242      *
2243      * @param b the current octet that is being written.
2244      * @param i the non zero integer.
2245      * @param constants the array of constants to use when encoding to determin
2246      *        how the non zero integer is encoded.
2247      */
2248     protected final void encodeNonZeroInteger(int b, int i, int[] constants) throws IOException {
2249         if (i < constants[EncodingConstants.INTEGER_SMALL_LIMIT]) {
2250             write(b | i);
2251         } else if (i < constants[EncodingConstants.INTEGER_MEDIUM_LIMIT]) {
2252             i -= constants[EncodingConstants.INTEGER_SMALL_LIMIT];
2253             write(b | constants[EncodingConstants.INTEGER_MEDIUM_FLAG] | (i >> 8));
2254             write(i & 0xFF);
2255         } else if (i < constants[EncodingConstants.INTEGER_LARGE_LIMIT]) {
2256             i -= constants[EncodingConstants.INTEGER_MEDIUM_LIMIT];
2257             write(b | constants[EncodingConstants.INTEGER_LARGE_FLAG] | (i >> 16));
2258             write((i >> 8) & 0xFF);
2259             write(i & 0xFF);
2260         } else if (i < EncodingConstants.INTEGER_MAXIMUM_SIZE) {
2261             i -= constants[EncodingConstants.INTEGER_LARGE_LIMIT];
2262             write(b | constants[EncodingConstants.INTEGER_LARGE_LARGE_FLAG]);
2263             write(i >> 16);
2264             write((i >> 8) & 0xFF);
2265             write(i & 0xFF);
2266         } else {
2267             throw new IOException(CommonResourceBundle.getInstance().getString("message.integerMaxSize", new Object[]{Integer.valueOf(EncodingConstants.INTEGER_MAXIMUM_SIZE)}));
2268         }
2269     }
2270 
2271     /**
2272      * Mark the current position in the buffered stream.
2273      */
2274     protected final void mark() {
2275         _markIndex = _octetBufferIndex;
2276     }
2277 
2278     /**
2279      * Reset the marked position in the buffered stream.
2280      */
2281     protected final void resetMark() {
2282         _markIndex = -1;
2283     }
2284 
2285     /**
2286      * @return true if the mark has been set, otherwise false if the mark
2287      *         has not been set.
2288      */
2289     protected final boolean hasMark() {
2290         return _markIndex != -1;
2291     }
2292 
2293     /**
2294      * Write a byte to the buffered stream.
2295      */
2296     protected final void write(int i) throws IOException {
2297         if (_octetBufferIndex < _octetBuffer.length) {
2298             _octetBuffer[_octetBufferIndex++] = (byte)i;
2299         } else {
2300             if (_markIndex == -1) {
2301                 _s.write(_octetBuffer);
2302                 _octetBufferIndex = 1;
2303                 _octetBuffer[0] = (byte)i;
2304             } else {
2305                 resize(_octetBuffer.length * 3 / 2);
2306                 _octetBuffer[_octetBufferIndex++] = (byte)i;
2307             }
2308         }
2309     }
2310 
2311     /**
2312      * Write an array of bytes to the buffered stream.
2313      *
2314      * @param b the array of bytes.
2315      * @param length the length of bytes.
2316      */
2317     protected final void write(byte[] b, int length) throws IOException {
2318         write(b, 0,  length);
2319     }
2320 
2321     /**
2322      * Write an array of bytes to the buffered stream.
2323      *
2324      * @param b the array of bytes.
2325      * @param offset the offset into the array of bytes.
2326      * @param length the length of bytes.
2327      */
2328     protected final void write(byte[] b, int offset, int length) throws IOException {
2329         if ((_octetBufferIndex + length) < _octetBuffer.length) {
2330             System.arraycopy(b, offset, _octetBuffer, _octetBufferIndex, length);
2331             _octetBufferIndex += length;
2332         } else {
2333             if (_markIndex == -1) {
2334                 _s.write(_octetBuffer, 0, _octetBufferIndex);
2335                 _s.write(b, offset, length);
2336                 _octetBufferIndex = 0;
2337             } else {
2338                 resize((_octetBuffer.length + length) * 3 / 2 + 1);
2339                 System.arraycopy(b, offset, _octetBuffer, _octetBufferIndex, length);
2340                 _octetBufferIndex += length;
2341             }
2342         }
2343     }
2344 
2345     private void ensureSize(int length) {
2346         if ((_octetBufferIndex + length) > _octetBuffer.length) {
2347             resize((_octetBufferIndex + length) * 3 / 2 + 1);
2348         }
2349     }
2350 
2351     private void resize(int length) {
2352         byte[] b = new byte[length];
2353         System.arraycopy(_octetBuffer, 0, b, 0, _octetBufferIndex);
2354         _octetBuffer = b;
2355     }
2356 
2357     private void _flush() throws IOException {
2358         if (_octetBufferIndex > 0) {
2359             _s.write(_octetBuffer, 0, _octetBufferIndex);
2360             _octetBufferIndex = 0;
2361         }
2362     }
2363 
2364 
2365     private EncodingBufferOutputStream _encodingBufferOutputStream = new EncodingBufferOutputStream();
2366 
2367     private byte[] _encodingBuffer = new byte[512];
2368 
2369     private int _encodingBufferIndex;
2370 
2371     private class EncodingBufferOutputStream extends OutputStream {
2372 
2373         public void write(int b) throws IOException {
2374             if (_encodingBufferIndex < _encodingBuffer.length) {
2375                 _encodingBuffer[_encodingBufferIndex++] = (byte)b;
2376             } else {
2377                 byte newbuf[] = new byte[Math.max(_encodingBuffer.length << 1, _encodingBufferIndex)];
2378                 System.arraycopy(_encodingBuffer, 0, newbuf, 0, _encodingBufferIndex);
2379                 _encodingBuffer = newbuf;
2380 
2381                 _encodingBuffer[_encodingBufferIndex++] = (byte)b;
2382             }
2383         }
2384 
2385         public void write(byte b[], int off, int len) throws IOException {
2386             if ((off < 0) || (off > b.length) || (len < 0) ||
2387                 ((off + len) > b.length) || ((off + len) < 0)) {
2388                 throw new IndexOutOfBoundsException();
2389             } else if (len == 0) {
2390                 return;
2391             }
2392             final int newoffset = _encodingBufferIndex + len;
2393             if (newoffset > _encodingBuffer.length) {
2394                 byte newbuf[] = new byte[Math.max(_encodingBuffer.length << 1, newoffset)];
2395                 System.arraycopy(_encodingBuffer, 0, newbuf, 0, _encodingBufferIndex);
2396                 _encodingBuffer = newbuf;
2397             }
2398             System.arraycopy(b, off, _encodingBuffer, _encodingBufferIndex, len);
2399             _encodingBufferIndex = newoffset;
2400         }
2401 
2402         public int getLength() {
2403             return _encodingBufferIndex;
2404         }
2405 
2406         public void reset() {
2407             _encodingBufferIndex = 0;
2408         }
2409     }
2410 
2411     /**
2412      * Encode a string using the UTF-8 encoding.
2413      *
2414      * @param s the string to encode.
2415      */
2416     protected final int encodeUTF8String(String s) throws IOException {
2417         final int length = s.length();
2418         if (length < _charBuffer.length) {
2419             s.getChars(0, length, _charBuffer, 0);
2420             return encodeUTF8String(_charBuffer, 0, length);
2421         } else {
2422             char[] ch = s.toCharArray();
2423             return encodeUTF8String(ch, 0, length);
2424         }
2425     }
2426 
2427     private void ensureEncodingBufferSizeForUtf8String(int length) {
2428         final int newLength = 4 * length;
2429         if (_encodingBuffer.length < newLength) {
2430             _encodingBuffer = new byte[newLength];
2431         }
2432     }
2433 
2434     /**
2435      * Encode a string using the UTF-8 encoding.
2436      *
2437      * @param ch the array of characters.
2438      * @param offset the offset into the array of characters.
2439      * @param length the length of characters.
2440      */
2441     protected final int encodeUTF8String(char[] ch, int offset, int length) throws IOException {
2442         int bpos = 0;
2443 
2444         // Make sure buffer is large enough
2445         ensureEncodingBufferSizeForUtf8String(length);
2446 
2447         final int end = offset + length;
2448         int c;
2449         while (end != offset) {
2450             c = ch[offset++];
2451             if (c < 0x80) {
2452                 // 1 byte, 7 bits
2453                 _encodingBuffer[bpos++] = (byte) c;
2454             } else if (c < 0x800) {
2455                 // 2 bytes, 11 bits
2456                 _encodingBuffer[bpos++] =
2457                     (byte) (0xC0 | (c >> 6));    // first 5
2458                 _encodingBuffer[bpos++] =
2459                     (byte) (0x80 | (c & 0x3F));  // second 6
2460             } else if (c <= '\uFFFF') {
2461                 if (!XMLChar.isHighSurrogate(c) && !XMLChar.isLowSurrogate(c)) {
2462                     // 3 bytes, 16 bits
2463                     _encodingBuffer[bpos++] =
2464                         (byte) (0xE0 | (c >> 12));   // first 4
2465                     _encodingBuffer[bpos++] =
2466                         (byte) (0x80 | ((c >> 6) & 0x3F));  // second 6
2467                     _encodingBuffer[bpos++] =
2468                         (byte) (0x80 | (c & 0x3F));  // third 6
2469                 } else {
2470                     // 4 bytes, high and low surrogate
2471                     encodeCharacterAsUtf8FourByte(c, ch, offset, end, bpos);
2472                     bpos += 4;
2473                     offset++;
2474                 }
2475             }
2476         }
2477 
2478         return bpos;
2479     }
2480 
2481     private void encodeCharacterAsUtf8FourByte(int c, char[] ch, int chpos, int chend, int bpos) throws IOException {
2482         if (chpos == chend) {
2483             throw new IOException("");
2484         }
2485 
2486         final char d = ch[chpos];
2487         if (!XMLChar.isLowSurrogate(d)) {
2488             throw new IOException("");
2489         }
2490 
2491         final int uc = (((c & 0x3ff) << 10) | (d & 0x3ff)) + 0x10000;
2492         if (uc < 0 || uc >= 0x200000) {
2493             throw new IOException("");
2494         }
2495 
2496         _encodingBuffer[bpos++] = (byte)(0xF0 | ((uc >> 18)));
2497         _encodingBuffer[bpos++] = (byte)(0x80 | ((uc >> 12) & 0x3F));
2498         _encodingBuffer[bpos++] = (byte)(0x80 | ((uc >> 6) & 0x3F));
2499         _encodingBuffer[bpos++] = (byte)(0x80 | (uc & 0x3F));
2500     }
2501 
2502     /**
2503      * Encode a string using the UTF-16 encoding.
2504      *
2505      * @param s the string to encode.
2506      */
2507     protected final int encodeUtf16String(String s) throws IOException {
2508         final int length = s.length();
2509         if (length < _charBuffer.length) {
2510             s.getChars(0, length, _charBuffer, 0);
2511             return encodeUtf16String(_charBuffer, 0, length);
2512         } else {
2513             char[] ch = s.toCharArray();
2514             return encodeUtf16String(ch, 0, length);
2515         }
2516     }
2517 
2518     private void ensureEncodingBufferSizeForUtf16String(int length) {
2519         final int newLength = 2 * length;
2520         if (_encodingBuffer.length < newLength) {
2521             _encodingBuffer = new byte[newLength];
2522         }
2523     }
2524 
2525     /**
2526      * Encode a string using the UTF-16 encoding.
2527      *
2528      * @param ch the array of characters.
2529      * @param offset the offset into the array of characters.
2530      * @param length the length of characters.
2531      */
2532     protected final int encodeUtf16String(char[] ch, int offset, int length) throws IOException {
2533         int byteLength = 0;
2534 
2535         // Make sure buffer is large enough
2536         ensureEncodingBufferSizeForUtf16String(length);
2537 
2538         final int n = offset + length;
2539         for (int i = offset; i < n; i++) {
2540             final int c = (int) ch[i];
2541             _encodingBuffer[byteLength++] = (byte)(c >> 8);
2542             _encodingBuffer[byteLength++] = (byte)(c & 0xFF);
2543         }
2544 
2545         return byteLength;
2546     }
2547 
2548     /**
2549      * Obtain the prefix from a qualified name.
2550      *
2551      * @param qName the qualified name
2552      * @return the prefix, or "" if there is no prefix.
2553      */
2554     public static String getPrefixFromQualifiedName(String qName) {
2555         int i = qName.indexOf(':');
2556         String prefix = "";
2557         if (i != -1) {
2558             prefix = qName.substring(0, i);
2559         }
2560         return prefix;
2561     }
2562 
2563     /**
2564      * Check if character array contains characters that are all white space.
2565      *
2566      * @param ch the character array
2567      * @param start the starting character index into the array to check from
2568      * @param length the number of characters to check
2569      * @return true if all characters are white space, false otherwise
2570      */
2571     public static boolean isWhiteSpace(final char[] ch, int start, final int length) {
2572         if (!XMLChar.isSpace(ch[start])) return false;
2573 
2574         final int end = start + length;
2575         while(++start < end && XMLChar.isSpace(ch[start]));
2576 
2577         return start == end;
2578     }
2579 
2580     /**
2581      * Check if a String contains characters that are all white space.
2582      *
2583      * @param s the string
2584      * @return true if all characters are white space, false otherwise
2585      */
2586     public static boolean isWhiteSpace(String s) {
2587         if (!XMLChar.isSpace(s.charAt(0))) return false;
2588 
2589         final int end = s.length();
2590         int start = 1;
2591         while(start < end && XMLChar.isSpace(s.charAt(start++)));
2592         return start == end;
2593     }
2594 }