blob: ac5495442e906633ae23b60ba871b8477e33868c [file] [log] [blame]
/*
* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
* THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC.
*/
package com.sun.xml.internal.fastinfoset;
import com.sun.xml.internal.fastinfoset.algorithm.BuiltInEncodingAlgorithmFactory;
import com.sun.xml.internal.fastinfoset.org.apache.xerces.util.XMLChar;
import com.sun.xml.internal.fastinfoset.util.CharArrayIntMap;
import com.sun.xml.internal.fastinfoset.util.KeyIntMap;
import com.sun.xml.internal.fastinfoset.util.LocalNameQualifiedNamesMap;
import com.sun.xml.internal.fastinfoset.util.StringIntMap;
import com.sun.xml.internal.fastinfoset.vocab.SerializerVocabulary;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithm;
import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmException;
import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmIndexes;
import com.sun.xml.internal.org.jvnet.fastinfoset.ExternalVocabulary;
import com.sun.xml.internal.org.jvnet.fastinfoset.FastInfosetException;
import com.sun.xml.internal.org.jvnet.fastinfoset.FastInfosetSerializer;
import com.sun.xml.internal.org.jvnet.fastinfoset.RestrictedAlphabet;
import com.sun.xml.internal.org.jvnet.fastinfoset.VocabularyApplicationData;
import org.xml.sax.helpers.DefaultHandler;
/**
* Abstract encoder for developing concrete encoders.
*
* Concrete implementations extending Encoder will utilize methods on Encoder
* to encode XML infoset according to the Fast Infoset standard. It is the
* responsibility of the concrete implementation to ensure that methods are
* invoked in the correct order to produce a valid fast infoset document.
*
* <p>
* This class extends org.sax.xml.DefaultHandler so that concrete SAX
* implementations can be used with javax.xml.parsers.SAXParser and the parse
* methods that take org.sax.xml.DefaultHandler as a parameter.
*
* <p>
* Buffering of octets that are written to an {@link java.io.OutputStream} is
* supported in a similar manner to a {@link java.io.BufferedOutputStream}.
* Combining buffering with encoding enables better performance.
*
* <p>
* More than one fast infoset document may be encoded to the
* {@link java.io.OutputStream}.
*
*/
public abstract class Encoder extends DefaultHandler implements FastInfosetSerializer {
/**
* Character encoding scheme system property for the encoding
* of content and attribute values.
*/
public static final String CHARACTER_ENCODING_SCHEME_SYSTEM_PROPERTY =
"com.sun.xml.internal.fastinfoset.serializer.character-encoding-scheme";
/**
* Default character encoding scheme system property for the encoding
* of content and attribute values.
*/
protected static final String _characterEncodingSchemeSystemDefault = getDefaultEncodingScheme();
private static String getDefaultEncodingScheme() {
String p = System.getProperty(CHARACTER_ENCODING_SCHEME_SYSTEM_PROPERTY,
UTF_8);
if (p.equals(UTF_16BE)) {
return UTF_16BE;
} else {
return UTF_8;
}
}
private static int[] NUMERIC_CHARACTERS_TABLE;
private static int[] DATE_TIME_CHARACTERS_TABLE;
static {
NUMERIC_CHARACTERS_TABLE = new int[maxCharacter(RestrictedAlphabet.NUMERIC_CHARACTERS) + 1];
DATE_TIME_CHARACTERS_TABLE = new int[maxCharacter(RestrictedAlphabet.DATE_TIME_CHARACTERS) + 1];
for (int i = 0; i < NUMERIC_CHARACTERS_TABLE.length ; i++) {
NUMERIC_CHARACTERS_TABLE[i] = -1;
}
for (int i = 0; i < DATE_TIME_CHARACTERS_TABLE.length ; i++) {
DATE_TIME_CHARACTERS_TABLE[i] = -1;
}
for (int i = 0; i < RestrictedAlphabet.NUMERIC_CHARACTERS.length() ; i++) {
NUMERIC_CHARACTERS_TABLE[RestrictedAlphabet.NUMERIC_CHARACTERS.charAt(i)] = i;
}
for (int i = 0; i < RestrictedAlphabet.DATE_TIME_CHARACTERS.length() ; i++) {
DATE_TIME_CHARACTERS_TABLE[RestrictedAlphabet.DATE_TIME_CHARACTERS.charAt(i)] = i;
}
}
private static int maxCharacter(String alphabet) {
int c = 0;
for (int i = 0; i < alphabet.length() ; i++) {
if (c < alphabet.charAt(i)) {
c = alphabet.charAt(i);
}
}
return c;
}
/**
* True if DTD and internal subset shall be ignored.
*/
private boolean _ignoreDTD;
/**
* True if comments shall be ignored.
*/
private boolean _ignoreComments;
/**
* True if procesing instructions shall be ignored.
*/
private boolean _ignoreProcessingInstructions;
/**
* True if white space characters for text content shall be ignored.
*/
private boolean _ignoreWhiteSpaceTextContent;
/**
* True, if the local name string is used as the key to find the
* associated set of qualified names.
* <p>
* False, if the <prefix>:<local name> string is used as the key
* to find the associated set of qualified names.
*/
private boolean _useLocalNameAsKeyForQualifiedNameLookup;
/**
* True if strings for text content and attribute values will be
* UTF-8 encoded otherwise they will be UTF-16 encoded.
*/
private boolean _encodingStringsAsUtf8 = true;
/**
* Encoding constant generated from the string encoding.
*/
private int _nonIdentifyingStringOnThirdBitCES;
/**
* Encoding constant generated from the string encoding.
*/
private int _nonIdentifyingStringOnFirstBitCES;
/**
* The map of URIs to algorithms.
*/
private Map _registeredEncodingAlgorithms = new HashMap();
/**
* The vocabulary that is used by the encoder
*/
protected SerializerVocabulary _v;
/**
* The vocabulary application data that is used by the encoder
*/
protected VocabularyApplicationData _vData;
/**
* True if the vocubulary is internal to the encoder
*/
private boolean _vIsInternal;
/**
* True if terminatation of an information item is required
*/
protected boolean _terminate = false;
/**
* The current octet that is to be written.
*/
protected int _b;
/**
* The {@link java.io.OutputStream} that the encoded XML infoset (the
* fast infoset document) is written to.
*/
protected OutputStream _s;
/**
* The internal buffer of characters used for the UTF-8 or UTF-16 encoding
* of characters.
*/
protected char[] _charBuffer = new char[512];
/**
* The internal buffer of bytes.
*/
protected byte[] _octetBuffer = new byte[1024];
/**
* The current position in the internal buffer.
*/
protected int _octetBufferIndex;
/**
* The current mark in the internal buffer.
*
* <p>
* If the value of the mark is < 0 then the mark is not set.
*/
protected int _markIndex = -1;
/**
* The minimum size of [normalized value] of Attribute Information
* Items that will be indexed.
*/
protected int minAttributeValueSize = FastInfosetSerializer.MIN_ATTRIBUTE_VALUE_SIZE;
/**
* The maximum size of [normalized value] of Attribute Information
* Items that will be indexed.
*/
protected int maxAttributeValueSize = FastInfosetSerializer.MAX_ATTRIBUTE_VALUE_SIZE;
/**
* The limit on the size of indexed Map for attribute values
* Limit is measured in characters number
*/
protected int attributeValueMapTotalCharactersConstraint = FastInfosetSerializer.ATTRIBUTE_VALUE_MAP_MEMORY_CONSTRAINT / 2;
/**
* The minimum size of character content chunks
* of Character Information Items or Comment Information Items that
* will be indexed.
*/
protected int minCharacterContentChunkSize = FastInfosetSerializer.MIN_CHARACTER_CONTENT_CHUNK_SIZE;
/**
* The maximum size of character content chunks
* of Character Information Items or Comment Information Items that
* will be indexed.
*/
protected int maxCharacterContentChunkSize = FastInfosetSerializer.MAX_CHARACTER_CONTENT_CHUNK_SIZE;
/**
* The limit on the size of indexed Map for character content chunks
* Limit is measured in characters number
*/
protected int characterContentChunkMapTotalCharactersConstraint = FastInfosetSerializer.CHARACTER_CONTENT_CHUNK_MAP_MEMORY_CONSTRAINT / 2;
/**
* Default constructor for the Encoder.
*/
protected Encoder() {
setCharacterEncodingScheme(_characterEncodingSchemeSystemDefault);
}
protected Encoder(boolean useLocalNameAsKeyForQualifiedNameLookup) {
setCharacterEncodingScheme(_characterEncodingSchemeSystemDefault);
_useLocalNameAsKeyForQualifiedNameLookup = useLocalNameAsKeyForQualifiedNameLookup;
}
// FastInfosetSerializer interface
/**
* {@inheritDoc}
*/
public final void setIgnoreDTD(boolean ignoreDTD) {
_ignoreDTD = ignoreDTD;
}
/**
* {@inheritDoc}
*/
public final boolean getIgnoreDTD() {
return _ignoreDTD;
}
/**
* {@inheritDoc}
*/
public final void setIgnoreComments(boolean ignoreComments) {
_ignoreComments = ignoreComments;
}
/**
* {@inheritDoc}
*/
public final boolean getIgnoreComments() {
return _ignoreComments;
}
/**
* {@inheritDoc}
*/
public final void setIgnoreProcesingInstructions(boolean
ignoreProcesingInstructions) {
_ignoreProcessingInstructions = ignoreProcesingInstructions;
}
/**
* {@inheritDoc}
*/
public final boolean getIgnoreProcesingInstructions() {
return _ignoreProcessingInstructions;
}
/**
* {@inheritDoc}
*/
public final void setIgnoreWhiteSpaceTextContent(boolean ignoreWhiteSpaceTextContent) {
_ignoreWhiteSpaceTextContent = ignoreWhiteSpaceTextContent;
}
/**
* {@inheritDoc}
*/
public final boolean getIgnoreWhiteSpaceTextContent() {
return _ignoreWhiteSpaceTextContent;
}
/**
* {@inheritDoc}
*/
public void setCharacterEncodingScheme(String characterEncodingScheme) {
if (characterEncodingScheme.equals(UTF_16BE)) {
_encodingStringsAsUtf8 = false;
_nonIdentifyingStringOnThirdBitCES = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_UTF_16_FLAG;
_nonIdentifyingStringOnFirstBitCES = EncodingConstants.NISTRING_UTF_16_FLAG;
} else {
_encodingStringsAsUtf8 = true;
_nonIdentifyingStringOnThirdBitCES = EncodingConstants.CHARACTER_CHUNK;
_nonIdentifyingStringOnFirstBitCES = 0;
}
}
/**
* {@inheritDoc}
*/
public String getCharacterEncodingScheme() {
return (_encodingStringsAsUtf8) ? UTF_8 : UTF_16BE;
}
/**
* {@inheritDoc}
*/
public void setRegisteredEncodingAlgorithms(Map algorithms) {
_registeredEncodingAlgorithms = algorithms;
if (_registeredEncodingAlgorithms == null) {
_registeredEncodingAlgorithms = new HashMap();
}
}
/**
* {@inheritDoc}
*/
public Map getRegisteredEncodingAlgorithms() {
return _registeredEncodingAlgorithms;
}
/**
* {@inheritDoc}
*/
public int getMinCharacterContentChunkSize() {
return minCharacterContentChunkSize;
}
/**
* {@inheritDoc}
*/
public void setMinCharacterContentChunkSize(int size) {
if (size < 0 ) {
size = 0;
}
minCharacterContentChunkSize = size;
}
/**
* {@inheritDoc}
*/
public int getMaxCharacterContentChunkSize() {
return maxCharacterContentChunkSize;
}
/**
* {@inheritDoc}
*/
public void setMaxCharacterContentChunkSize(int size) {
if (size < 0 ) {
size = 0;
}
maxCharacterContentChunkSize = size;
}
/**
* {@inheritDoc}
*/
public int getCharacterContentChunkMapMemoryLimit() {
return characterContentChunkMapTotalCharactersConstraint * 2;
}
/**
* {@inheritDoc}
*/
public void setCharacterContentChunkMapMemoryLimit(int size) {
if (size < 0 ) {
size = 0;
}
characterContentChunkMapTotalCharactersConstraint = size / 2;
}
/**
* Checks whether character content chunk (its length) matches length limit
*
* @param length the length of character content chunk is checking to be added to Map.
* @return whether character content chunk length matches limit
*/
public boolean isCharacterContentChunkLengthMatchesLimit(int length) {
return length >= minCharacterContentChunkSize &&
length < maxCharacterContentChunkSize;
}
/**
* Checks whether character content table has enough memory to
* store character content chunk with the given length
*
* @param length the length of character content chunk is checking to be added to Map.
* @param map the custom CharArrayIntMap, which memory limits will be checked.
* @return whether character content map has enough memory
*/
public boolean canAddCharacterContentToTable(int length, CharArrayIntMap map) {
return map.getTotalCharacterCount() + length <
characterContentChunkMapTotalCharactersConstraint;
}
/**
* {@inheritDoc}
*/
public int getMinAttributeValueSize() {
return minAttributeValueSize;
}
/**
* {@inheritDoc}
*/
public void setMinAttributeValueSize(int size) {
if (size < 0 ) {
size = 0;
}
minAttributeValueSize = size;
}
/**
* {@inheritDoc}
*/
public int getMaxAttributeValueSize() {
return maxAttributeValueSize;
}
/**
* {@inheritDoc}
*/
public void setMaxAttributeValueSize(int size) {
if (size < 0 ) {
size = 0;
}
maxAttributeValueSize = size;
}
/**
* {@inheritDoc}
*/
public void setAttributeValueMapMemoryLimit(int size) {
if (size < 0 ) {
size = 0;
}
attributeValueMapTotalCharactersConstraint = size / 2;
}
/**
* {@inheritDoc}
*/
public int getAttributeValueMapMemoryLimit() {
return attributeValueMapTotalCharactersConstraint * 2;
}
/**
* Checks whether attribute value (its length) matches length limit
*
* @param length the length of attribute
* @return whether attribute value matches limit
*/
public boolean isAttributeValueLengthMatchesLimit(int length) {
return length >= minAttributeValueSize &&
length < maxAttributeValueSize;
}
/**
* Checks whether attribute table has enough memory to
* store attribute value with the given length
*
* @param length the length of attribute value is checking to be added to Map.
* @return whether attribute map has enough memory
*/
public boolean canAddAttributeToTable(int length) {
return _v.attributeValue.getTotalCharacterCount() + length <
attributeValueMapTotalCharactersConstraint;
}
/**
* {@inheritDoc}
*/
public void setExternalVocabulary(ExternalVocabulary v) {
// Create internal serializer vocabulary
_v = new SerializerVocabulary();
// Set the external vocabulary
SerializerVocabulary ev = new SerializerVocabulary(v.vocabulary,
_useLocalNameAsKeyForQualifiedNameLookup);
_v.setExternalVocabulary(v.URI,
ev, false);
_vIsInternal = true;
}
/**
* {@inheritDoc}
*/
public void setVocabularyApplicationData(VocabularyApplicationData data) {
_vData = data;
}
/**
* {@inheritDoc}
*/
public VocabularyApplicationData getVocabularyApplicationData() {
return _vData;
}
// End of FastInfosetSerializer interface
/**
* Reset the encoder for reuse encoding another XML infoset.
*/
public void reset() {
_terminate = false;
}
/**
* Set the OutputStream to encode the XML infoset to a
* fast infoset document.
*
* @param s the OutputStream where the fast infoset document is written to.
*/
public void setOutputStream(OutputStream s) {
_octetBufferIndex = 0;
_markIndex = -1;
_s = s;
}
/**
* Set the SerializerVocabulary to be used for encoding.
*
* @param vocabulary the vocabulary to be used for encoding.
*/
public void setVocabulary(SerializerVocabulary vocabulary) {
_v = vocabulary;
_vIsInternal = false;
}
/**
* Encode the header of a fast infoset document.
*
* @param encodeXmlDecl true if the XML declaration should be encoded.
*/
protected final void encodeHeader(boolean encodeXmlDecl) throws IOException {
if (encodeXmlDecl) {
_s.write(EncodingConstants.XML_DECLARATION_VALUES[0]);
}
_s.write(EncodingConstants.BINARY_HEADER);
}
/**
* Encode the initial vocabulary of a fast infoset document.
*
*/
protected final void encodeInitialVocabulary() throws IOException {
if (_v == null) {
_v = new SerializerVocabulary();
_vIsInternal = true;
} else if (_vIsInternal) {
_v.clear();
if (_vData != null)
_vData.clear();
}
if (!_v.hasInitialVocabulary() && !_v.hasExternalVocabulary()) {
write(0);
} else if (_v.hasInitialVocabulary()) {
_b = EncodingConstants.DOCUMENT_INITIAL_VOCABULARY_FLAG;
write(_b);
SerializerVocabulary initialVocabulary = _v.getReadOnlyVocabulary();
// TODO check for contents of vocabulary to assign bits
if (initialVocabulary.hasExternalVocabulary()) {
_b = EncodingConstants.INITIAL_VOCABULARY_EXTERNAL_VOCABULARY_FLAG;
write(_b);
write(0);
}
if (initialVocabulary.hasExternalVocabulary()) {
encodeNonEmptyOctetStringOnSecondBit(_v.getExternalVocabularyURI());
}
// TODO check for contents of vocabulary to encode values
} else if (_v.hasExternalVocabulary()) {
_b = EncodingConstants.DOCUMENT_INITIAL_VOCABULARY_FLAG;
write(_b);
_b = EncodingConstants.INITIAL_VOCABULARY_EXTERNAL_VOCABULARY_FLAG;
write(_b);
write(0);
encodeNonEmptyOctetStringOnSecondBit(_v.getExternalVocabularyURI());
}
}
/**
* Encode the termination of the Document Information Item.
*
*/
protected final void encodeDocumentTermination() throws IOException {
encodeElementTermination();
encodeTermination();
_flush();
_s.flush();
}
/**
* Encode the termination of an Element Information Item.
*
*/
protected final void encodeElementTermination() throws IOException {
_terminate = true;
switch (_b) {
case EncodingConstants.TERMINATOR:
_b = EncodingConstants.DOUBLE_TERMINATOR;
break;
case EncodingConstants.DOUBLE_TERMINATOR:
write(EncodingConstants.DOUBLE_TERMINATOR);
default:
_b = EncodingConstants.TERMINATOR;
}
}
/**
* Encode a termination if required.
*
*/
protected final void encodeTermination() throws IOException {
if (_terminate) {
write(_b);
_b = 0;
_terminate = false;
}
}
/**
* Encode a Attribute Information Item that is a namespace declaration.
*
* @param prefix the prefix of the namespace declaration,
* if "" then there is no prefix for the namespace declaration.
* @param uri the URI of the namespace declaration,
* if "" then there is no URI for the namespace declaration.
*/
protected final void encodeNamespaceAttribute(String prefix, String uri) throws IOException {
_b = EncodingConstants.NAMESPACE_ATTRIBUTE;
if (prefix.length() > 0) {
_b |= EncodingConstants.NAMESPACE_ATTRIBUTE_PREFIX_FLAG;
}
if (uri.length() > 0) {
_b |= EncodingConstants.NAMESPACE_ATTRIBUTE_NAME_FLAG;
}
// NOTE a prefix with out a namespace name is an undeclaration
// of the namespace bound to the prefix
// TODO needs to investigate how the startPrefixMapping works in
// relation to undeclaration
write(_b);
if (prefix.length() > 0) {
encodeIdentifyingNonEmptyStringOnFirstBit(prefix, _v.prefix);
}
if (uri.length() > 0) {
encodeIdentifyingNonEmptyStringOnFirstBit(uri, _v.namespaceName);
}
}
/**
* Encode a chunk of Character Information Items.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @throws ArrayIndexOutOfBoundsException.
*/
protected final void encodeCharacters(char[] ch, int offset, int length) throws IOException {
final boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
encodeNonIdentifyingStringOnThirdBit(ch, offset, length, _v.characterContentChunk, addToTable, true);
}
/**
* Encode a chunk of Character Information Items.
*
* If the array of characters is to be indexed (as determined by
* {@link Encoder#characterContentChunkSizeContraint}) then the array is not cloned
* when adding the array to the vocabulary.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @throws ArrayIndexOutOfBoundsException.
*/
protected final void encodeCharactersNoClone(char[] ch, int offset, int length) throws IOException {
final boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
encodeNonIdentifyingStringOnThirdBit(ch, offset, length, _v.characterContentChunk, addToTable, false);
}
/**
* Encode a chunk of Character Information Items using a numeric
* alphabet that results in the encoding of a character in 4 bits
* (or two characters per octet).
*
* @param id the restricted alphabet identifier.
* @param table the table mapping characters to 4 bit values.
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @param addToTable if characters should be added to table.
* @throws ArrayIndexOutOfBoundsException.
*/
protected final void encodeNumericFourBitCharacters(char[] ch, int offset, int length,
boolean addToTable) throws FastInfosetException, IOException {
encodeFourBitCharacters(RestrictedAlphabet.NUMERIC_CHARACTERS_INDEX,
NUMERIC_CHARACTERS_TABLE, ch, offset, length, addToTable);
}
/**
* Encode a chunk of Character Information Items using a date-time
* alphabet that results in the encoding of a character in 4 bits
* (or two characters per octet).
*
* @param id the restricted alphabet identifier.
* @param table the table mapping characters to 4 bit values.
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @param addToTable if characters should be added to table.
* @throws ArrayIndexOutOfBoundsException.
*/
protected final void encodeDateTimeFourBitCharacters(char[] ch, int offset, int length,
boolean addToTable) throws FastInfosetException, IOException {
encodeFourBitCharacters(RestrictedAlphabet.DATE_TIME_CHARACTERS_INDEX,
DATE_TIME_CHARACTERS_TABLE, ch, offset, length, addToTable);
}
/**
* Encode a chunk of Character Information Items using a restricted
* alphabet that results in the encoding of a character in 4 bits
* (or two characters per octet).
*
* @param id the restricted alphabet identifier.
* @param table the table mapping characters to 4 bit values.
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @param addToTable if characters should be added to table.
* @throws ArrayIndexOutOfBoundsException.
*/
protected final void encodeFourBitCharacters(int id, int[] table, char[] ch, int offset, int length,
boolean addToTable) throws FastInfosetException, IOException {
if (addToTable) {
// if char array could be added to table
boolean canAddCharacterContentToTable =
canAddCharacterContentToTable(length, _v.characterContentChunk);
// obtain/get index
int index = canAddCharacterContentToTable ?
_v.characterContentChunk.obtainIndex(ch, offset, length, true) :
_v.characterContentChunk.get(ch, offset, length);
if (index != KeyIntMap.NOT_PRESENT) {
// if char array is in table
_b = EncodingConstants.CHARACTER_CHUNK | 0x20;
encodeNonZeroIntegerOnFourthBit(index);
return;
} else if (canAddCharacterContentToTable) {
// if char array is not in table, but could be added
_b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG | EncodingConstants.CHARACTER_CHUNK_ADD_TO_TABLE_FLAG;
} else {
// if char array is not in table and could not be added
_b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
}
} else {
_b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
}
write (_b);
// Encode bottom 6 bits of enoding algorithm id
_b = id << 2;
encodeNonEmptyFourBitCharacterStringOnSeventhBit(table, ch, offset, length);
}
/**
* Encode a chunk of Character Information Items using a restricted
* alphabet table.
*
* @param alphabet the alphabet defining the mapping between characters and
* integer values.
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @param addToTable if characters should be added to table
* @throws ArrayIndexOutOfBoundsException.
* @throws FastInfosetException if the alphabet is not present in the
* vocabulary.
*/
protected final void encodeAlphabetCharacters(String alphabet, char[] ch, int offset, int length,
boolean addToTable) throws FastInfosetException, IOException {
if (addToTable) {
// if char array could be added to table
boolean canAddCharacterContentToTable =
canAddCharacterContentToTable(length, _v.characterContentChunk);
// obtain/get index
int index = canAddCharacterContentToTable ?
_v.characterContentChunk.obtainIndex(ch, offset, length, true) :
_v.characterContentChunk.get(ch, offset, length);
if (index != KeyIntMap.NOT_PRESENT) {
// if char array is in table
_b = EncodingConstants.CHARACTER_CHUNK | 0x20;
encodeNonZeroIntegerOnFourthBit(index);
return;
} else if (canAddCharacterContentToTable) {
// if char array is not in table, but could be added
_b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG | EncodingConstants.CHARACTER_CHUNK_ADD_TO_TABLE_FLAG;
} else {
// if char array is not in table and could not be added
_b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
}
} else {
_b = EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_RESTRICTED_ALPHABET_FLAG;
}
int id = _v.restrictedAlphabet.get(alphabet);
if (id == KeyIntMap.NOT_PRESENT) {
throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.restrictedAlphabetNotPresent"));
}
id += EncodingConstants.RESTRICTED_ALPHABET_APPLICATION_START;
_b |= (id & 0xC0) >> 6;
write(_b);
// Encode bottom 6 bits of enoding algorithm id
_b = (id & 0x3F) << 2;
encodeNonEmptyNBitCharacterStringOnSeventhBit(alphabet, ch, offset, length);
}
/**
* Encode a Processing Instruction Information Item.
*
* @param target the target of the processing instruction.
* @param data the data of the processing instruction.
*/
protected final void encodeProcessingInstruction(String target, String data) throws IOException {
write(EncodingConstants.PROCESSING_INSTRUCTION);
// Target
encodeIdentifyingNonEmptyStringOnFirstBit(target, _v.otherNCName);
// Data
boolean addToTable = isCharacterContentChunkLengthMatchesLimit(data.length());
encodeNonIdentifyingStringOnFirstBit(data, _v.otherString, addToTable);
}
/**
* Encode a Document Type Declaration.
*
* @param systemId the system identifier of the external subset.
* @param publicId the public identifier of the external subset.
*/
protected final void encodeDocumentTypeDeclaration(String systemId, String publicId) throws IOException {
_b = EncodingConstants.DOCUMENT_TYPE_DECLARATION;
if (systemId != null && systemId.length() > 0) {
_b |= EncodingConstants.DOCUMENT_TYPE_SYSTEM_IDENTIFIER_FLAG;
}
if (publicId != null && publicId.length() > 0) {
_b |= EncodingConstants.DOCUMENT_TYPE_PUBLIC_IDENTIFIER_FLAG;
}
write(_b);
if (systemId != null && systemId.length() > 0) {
encodeIdentifyingNonEmptyStringOnFirstBit(systemId, _v.otherURI);
}
if (publicId != null && publicId.length() > 0) {
encodeIdentifyingNonEmptyStringOnFirstBit(publicId, _v.otherURI);
}
}
/**
* Encode a Comment Information Item.
*
* @param ch the array of characters that is as comment.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @throws ArrayIndexOutOfBoundsException.
*/
protected final void encodeComment(char[] ch, int offset, int length) throws IOException {
write(EncodingConstants.COMMENT);
boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
encodeNonIdentifyingStringOnFirstBit(ch, offset, length, _v.otherString, addToTable, true);
}
/**
* Encode a Comment Information Item.
*
* If the array of characters that is a comment is to be indexed (as
* determined by {@link Encoder#characterContentChunkSizeContraint}) then
* the array is not cloned when adding the array to the vocabulary.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @throws ArrayIndexOutOfBoundsException.
*/
protected final void encodeCommentNoClone(char[] ch, int offset, int length) throws IOException {
write(EncodingConstants.COMMENT);
boolean addToTable = isCharacterContentChunkLengthMatchesLimit(length);
encodeNonIdentifyingStringOnFirstBit(ch, offset, length, _v.otherString, addToTable, false);
}
/**
* Encode a qualified name of an Element Informaiton Item on the third bit
* of an octet.
* Implementation of clause C.18 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* <p>
* The index of the qualified name will be encoded if the name is present
* in the vocabulary otherwise the qualified name will be encoded literally
* (see {@link #encodeLiteralElementQualifiedNameOnThirdBit}).
*
* @param namespaceURI the namespace URI of the qualified name.
* @param prefix the prefix of the qualified name.
* @param localName the local name of the qualified name.
*/
protected final void encodeElementQualifiedNameOnThirdBit(String namespaceURI, String prefix, String localName) throws IOException {
LocalNameQualifiedNamesMap.Entry entry = _v.elementName.obtainEntry(localName);
if (entry._valueIndex > 0) {
QualifiedName[] names = entry._value;
for (int i = 0; i < entry._valueIndex; i++) {
if ((prefix == names[i].prefix || prefix.equals(names[i].prefix))
&& (namespaceURI == names[i].namespaceName || namespaceURI.equals(names[i].namespaceName))) {
encodeNonZeroIntegerOnThirdBit(names[i].index);
return;
}
}
}
encodeLiteralElementQualifiedNameOnThirdBit(namespaceURI, prefix,
localName, entry);
}
/**
* Encode a literal qualified name of an Element Informaiton Item on the
* third bit of an octet.
* Implementation of clause C.18 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param namespaceURI the namespace URI of the qualified name.
* @param prefix the prefix of the qualified name.
* @param localName the local name of the qualified name.
*/
protected final void encodeLiteralElementQualifiedNameOnThirdBit(String namespaceURI, String prefix, String localName,
LocalNameQualifiedNamesMap.Entry entry) throws IOException {
QualifiedName name = new QualifiedName(prefix, namespaceURI, localName, "", _v.elementName.getNextIndex());
entry.addQualifiedName(name);
int namespaceURIIndex = KeyIntMap.NOT_PRESENT;
int prefixIndex = KeyIntMap.NOT_PRESENT;
if (namespaceURI.length() > 0) {
namespaceURIIndex = _v.namespaceName.get(namespaceURI);
if (namespaceURIIndex == KeyIntMap.NOT_PRESENT) {
throw new IOException(CommonResourceBundle.getInstance().getString("message.namespaceURINotIndexed", new Object[]{namespaceURI}));
}
if (prefix.length() > 0) {
prefixIndex = _v.prefix.get(prefix);
if (prefixIndex == KeyIntMap.NOT_PRESENT) {
throw new IOException(CommonResourceBundle.getInstance().getString("message.prefixNotIndexed", new Object[]{prefix}));
}
}
}
int localNameIndex = _v.localName.obtainIndex(localName);
_b |= EncodingConstants.ELEMENT_LITERAL_QNAME_FLAG;
if (namespaceURIIndex >= 0) {
_b |= EncodingConstants.LITERAL_QNAME_NAMESPACE_NAME_FLAG;
if (prefixIndex >= 0) {
_b |= EncodingConstants.LITERAL_QNAME_PREFIX_FLAG;
}
}
write(_b);
if (namespaceURIIndex >= 0) {
if (prefixIndex >= 0) {
encodeNonZeroIntegerOnSecondBitFirstBitOne(prefixIndex);
}
encodeNonZeroIntegerOnSecondBitFirstBitOne(namespaceURIIndex);
}
if (localNameIndex >= 0) {
encodeNonZeroIntegerOnSecondBitFirstBitOne(localNameIndex);
} else {
encodeNonEmptyOctetStringOnSecondBit(localName);
}
}
/**
* Encode a qualified name of an Attribute Informaiton Item on the third bit
* of an octet.
* Implementation of clause C.17 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* <p>
* The index of the qualified name will be encoded if the name is present
* in the vocabulary otherwise the qualified name will be encoded literally
* (see {@link #encodeLiteralAttributeQualifiedNameOnSecondBit}).
*
* @param namespaceURI the namespace URI of the qualified name.
* @param prefix the prefix of the qualified name.
* @param localName the local name of the qualified name.
*/
protected final void encodeAttributeQualifiedNameOnSecondBit(String namespaceURI, String prefix, String localName) throws IOException {
LocalNameQualifiedNamesMap.Entry entry = _v.attributeName.obtainEntry(localName);
if (entry._valueIndex > 0) {
QualifiedName[] names = entry._value;
for (int i = 0; i < entry._valueIndex; i++) {
if ((prefix == names[i].prefix || prefix.equals(names[i].prefix))
&& (namespaceURI == names[i].namespaceName || namespaceURI.equals(names[i].namespaceName))) {
encodeNonZeroIntegerOnSecondBitFirstBitZero(names[i].index);
return;
}
}
}
encodeLiteralAttributeQualifiedNameOnSecondBit(namespaceURI, prefix,
localName, entry);
}
/**
* Encode a literal qualified name of an Attribute Informaiton Item on the
* third bit of an octet.
* Implementation of clause C.17 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param namespaceURI the namespace URI of the qualified name.
* @param prefix the prefix of the qualified name.
* @param localName the local name of the qualified name.
*/
protected final boolean encodeLiteralAttributeQualifiedNameOnSecondBit(String namespaceURI, String prefix, String localName,
LocalNameQualifiedNamesMap.Entry entry) throws IOException {
int namespaceURIIndex = KeyIntMap.NOT_PRESENT;
int prefixIndex = KeyIntMap.NOT_PRESENT;
if (namespaceURI.length() > 0) {
namespaceURIIndex = _v.namespaceName.get(namespaceURI);
if (namespaceURIIndex == KeyIntMap.NOT_PRESENT) {
if (namespaceURI == EncodingConstants.XMLNS_NAMESPACE_NAME ||
namespaceURI.equals(EncodingConstants.XMLNS_NAMESPACE_NAME)) {
return false;
} else {
throw new IOException(CommonResourceBundle.getInstance().getString("message.namespaceURINotIndexed", new Object[]{namespaceURI}));
}
}
if (prefix.length() > 0) {
prefixIndex = _v.prefix.get(prefix);
if (prefixIndex == KeyIntMap.NOT_PRESENT) {
throw new IOException(CommonResourceBundle.getInstance().getString("message.prefixNotIndexed", new Object[]{prefix}));
}
}
}
int localNameIndex = _v.localName.obtainIndex(localName);
QualifiedName name = new QualifiedName(prefix, namespaceURI, localName, "", _v.attributeName.getNextIndex());
entry.addQualifiedName(name);
_b = EncodingConstants.ATTRIBUTE_LITERAL_QNAME_FLAG;
if (namespaceURI.length() > 0) {
_b |= EncodingConstants.LITERAL_QNAME_NAMESPACE_NAME_FLAG;
if (prefix.length() > 0) {
_b |= EncodingConstants.LITERAL_QNAME_PREFIX_FLAG;
}
}
write(_b);
if (namespaceURIIndex >= 0) {
if (prefixIndex >= 0) {
encodeNonZeroIntegerOnSecondBitFirstBitOne(prefixIndex);
}
encodeNonZeroIntegerOnSecondBitFirstBitOne(namespaceURIIndex);
} else if (namespaceURI != "") {
// XML prefix and namespace name
encodeNonEmptyOctetStringOnSecondBit("xml");
encodeNonEmptyOctetStringOnSecondBit("http://www.w3.org/XML/1998/namespace");
}
if (localNameIndex >= 0) {
encodeNonZeroIntegerOnSecondBitFirstBitOne(localNameIndex);
} else {
encodeNonEmptyOctetStringOnSecondBit(localName);
}
return true;
}
/**
* Encode a non identifying string on the first bit of an octet.
* Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param s the string to encode
* @param map the vocabulary table of strings to indexes.
* @param addToTable true if the string could be added to the vocabulary
* table (if table has enough memory)
* @param mustBeAddedToTable true if the string must be added to the vocabulary
* table (if not already present in the table).
*/
protected final void encodeNonIdentifyingStringOnFirstBit(String s, StringIntMap map,
boolean addToTable, boolean mustBeAddedToTable) throws IOException {
if (s == null || s.length() == 0) {
// C.26 an index (first bit '1') with seven '1' bits for an empty string
write(0xFF);
} else {
if (addToTable || mustBeAddedToTable) {
// if attribute value could be added to table
boolean canAddAttributeToTable = mustBeAddedToTable ||
canAddAttributeToTable(s.length());
// obtain/get index
int index = canAddAttributeToTable ?
map.obtainIndex(s) :
map.get(s);
if (index != KeyIntMap.NOT_PRESENT) {
// if attribute value is in table
encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
} else if (canAddAttributeToTable) {
// if attribute value is not in table, but could be added
_b = EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG |
_nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(s);
} else {
// if attribute value is not in table and could not be added
_b = _nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(s);
}
} else {
_b = _nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(s);
}
}
}
/**
* Encode a non identifying string on the first bit of an octet.
* Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param s the string to encode
* @param map the vocabulary table of character arrays to indexes.
* @param addToTable true if the string should be added to the vocabulary
* table (if not already present in the table).
*/
protected final void encodeNonIdentifyingStringOnFirstBit(String s, CharArrayIntMap map, boolean addToTable) throws IOException {
if (s == null || s.length() == 0) {
// C.26 an index (first bit '1') with seven '1' bits for an empty string
write(0xFF);
} else {
if (addToTable) {
final char[] ch = s.toCharArray();
final int length = s.length();
// if char array could be added to table
boolean canAddCharacterContentToTable =
canAddCharacterContentToTable(length, map);
// obtain/get index
int index = canAddCharacterContentToTable ?
map.obtainIndex(ch, 0, length, false) :
map.get(ch, 0, length);
if (index != KeyIntMap.NOT_PRESENT) {
// if char array is in table
encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
} else if (canAddCharacterContentToTable) {
// if char array is not in table, but could be added
_b = EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG |
_nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(ch, 0, length);
} else {
// if char array is not in table and could not be added
_b = _nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(s);
}
} else {
_b = _nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(s);
}
}
}
/**
* Encode a non identifying string on the first bit of an octet.
* Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @param map the vocabulary table of character arrays to indexes.
* @param addToTable true if the string should be added to the vocabulary
* table (if not already present in the table).
* @param clone true if the array of characters should be cloned if added
* to the vocabulary table.
*/
protected final void encodeNonIdentifyingStringOnFirstBit(char[] ch, int offset, int length, CharArrayIntMap map,
boolean addToTable, boolean clone) throws IOException {
if (length == 0) {
// C.26 an index (first bit '1') with seven '1' bits for an empty string
write(0xFF);
} else {
if (addToTable) {
// if char array could be added to table
boolean canAddCharacterContentToTable =
canAddCharacterContentToTable(length, map);
// obtain/get index
int index = canAddCharacterContentToTable ?
map.obtainIndex(ch, offset, length, clone) :
map.get(ch, offset, length);
if (index != KeyIntMap.NOT_PRESENT) {
// if char array is in table
encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
} else if (canAddCharacterContentToTable) {
// if char array is not in table, but could be added
_b = EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG |
_nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(ch, offset, length);
} else {
// if char array is not in table and could not be added
_b = _nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(ch, offset, length);
}
} else {
_b = _nonIdentifyingStringOnFirstBitCES;
encodeNonEmptyCharacterStringOnFifthBit(ch, offset, length);
}
}
}
protected final void encodeNumericNonIdentifyingStringOnFirstBit(
String s, boolean addToTable, boolean mustBeAddedToTable)
throws IOException, FastInfosetException {
encodeNonIdentifyingStringOnFirstBit(
RestrictedAlphabet.NUMERIC_CHARACTERS_INDEX,
NUMERIC_CHARACTERS_TABLE, s, addToTable,
mustBeAddedToTable);
}
protected final void encodeDateTimeNonIdentifyingStringOnFirstBit(
String s, boolean addToTable, boolean mustBeAddedToTable)
throws IOException, FastInfosetException {
encodeNonIdentifyingStringOnFirstBit(
RestrictedAlphabet.DATE_TIME_CHARACTERS_INDEX,
DATE_TIME_CHARACTERS_TABLE, s, addToTable,
mustBeAddedToTable);
}
protected final void encodeNonIdentifyingStringOnFirstBit(int id, int[] table,
String s, boolean addToTable, boolean mustBeAddedToTable)
throws IOException, FastInfosetException {
if (s == null || s.length() == 0) {
// C.26 an index (first bit '1') with seven '1' bits for an empty string
write(0xFF);
return;
}
if (addToTable || mustBeAddedToTable) {
// if attribute value could be added to table
boolean canAddAttributeToTable = mustBeAddedToTable ||
canAddAttributeToTable(s.length());
// obtain/get index
int index = canAddAttributeToTable ?
_v.attributeValue.obtainIndex(s) :
_v.attributeValue.get(s);
if (index != KeyIntMap.NOT_PRESENT) {
// if attribute value is in table
encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
return;
} else if (canAddAttributeToTable) {
// if attribute value is not in table, but could be added
_b = EncodingConstants.NISTRING_RESTRICTED_ALPHABET_FLAG |
EncodingConstants.NISTRING_ADD_TO_TABLE_FLAG;
} else {
// if attribute value is not in table and could not be added
_b = EncodingConstants.NISTRING_RESTRICTED_ALPHABET_FLAG;
}
} else {
_b = EncodingConstants.NISTRING_RESTRICTED_ALPHABET_FLAG;
}
// Encode identification and top four bits of alphabet id
write (_b | ((id & 0xF0) >> 4));
// Encode bottom 4 bits of alphabet id
_b = (id & 0x0F) << 4;
final int length = s.length();
final int octetPairLength = length / 2;
final int octetSingleLength = length % 2;
encodeNonZeroOctetStringLengthOnFifthBit(octetPairLength + octetSingleLength);
encodeNonEmptyFourBitCharacterString(table, s.toCharArray(), 0, octetPairLength, octetSingleLength);
}
/**
* Encode a non identifying string on the first bit of an octet as binary
* data using an encoding algorithm.
* Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param URI the encoding algorithm URI. If the URI == null then the
* encoding algorithm identifier takes precendence.
* @param id the encoding algorithm identifier.
* @param data the data to be encoded using an encoding algorithm.
* @throws EncodingAlgorithmException if the encoding algorithm URI is not
* present in the vocabulary, or the encoding algorithm identifier
* is not with the required range.
*/
protected final void encodeNonIdentifyingStringOnFirstBit(String URI, int id, Object data) throws FastInfosetException, IOException {
if (URI != null) {
id = _v.encodingAlgorithm.get(URI);
if (id == KeyIntMap.NOT_PRESENT) {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.EncodingAlgorithmURI", new Object[]{URI}));
}
id += EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START;
EncodingAlgorithm ea = (EncodingAlgorithm)_registeredEncodingAlgorithms.get(URI);
if (ea != null) {
encodeAIIObjectAlgorithmData(id, data, ea);
} else {
if (data instanceof byte[]) {
byte[] d = (byte[])data;
encodeAIIOctetAlgorithmData(id, d, 0, d.length);
} else {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
}
}
} else if (id <= EncodingConstants.ENCODING_ALGORITHM_BUILTIN_END) {
int length = 0;
switch(id) {
case EncodingAlgorithmIndexes.HEXADECIMAL:
case EncodingAlgorithmIndexes.BASE64:
length = ((byte[])data).length;
break;
case EncodingAlgorithmIndexes.SHORT:
length = ((short[])data).length;
break;
case EncodingAlgorithmIndexes.INT:
length = ((int[])data).length;
break;
case EncodingAlgorithmIndexes.LONG:
case EncodingAlgorithmIndexes.UUID:
length = ((long[])data).length;
break;
case EncodingAlgorithmIndexes.BOOLEAN:
length = ((boolean[])data).length;
break;
case EncodingAlgorithmIndexes.FLOAT:
length = ((float[])data).length;
break;
case EncodingAlgorithmIndexes.DOUBLE:
length = ((double[])data).length;
break;
case EncodingAlgorithmIndexes.CDATA:
throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.CDATA"));
default:
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.UnsupportedBuiltInAlgorithm", new Object[]{Integer.valueOf(id)}));
}
encodeAIIBuiltInAlgorithmData(id, data, 0, length);
} else if (id >= EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START) {
if (data instanceof byte[]) {
byte[] d = (byte[])data;
encodeAIIOctetAlgorithmData(id, d, 0, d.length);
} else {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
}
} else {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.identifiers10to31Reserved"));
}
}
/**
* Encode the [normalized value] of an Attribute Information Item using
* using an encoding algorithm.
* Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param id the encoding algorithm identifier.
* @param d the data, as an array of bytes, to be encoded.
* @param offset the offset into the array of bytes.
* @param length the length of bytes.
*/
protected final void encodeAIIOctetAlgorithmData(int id, byte[] d, int offset, int length) throws IOException {
// Encode identification and top four bits of encoding algorithm id
write (EncodingConstants.NISTRING_ENCODING_ALGORITHM_FLAG |
((id & 0xF0) >> 4));
// Encode bottom 4 bits of enoding algorithm id
_b = (id & 0x0F) << 4;
// Encode the length
encodeNonZeroOctetStringLengthOnFifthBit(length);
write(d, offset, length);
}
/**
* Encode the [normalized value] of an Attribute Information Item using
* using an encoding algorithm.
* Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param id the encoding algorithm identifier.
* @param data the data to be encoded using an encoding algorithm.
* @param ea the encoding algorithm to use to encode the data into an
* array of bytes.
*/
protected final void encodeAIIObjectAlgorithmData(int id, Object data, EncodingAlgorithm ea) throws FastInfosetException, IOException {
// Encode identification and top four bits of encoding algorithm id
write (EncodingConstants.NISTRING_ENCODING_ALGORITHM_FLAG |
((id & 0xF0) >> 4));
// Encode bottom 4 bits of enoding algorithm id
_b = (id & 0x0F) << 4;
_encodingBufferOutputStream.reset();
ea.encodeToOutputStream(data, _encodingBufferOutputStream);
encodeNonZeroOctetStringLengthOnFifthBit(_encodingBufferIndex);
write(_encodingBuffer, _encodingBufferIndex);
}
/**
* Encode the [normalized value] of an Attribute Information Item using
* using a built in encoding algorithm.
* Implementation of clause C.14 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param id the built in encoding algorithm identifier.
* @param data the data to be encoded using an encoding algorithm. The data
* represents an array of items specified by the encoding algorithm
* identifier
* @param offset the offset into the array of bytes.
* @param length the length of bytes.
*/
protected final void encodeAIIBuiltInAlgorithmData(int id, Object data, int offset, int length) throws IOException {
// Encode identification and top four bits of encoding algorithm id
write (EncodingConstants.NISTRING_ENCODING_ALGORITHM_FLAG |
((id & 0xF0) >> 4));
// Encode bottom 4 bits of enoding algorithm id
_b = (id & 0x0F) << 4;
final int octetLength = BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
getOctetLengthFromPrimitiveLength(length);
encodeNonZeroOctetStringLengthOnFifthBit(octetLength);
ensureSize(octetLength);
BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
encodeToBytes(data, offset, length, _octetBuffer, _octetBufferIndex);
_octetBufferIndex += octetLength;
}
/**
* Encode a non identifying string on the third bit of an octet.
* Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* @param map the vocabulary table of character arrays to indexes.
* @param addToTable true if the array of characters should be added to the vocabulary
* table (if not already present in the table).
* @param clone true if the array of characters should be cloned if added
* to the vocabulary table.
*/
protected final void encodeNonIdentifyingStringOnThirdBit(char[] ch, int offset, int length,
CharArrayIntMap map, boolean addToTable, boolean clone) throws IOException {
// length cannot be zero since sequence of CIIs has to be > 0
if (addToTable) {
// if char array could be added to table
boolean canAddCharacterContentToTable =
canAddCharacterContentToTable(length, map);
// obtain/get index
int index = canAddCharacterContentToTable ?
map.obtainIndex(ch, offset, length, clone) :
map.get(ch, offset, length);
if (index != KeyIntMap.NOT_PRESENT) {
// if char array is in table
_b = EncodingConstants.CHARACTER_CHUNK | 0x20;
encodeNonZeroIntegerOnFourthBit(index);
} else if (canAddCharacterContentToTable) {
// if char array is not in table, but could be added
_b = EncodingConstants.CHARACTER_CHUNK_ADD_TO_TABLE_FLAG |
_nonIdentifyingStringOnThirdBitCES;
encodeNonEmptyCharacterStringOnSeventhBit(ch, offset, length);
} else {
// if char array is not in table and could not be added
_b = _nonIdentifyingStringOnThirdBitCES;
encodeNonEmptyCharacterStringOnSeventhBit(ch, offset, length);
}
} else {
// char array will not be added to map
_b = _nonIdentifyingStringOnThirdBitCES;
encodeNonEmptyCharacterStringOnSeventhBit(ch, offset, length);
}
}
/**
* Encode a non identifying string on the third bit of an octet as binary
* data using an encoding algorithm.
* Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param URI the encoding algorithm URI. If the URI == null then the
* encoding algorithm identifier takes precendence.
* @param id the encoding algorithm identifier.
* @param data the data to be encoded using an encoding algorithm.
* @throws EncodingAlgorithmException if the encoding algorithm URI is not
* present in the vocabulary, or the encoding algorithm identifier
* is not with the required range.
*/
protected final void encodeNonIdentifyingStringOnThirdBit(String URI, int id, Object data) throws FastInfosetException, IOException {
if (URI != null) {
id = _v.encodingAlgorithm.get(URI);
if (id == KeyIntMap.NOT_PRESENT) {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.EncodingAlgorithmURI", new Object[]{URI}));
}
id += EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START;
EncodingAlgorithm ea = (EncodingAlgorithm)_registeredEncodingAlgorithms.get(URI);
if (ea != null) {
encodeCIIObjectAlgorithmData(id, data, ea);
} else {
if (data instanceof byte[]) {
byte[] d = (byte[])data;
encodeCIIOctetAlgorithmData(id, d, 0, d.length);
} else {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
}
}
} else if (id <= EncodingConstants.ENCODING_ALGORITHM_BUILTIN_END) {
int length = 0;
switch(id) {
case EncodingAlgorithmIndexes.HEXADECIMAL:
case EncodingAlgorithmIndexes.BASE64:
length = ((byte[])data).length;
break;
case EncodingAlgorithmIndexes.SHORT:
length = ((short[])data).length;
break;
case EncodingAlgorithmIndexes.INT:
length = ((int[])data).length;
break;
case EncodingAlgorithmIndexes.LONG:
case EncodingAlgorithmIndexes.UUID:
length = ((long[])data).length;
break;
case EncodingAlgorithmIndexes.BOOLEAN:
length = ((boolean[])data).length;
break;
case EncodingAlgorithmIndexes.FLOAT:
length = ((float[])data).length;
break;
case EncodingAlgorithmIndexes.DOUBLE:
length = ((double[])data).length;
break;
case EncodingAlgorithmIndexes.CDATA:
throw new UnsupportedOperationException(CommonResourceBundle.getInstance().getString("message.CDATA"));
default:
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.UnsupportedBuiltInAlgorithm", new Object[]{Integer.valueOf(id)}));
}
encodeCIIBuiltInAlgorithmData(id, data, 0, length);
} else if (id >= EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START) {
if (data instanceof byte[]) {
byte[] d = (byte[])data;
encodeCIIOctetAlgorithmData(id, d, 0, d.length);
} else {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.nullEncodingAlgorithmURI"));
}
} else {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.identifiers10to31Reserved"));
}
}
/**
* Encode a non identifying string on the third bit of an octet as binary
* data using an encoding algorithm.
* Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param URI the encoding algorithm URI. If the URI == null then the
* encoding algorithm identifier takes precendence.
* @param id the encoding algorithm identifier.
* @param d the data, as an array of bytes, to be encoded.
* @param offset the offset into the array of bytes.
* @param length the length of bytes.
* @throws EncodingAlgorithmException if the encoding algorithm URI is not
* present in the vocabulary.
*/
protected final void encodeNonIdentifyingStringOnThirdBit(String URI, int id, byte[] d, int offset, int length) throws FastInfosetException, IOException {
if (URI != null) {
id = _v.encodingAlgorithm.get(URI);
if (id == KeyIntMap.NOT_PRESENT) {
throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.EncodingAlgorithmURI", new Object[]{URI}));
}
id += EncodingConstants.ENCODING_ALGORITHM_APPLICATION_START;
}
encodeCIIOctetAlgorithmData(id, d, offset, length);
}
/**
* Encode a chunk of Character Information Items using
* using an encoding algorithm.
* Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param id the encoding algorithm identifier.
* @param d the data, as an array of bytes, to be encoded.
* @param offset the offset into the array of bytes.
* @param length the length of bytes.
*/
protected final void encodeCIIOctetAlgorithmData(int id, byte[] d, int offset, int length) throws IOException {
// Encode identification and top two bits of encoding algorithm id
write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG |
((id & 0xC0) >> 6));
// Encode bottom 6 bits of enoding algorithm id
_b = (id & 0x3F) << 2;
// Encode the length
encodeNonZeroOctetStringLengthOnSenventhBit(length);
write(d, offset, length);
}
/**
* Encode a chunk of Character Information Items using
* using an encoding algorithm.
* Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param id the encoding algorithm identifier.
* @param data the data to be encoded using an encoding algorithm.
* @param ea the encoding algorithm to use to encode the data into an
* array of bytes.
*/
protected final void encodeCIIObjectAlgorithmData(int id, Object data, EncodingAlgorithm ea) throws FastInfosetException, IOException {
// Encode identification and top two bits of encoding algorithm id
write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG |
((id & 0xC0) >> 6));
// Encode bottom 6 bits of enoding algorithm id
_b = (id & 0x3F) << 2;
_encodingBufferOutputStream.reset();
ea.encodeToOutputStream(data, _encodingBufferOutputStream);
encodeNonZeroOctetStringLengthOnSenventhBit(_encodingBufferIndex);
write(_encodingBuffer, _encodingBufferIndex);
}
/**
* Encode a chunk of Character Information Items using
* using an encoding algorithm.
* Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param id the built in encoding algorithm identifier.
* @param data the data to be encoded using an encoding algorithm. The data
* represents an array of items specified by the encoding algorithm
* identifier
* @param offset the offset into the array of bytes.
* @param length the length of bytes.
*/
protected final void encodeCIIBuiltInAlgorithmData(int id, Object data, int offset, int length) throws FastInfosetException, IOException {
// Encode identification and top two bits of encoding algorithm id
write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG |
((id & 0xC0) >> 6));
// Encode bottom 6 bits of enoding algorithm id
_b = (id & 0x3F) << 2;
final int octetLength = BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
getOctetLengthFromPrimitiveLength(length);
encodeNonZeroOctetStringLengthOnSenventhBit(octetLength);
ensureSize(octetLength);
BuiltInEncodingAlgorithmFactory.getAlgorithm(id).
encodeToBytes(data, offset, length, _octetBuffer, _octetBufferIndex);
_octetBufferIndex += octetLength;
}
/**
* Encode a chunk of Character Information Items using
* using the CDATA built in encoding algorithm.
* Implementation of clause C.15 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
*/
protected final void encodeCIIBuiltInAlgorithmDataAsCDATA(char[] ch, int offset, int length) throws FastInfosetException, IOException {
// Encode identification and top two bits of encoding algorithm id
write (EncodingConstants.CHARACTER_CHUNK | EncodingConstants.CHARACTER_CHUNK_ENCODING_ALGORITHM_FLAG);
// Encode bottom 6 bits of enoding algorithm id
_b = EncodingAlgorithmIndexes.CDATA << 2;
length = encodeUTF8String(ch, offset, length);
encodeNonZeroOctetStringLengthOnSenventhBit(length);
write(_encodingBuffer, length);
}
/**
* Encode a non empty identifying string on the first bit of an octet.
* Implementation of clause C.13 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param s the identifying string.
* @param map the vocabulary table to use to determin the index of the
* identifying string
*/
protected final void encodeIdentifyingNonEmptyStringOnFirstBit(String s, StringIntMap map) throws IOException {
int index = map.obtainIndex(s);
if (index == KeyIntMap.NOT_PRESENT) {
// _b = 0;
encodeNonEmptyOctetStringOnSecondBit(s);
} else {
// _b = 0x80;
encodeNonZeroIntegerOnSecondBitFirstBitOne(index);
}
}
/**
* Encode a non empty string on the second bit of an octet using the UTF-8
* encoding.
* Implementation of clause C.22 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param s the string.
*/
protected final void encodeNonEmptyOctetStringOnSecondBit(String s) throws IOException {
final int length = encodeUTF8String(s);
encodeNonZeroOctetStringLengthOnSecondBit(length);
write(_encodingBuffer, length);
}
/**
* Encode the length of a UTF-8 encoded string on the second bit of an octet.
* Implementation of clause C.22 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param length the length to encode.
*/
protected final void encodeNonZeroOctetStringLengthOnSecondBit(int length) throws IOException {
if (length < EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_SMALL_LIMIT) {
// [1, 64]
write(length - 1);
} else if (length < EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_MEDIUM_LIMIT) {
// [65, 320]
write(EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_MEDIUM_FLAG); // 010 00000
write(length - EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_SMALL_LIMIT);
} else {
// [321, 4294967296]
write(EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_LARGE_FLAG); // 0110 0000
length -= EncodingConstants.OCTET_STRING_LENGTH_2ND_BIT_MEDIUM_LIMIT;
write(length >>> 24);
write((length >> 16) & 0xFF);
write((length >> 8) & 0xFF);
write(length & 0xFF);
}
}
/**
* Encode a non empty string on the fifth bit of an octet using the UTF-8
* or UTF-16 encoding.
* Implementation of clause C.23 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param s the string.
*/
protected final void encodeNonEmptyCharacterStringOnFifthBit(String s) throws IOException {
final int length = (_encodingStringsAsUtf8) ? encodeUTF8String(s) : encodeUtf16String(s);
encodeNonZeroOctetStringLengthOnFifthBit(length);
write(_encodingBuffer, length);
}
/**
* Encode a non empty string on the fifth bit of an octet using the UTF-8
* or UTF-16 encoding.
* Implementation of clause C.23 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
*/
protected final void encodeNonEmptyCharacterStringOnFifthBit(char[] ch, int offset, int length) throws IOException {
length = (_encodingStringsAsUtf8) ? encodeUTF8String(ch, offset, length) : encodeUtf16String(ch, offset, length);
encodeNonZeroOctetStringLengthOnFifthBit(length);
write(_encodingBuffer, length);
}
/**
* Encode the length of a UTF-8 or UTF-16 encoded string on the fifth bit
* of an octet.
* Implementation of clause C.23 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param length the length to encode.
*/
protected final void encodeNonZeroOctetStringLengthOnFifthBit(int length) throws IOException {
if (length < EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_SMALL_LIMIT) {
// [1, 8]
write(_b | (length - 1));
} else if (length < EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_MEDIUM_LIMIT) {
// [9, 264]
write(_b | EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_MEDIUM_FLAG); // 000010 00
write(length - EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_SMALL_LIMIT);
} else {
// [265, 4294967296]
write(_b | EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_LARGE_FLAG); // 000011 00
length -= EncodingConstants.OCTET_STRING_LENGTH_5TH_BIT_MEDIUM_LIMIT;
write(length >>> 24);
write((length >> 16) & 0xFF);
write((length >> 8) & 0xFF);
write(length & 0xFF);
}
}
/**
* Encode a non empty string on the seventh bit of an octet using the UTF-8
* or UTF-16 encoding.
* Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
*/
protected final void encodeNonEmptyCharacterStringOnSeventhBit(char[] ch, int offset, int length) throws IOException {
length = (_encodingStringsAsUtf8) ? encodeUTF8String(ch, offset, length) : encodeUtf16String(ch, offset, length);
encodeNonZeroOctetStringLengthOnSenventhBit(length);
write(_encodingBuffer, length);
}
/**
* Encode a non empty string on the seventh bit of an octet using a restricted
* alphabet that results in the encoding of a character in 4 bits
* (or two characters per octet).
* Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param table the table mapping characters to 4 bit values.
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
*/
protected final void encodeNonEmptyFourBitCharacterStringOnSeventhBit(int[] table, char[] ch, int offset, int length) throws FastInfosetException, IOException {
final int octetPairLength = length / 2;
final int octetSingleLength = length % 2;
// Encode the length
encodeNonZeroOctetStringLengthOnSenventhBit(octetPairLength + octetSingleLength);
encodeNonEmptyFourBitCharacterString(table, ch, offset, octetPairLength, octetSingleLength);
}
protected final void encodeNonEmptyFourBitCharacterString(int[] table, char[] ch, int offset,
int octetPairLength, int octetSingleLength) throws FastInfosetException, IOException {
ensureSize(octetPairLength + octetSingleLength);
// Encode all pairs
int v = 0;
for (int i = 0; i < octetPairLength; i++) {
v = (table[ch[offset++]] << 4) | table[ch[offset++]];
if (v < 0) {
throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.characterOutofAlphabetRange"));
}
_octetBuffer[_octetBufferIndex++] = (byte)v;
}
// Encode single character at end with termination bits
if (octetSingleLength == 1) {
v = (table[ch[offset]] << 4) | 0x0F;
if (v < 0) {
throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.characterOutofAlphabetRange"));
}
_octetBuffer[_octetBufferIndex++] = (byte)v;
}
}
/**
* Encode a non empty string on the seventh bit of an octet using a restricted
* alphabet table.
* Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param alphabet the alphabet defining the mapping between characters and
* integer values.
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
*/
protected final void encodeNonEmptyNBitCharacterStringOnSeventhBit(String alphabet, char[] ch, int offset, int length) throws FastInfosetException, IOException {
int bitsPerCharacter = 1;
while ((1 << bitsPerCharacter) <= alphabet.length()) {
bitsPerCharacter++;
}
final int bits = length * bitsPerCharacter;
final int octets = bits / 8;
final int bitsOfLastOctet = bits % 8;
final int totalOctets = octets + ((bitsOfLastOctet > 0) ? 1 : 0);
// Encode the length
encodeNonZeroOctetStringLengthOnSenventhBit(totalOctets);
resetBits();
ensureSize(totalOctets);
int v = 0;
for (int i = 0; i < length; i++) {
final char c = ch[offset + i];
// This is grotesquely slow, need to use hash table of character to int value
for (v = 0; v < alphabet.length(); v++) {
if (c == alphabet.charAt(v)) {
break;
}
}
if (v == alphabet.length()) {
throw new FastInfosetException(CommonResourceBundle.getInstance().getString("message.characterOutofAlphabetRange"));
}
writeBits(bitsPerCharacter, v);
}
if (bitsOfLastOctet > 0) {
_b |= (1 << (8 - bitsOfLastOctet)) - 1;
write(_b);
}
}
private int _bitsLeftInOctet;
private final void resetBits() {
_bitsLeftInOctet = 8;
_b = 0;
}
private final void writeBits(int bits, int v) throws IOException {
while (bits > 0) {
final int bit = (v & (1 << --bits)) > 0 ? 1 : 0;
_b |= bit << (--_bitsLeftInOctet);
if (_bitsLeftInOctet == 0) {
write(_b);
_bitsLeftInOctet = 8;
_b = 0;
}
}
}
/**
* Encode the length of a encoded string on the seventh bit
* of an octet.
* Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param length the length to encode.
*/
protected final void encodeNonZeroOctetStringLengthOnSenventhBit(int length) throws IOException {
if (length < EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_SMALL_LIMIT) {
// [1, 2]
write(_b | (length - 1));
} else if (length < EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_MEDIUM_LIMIT) {
// [3, 258]
write(_b | EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_MEDIUM_FLAG); // 00000010
write(length - EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_SMALL_LIMIT);
} else {
// [259, 4294967296]
write(_b | EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_LARGE_FLAG); // 00000011
length -= EncodingConstants.OCTET_STRING_LENGTH_7TH_BIT_MEDIUM_LIMIT;
write(length >>> 24);
write((length >> 16) & 0xFF);
write((length >> 8) & 0xFF);
write(length & 0xFF);
}
}
/**
* Encode a non zero integer on the second bit of an octet, setting
* the first bit to 1.
* Implementation of clause C.24 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* <p>
* The first bit of the first octet is set, as specified in clause C.13 of
* ITU-T Rec. X.891 | ISO/IEC 24824-1
*
* @param i The integer to encode, which is a member of the interval
* [0, 1048575]. In the specification the interval is [1, 1048576]
*
*/
protected final void encodeNonZeroIntegerOnSecondBitFirstBitOne(int i) throws IOException {
if (i < EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT) {
// [1, 64] ( [0, 63] ) 6 bits
write(0x80 | i);
} else if (i < EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT) {
// [65, 8256] ( [64, 8255] ) 13 bits
i -= EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT;
_b = (0x80 | EncodingConstants.INTEGER_2ND_BIT_MEDIUM_FLAG) | (i >> 8); // 010 00000
// _b = 0xC0 | (i >> 8); // 010 00000
write(_b);
write(i & 0xFF);
} else if (i < EncodingConstants.INTEGER_2ND_BIT_LARGE_LIMIT) {
// [8257, 1048576] ( [8256, 1048575] ) 20 bits
i -= EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT;
_b = (0x80 | EncodingConstants.INTEGER_2ND_BIT_LARGE_FLAG) | (i >> 16); // 0110 0000
// _b = 0xE0 | (i >> 16); // 0110 0000
write(_b);
write((i >> 8) & 0xFF);
write(i & 0xFF);
} else {
throw new IOException(
CommonResourceBundle.getInstance().getString("message.integerMaxSize",
new Object[]{Integer.valueOf(EncodingConstants.INTEGER_2ND_BIT_LARGE_LIMIT)}));
}
}
/**
* Encode a non zero integer on the second bit of an octet, setting
* the first bit to 0.
* Implementation of clause C.25 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* <p>
* The first bit of the first octet is set, as specified in clause C.13 of
* ITU-T Rec. X.891 | ISO/IEC 24824-1
*
* @param i The integer to encode, which is a member of the interval
* [0, 1048575]. In the specification the interval is [1, 1048576]
*
*/
protected final void encodeNonZeroIntegerOnSecondBitFirstBitZero(int i) throws IOException {
if (i < EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT) {
// [1, 64] ( [0, 63] ) 6 bits
write(i);
} else if (i < EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT) {
// [65, 8256] ( [64, 8255] ) 13 bits
i -= EncodingConstants.INTEGER_2ND_BIT_SMALL_LIMIT;
_b = EncodingConstants.INTEGER_2ND_BIT_MEDIUM_FLAG | (i >> 8); // 010 00000
write(_b);
write(i & 0xFF);
} else {
// [8257, 1048576] ( [8256, 1048575] ) 20 bits
i -= EncodingConstants.INTEGER_2ND_BIT_MEDIUM_LIMIT;
_b = EncodingConstants.INTEGER_2ND_BIT_LARGE_FLAG | (i >> 16); // 0110 0000
write(_b);
write((i >> 8) & 0xFF);
write(i & 0xFF);
}
}
/**
* Encode a non zero integer on the third bit of an octet.
* Implementation of clause C.27 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param i The integer to encode, which is a member of the interval
* [0, 1048575]. In the specification the interval is [1, 1048576]
*
*/
protected final void encodeNonZeroIntegerOnThirdBit(int i) throws IOException {
if (i < EncodingConstants.INTEGER_3RD_BIT_SMALL_LIMIT) {
// [1, 32] ( [0, 31] ) 5 bits
write(_b | i);
} else if (i < EncodingConstants.INTEGER_3RD_BIT_MEDIUM_LIMIT) {
// [33, 2080] ( [32, 2079] ) 11 bits
i -= EncodingConstants.INTEGER_3RD_BIT_SMALL_LIMIT;
_b |= EncodingConstants.INTEGER_3RD_BIT_MEDIUM_FLAG | (i >> 8); // 00100 000
write(_b);
write(i & 0xFF);
} else if (i < EncodingConstants.INTEGER_3RD_BIT_LARGE_LIMIT) {
// [2081, 526368] ( [2080, 526367] ) 19 bits
i -= EncodingConstants.INTEGER_3RD_BIT_MEDIUM_LIMIT;
_b |= EncodingConstants.INTEGER_3RD_BIT_LARGE_FLAG | (i >> 16); // 00101 000
write(_b);
write((i >> 8) & 0xFF);
write(i & 0xFF);
} else {
// [526369, 1048576] ( [526368, 1048575] ) 20 bits
i -= EncodingConstants.INTEGER_3RD_BIT_LARGE_LIMIT;
_b |= EncodingConstants.INTEGER_3RD_BIT_LARGE_LARGE_FLAG; // 00110 000
write(_b);
write(i >> 16);
write((i >> 8) & 0xFF);
write(i & 0xFF);
}
}
/**
* Encode a non zero integer on the fourth bit of an octet.
* Implementation of clause C.28 of ITU-T Rec. X.891 | ISO/IEC 24824-1.
*
* @param i The integer to encode, which is a member of the interval
* [0, 1048575]. In the specification the interval is [1, 1048576]
*
*/
protected final void encodeNonZeroIntegerOnFourthBit(int i) throws IOException {
if (i < EncodingConstants.INTEGER_4TH_BIT_SMALL_LIMIT) {
// [1, 16] ( [0, 15] ) 4 bits
write(_b | i);
} else if (i < EncodingConstants.INTEGER_4TH_BIT_MEDIUM_LIMIT) {
// [17, 1040] ( [16, 1039] ) 10 bits
i -= EncodingConstants.INTEGER_4TH_BIT_SMALL_LIMIT;
_b |= EncodingConstants.INTEGER_4TH_BIT_MEDIUM_FLAG | (i >> 8); // 000 100 00
write(_b);
write(i & 0xFF);
} else if (i < EncodingConstants.INTEGER_4TH_BIT_LARGE_LIMIT) {
// [1041, 263184] ( [1040, 263183] ) 18 bits
i -= EncodingConstants.INTEGER_4TH_BIT_MEDIUM_LIMIT;
_b |= EncodingConstants.INTEGER_4TH_BIT_LARGE_FLAG | (i >> 16); // 000 101 00
write(_b);
write((i >> 8) & 0xFF);
write(i & 0xFF);
} else {
// [263185, 1048576] ( [263184, 1048575] ) 20 bits
i -= EncodingConstants.INTEGER_4TH_BIT_LARGE_LIMIT;
_b |= EncodingConstants.INTEGER_4TH_BIT_LARGE_LARGE_FLAG; // 000 110 00
write(_b);
write(i >> 16);
write((i >> 8) & 0xFF);
write(i & 0xFF);
}
}
/**
* Encode a non empty string using the UTF-8 encoding.
*
* @param b the current octet that is being written.
* @param s the string to be UTF-8 encoded.
* @param constants the array of constants to use when encoding to determin
* how the length of the UTF-8 encoded string is encoded.
*/
protected final void encodeNonEmptyUTF8StringAsOctetString(int b, String s, int[] constants) throws IOException {
final char[] ch = s.toCharArray();
encodeNonEmptyUTF8StringAsOctetString(b, ch, 0, ch.length, constants);
}
/**
* Encode a non empty string using the UTF-8 encoding.
*
* @param b the current octet that is being written.
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
* how the length of the UTF-8 encoded string is encoded.
* @param constants the array of constants to use when encoding to determin
* how the length of the UTF-8 encoded string is encoded.
*/
protected final void encodeNonEmptyUTF8StringAsOctetString(int b, char ch[], int offset, int length, int[] constants) throws IOException {
length = encodeUTF8String(ch, offset, length);
encodeNonZeroOctetStringLength(b, length, constants);
write(_encodingBuffer, length);
}
/**
* Encode the length of non empty UTF-8 encoded string.
*
* @param b the current octet that is being written.
* @param length the length of the UTF-8 encoded string.
* how the length of the UTF-8 encoded string is encoded.
* @param constants the array of constants to use when encoding to determin
* how the length of the UTF-8 encoded string is encoded.
*/
protected final void encodeNonZeroOctetStringLength(int b, int length, int[] constants) throws IOException {
if (length < constants[EncodingConstants.OCTET_STRING_LENGTH_SMALL_LIMIT]) {
write(b | (length - 1));
} else if (length < constants[EncodingConstants.OCTET_STRING_LENGTH_MEDIUM_LIMIT]) {
write(b | constants[EncodingConstants.OCTET_STRING_LENGTH_MEDIUM_FLAG]);
write(length - constants[EncodingConstants.OCTET_STRING_LENGTH_SMALL_LIMIT]);
} else {
write(b | constants[EncodingConstants.OCTET_STRING_LENGTH_LARGE_FLAG]);
length -= constants[EncodingConstants.OCTET_STRING_LENGTH_MEDIUM_LIMIT];
write(length >>> 24);
write((length >> 16) & 0xFF);
write((length >> 8) & 0xFF);
write(length & 0xFF);
}
}
/**
* Encode a non zero integer.
*
* @param b the current octet that is being written.
* @param i the non zero integer.
* @param constants the array of constants to use when encoding to determin
* how the non zero integer is encoded.
*/
protected final void encodeNonZeroInteger(int b, int i, int[] constants) throws IOException {
if (i < constants[EncodingConstants.INTEGER_SMALL_LIMIT]) {
write(b | i);
} else if (i < constants[EncodingConstants.INTEGER_MEDIUM_LIMIT]) {
i -= constants[EncodingConstants.INTEGER_SMALL_LIMIT];
write(b | constants[EncodingConstants.INTEGER_MEDIUM_FLAG] | (i >> 8));
write(i & 0xFF);
} else if (i < constants[EncodingConstants.INTEGER_LARGE_LIMIT]) {
i -= constants[EncodingConstants.INTEGER_MEDIUM_LIMIT];
write(b | constants[EncodingConstants.INTEGER_LARGE_FLAG] | (i >> 16));
write((i >> 8) & 0xFF);
write(i & 0xFF);
} else if (i < EncodingConstants.INTEGER_MAXIMUM_SIZE) {
i -= constants[EncodingConstants.INTEGER_LARGE_LIMIT];
write(b | constants[EncodingConstants.INTEGER_LARGE_LARGE_FLAG]);
write(i >> 16);
write((i >> 8) & 0xFF);
write(i & 0xFF);
} else {
throw new IOException(CommonResourceBundle.getInstance().getString("message.integerMaxSize", new Object[]{Integer.valueOf(EncodingConstants.INTEGER_MAXIMUM_SIZE)}));
}
}
/**
* Mark the current position in the buffered stream.
*/
protected final void mark() {
_markIndex = _octetBufferIndex;
}
/**
* Reset the marked position in the buffered stream.
*/
protected final void resetMark() {
_markIndex = -1;
}
/**
* @return true if the mark has been set, otherwise false if the mark
* has not been set.
*/
protected final boolean hasMark() {
return _markIndex != -1;
}
/**
* Write a byte to the buffered stream.
*/
protected final void write(int i) throws IOException {
if (_octetBufferIndex < _octetBuffer.length) {
_octetBuffer[_octetBufferIndex++] = (byte)i;
} else {
if (_markIndex == -1) {
_s.write(_octetBuffer);
_octetBufferIndex = 1;
_octetBuffer[0] = (byte)i;
} else {
resize(_octetBuffer.length * 3 / 2);
_octetBuffer[_octetBufferIndex++] = (byte)i;
}
}
}
/**
* Write an array of bytes to the buffered stream.
*
* @param b the array of bytes.
* @param length the length of bytes.
*/
protected final void write(byte[] b, int length) throws IOException {
write(b, 0, length);
}
/**
* Write an array of bytes to the buffered stream.
*
* @param b the array of bytes.
* @param offset the offset into the array of bytes.
* @param length the length of bytes.
*/
protected final void write(byte[] b, int offset, int length) throws IOException {
if ((_octetBufferIndex + length) < _octetBuffer.length) {
System.arraycopy(b, offset, _octetBuffer, _octetBufferIndex, length);
_octetBufferIndex += length;
} else {
if (_markIndex == -1) {
_s.write(_octetBuffer, 0, _octetBufferIndex);
_s.write(b, offset, length);
_octetBufferIndex = 0;
} else {
resize((_octetBuffer.length + length) * 3 / 2 + 1);
System.arraycopy(b, offset, _octetBuffer, _octetBufferIndex, length);
_octetBufferIndex += length;
}
}
}
private void ensureSize(int length) {
if ((_octetBufferIndex + length) > _octetBuffer.length) {
resize((_octetBufferIndex + length) * 3 / 2 + 1);
}
}
private void resize(int length) {
byte[] b = new byte[length];
System.arraycopy(_octetBuffer, 0, b, 0, _octetBufferIndex);
_octetBuffer = b;
}
private void _flush() throws IOException {
if (_octetBufferIndex > 0) {
_s.write(_octetBuffer, 0, _octetBufferIndex);
_octetBufferIndex = 0;
}
}
private EncodingBufferOutputStream _encodingBufferOutputStream = new EncodingBufferOutputStream();
private byte[] _encodingBuffer = new byte[512];
private int _encodingBufferIndex;
private class EncodingBufferOutputStream extends OutputStream {
public void write(int b) throws IOException {
if (_encodingBufferIndex < _encodingBuffer.length) {
_encodingBuffer[_encodingBufferIndex++] = (byte)b;
} else {
byte newbuf[] = new byte[Math.max(_encodingBuffer.length << 1, _encodingBufferIndex)];
System.arraycopy(_encodingBuffer, 0, newbuf, 0, _encodingBufferIndex);
_encodingBuffer = newbuf;
_encodingBuffer[_encodingBufferIndex++] = (byte)b;
}
}
public void write(byte b[], int off, int len) throws IOException {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
final int newoffset = _encodingBufferIndex + len;
if (newoffset > _encodingBuffer.length) {
byte newbuf[] = new byte[Math.max(_encodingBuffer.length << 1, newoffset)];
System.arraycopy(_encodingBuffer, 0, newbuf, 0, _encodingBufferIndex);
_encodingBuffer = newbuf;
}
System.arraycopy(b, off, _encodingBuffer, _encodingBufferIndex, len);
_encodingBufferIndex = newoffset;
}
public int getLength() {
return _encodingBufferIndex;
}
public void reset() {
_encodingBufferIndex = 0;
}
}
/**
* Encode a string using the UTF-8 encoding.
*
* @param s the string to encode.
*/
protected final int encodeUTF8String(String s) throws IOException {
final int length = s.length();
if (length < _charBuffer.length) {
s.getChars(0, length, _charBuffer, 0);
return encodeUTF8String(_charBuffer, 0, length);
} else {
char[] ch = s.toCharArray();
return encodeUTF8String(ch, 0, length);
}
}
private void ensureEncodingBufferSizeForUtf8String(int length) {
final int newLength = 4 * length;
if (_encodingBuffer.length < newLength) {
_encodingBuffer = new byte[newLength];
}
}
/**
* Encode a string using the UTF-8 encoding.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
*/
protected final int encodeUTF8String(char[] ch, int offset, int length) throws IOException {
int bpos = 0;
// Make sure buffer is large enough
ensureEncodingBufferSizeForUtf8String(length);
final int end = offset + length;
int c;
while (end != offset) {
c = ch[offset++];
if (c < 0x80) {
// 1 byte, 7 bits
_encodingBuffer[bpos++] = (byte) c;
} else if (c < 0x800) {
// 2 bytes, 11 bits
_encodingBuffer[bpos++] =
(byte) (0xC0 | (c >> 6)); // first 5
_encodingBuffer[bpos++] =
(byte) (0x80 | (c & 0x3F)); // second 6
} else if (c <= '\uFFFF') {
if (!XMLChar.isHighSurrogate(c) && !XMLChar.isLowSurrogate(c)) {
// 3 bytes, 16 bits
_encodingBuffer[bpos++] =
(byte) (0xE0 | (c >> 12)); // first 4
_encodingBuffer[bpos++] =
(byte) (0x80 | ((c >> 6) & 0x3F)); // second 6
_encodingBuffer[bpos++] =
(byte) (0x80 | (c & 0x3F)); // third 6
} else {
// 4 bytes, high and low surrogate
encodeCharacterAsUtf8FourByte(c, ch, offset, end, bpos);
bpos += 4;
offset++;
}
}
}
return bpos;
}
private void encodeCharacterAsUtf8FourByte(int c, char[] ch, int chpos, int chend, int bpos) throws IOException {
if (chpos == chend) {
throw new IOException("");
}
final char d = ch[chpos];
if (!XMLChar.isLowSurrogate(d)) {
throw new IOException("");
}
final int uc = (((c & 0x3ff) << 10) | (d & 0x3ff)) + 0x10000;
if (uc < 0 || uc >= 0x200000) {
throw new IOException("");
}
_encodingBuffer[bpos++] = (byte)(0xF0 | ((uc >> 18)));
_encodingBuffer[bpos++] = (byte)(0x80 | ((uc >> 12) & 0x3F));
_encodingBuffer[bpos++] = (byte)(0x80 | ((uc >> 6) & 0x3F));
_encodingBuffer[bpos++] = (byte)(0x80 | (uc & 0x3F));
}
/**
* Encode a string using the UTF-16 encoding.
*
* @param s the string to encode.
*/
protected final int encodeUtf16String(String s) throws IOException {
final int length = s.length();
if (length < _charBuffer.length) {
s.getChars(0, length, _charBuffer, 0);
return encodeUtf16String(_charBuffer, 0, length);
} else {
char[] ch = s.toCharArray();
return encodeUtf16String(ch, 0, length);
}
}
private void ensureEncodingBufferSizeForUtf16String(int length) {
final int newLength = 2 * length;
if (_encodingBuffer.length < newLength) {
_encodingBuffer = new byte[newLength];
}
}
/**
* Encode a string using the UTF-16 encoding.
*
* @param ch the array of characters.
* @param offset the offset into the array of characters.
* @param length the length of characters.
*/
protected final int encodeUtf16String(char[] ch, int offset, int length) throws IOException {
int byteLength = 0;
// Make sure buffer is large enough
ensureEncodingBufferSizeForUtf16String(length);
final int n = offset + length;
for (int i = offset; i < n; i++) {
final int c = (int) ch[i];
_encodingBuffer[byteLength++] = (byte)(c >> 8);
_encodingBuffer[byteLength++] = (byte)(c & 0xFF);
}
return byteLength;
}
/**
* Obtain the prefix from a qualified name.
*
* @param qName the qualified name
* @return the prefix, or "" if there is no prefix.
*/
public static String getPrefixFromQualifiedName(String qName) {
int i = qName.indexOf(':');
String prefix = "";
if (i != -1) {
prefix = qName.substring(0, i);
}
return prefix;
}
/**
* Check if character array contains characters that are all white space.
*
* @param ch the character array
* @param start the starting character index into the array to check from
* @param length the number of characters to check
* @return true if all characters are white space, false otherwise
*/
public static boolean isWhiteSpace(final char[] ch, int start, final int length) {
if (!XMLChar.isSpace(ch[start])) return false;
final int end = start + length;
while(++start < end && XMLChar.isSpace(ch[start]));
return start == end;
}
/**
* Check if a String contains characters that are all white space.
*
* @param s the string
* @return true if all characters are white space, false otherwise
*/
public static boolean isWhiteSpace(String s) {
if (!XMLChar.isSpace(s.charAt(0))) return false;
final int end = s.length();
int start = 1;
while(start < end && XMLChar.isSpace(s.charAt(start++)));
return start == end;
}
}