From 263d085885df024dca9250cc03c807912b0a7662 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 24 Apr 2012 22:33:21 +0000
Subject: [PATCH] Reformat to comply with new Checkstyle rules.

---
 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1InputStreamReader.java | 1261 +++++++++++++++++++++++++-------------------------------
 1 files changed, 559 insertions(+), 702 deletions(-)

diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1InputStreamReader.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1InputStreamReader.java
index b98198e..b88c07f 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1InputStreamReader.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/asn1/ASN1InputStreamReader.java
@@ -6,17 +6,16 @@
  * (the "License").  You may not use this file except in compliance
  * with the License.
  *
- * You can obtain a copy of the license at
- * trunk/opendj3/legal-notices/CDDLv1_0.txt
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
  * or http://forgerock.org/license/CDDLv1.0.html.
  * See the License for the specific language governing permissions
  * and limitations under the License.
  *
  * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
  *      Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
@@ -28,9 +27,10 @@
 
 package org.forgerock.opendj.asn1;
 
-
-
-import static org.forgerock.opendj.asn1.ASN1Constants.*;
+import static org.forgerock.opendj.asn1.ASN1Constants.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
+import static org.forgerock.opendj.asn1.ASN1Constants.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
+import static org.forgerock.opendj.asn1.ASN1Constants.ELEMENT_READ_STATE_NEED_TYPE;
+import static org.forgerock.opendj.asn1.ASN1Constants.ELEMENT_READ_STATE_NEED_VALUE_BYTES;
 import static org.forgerock.opendj.ldap.CoreMessages.*;
 
 import java.io.IOException;
@@ -46,739 +46,596 @@
 import com.forgerock.opendj.util.SizeLimitInputStream;
 import com.forgerock.opendj.util.StaticUtils;
 
-
-
 /**
  * An ASN1Reader that reads from an input stream.
  */
-final class ASN1InputStreamReader extends AbstractASN1Reader implements
-    ASN1Reader
-{
-  private int state = ELEMENT_READ_STATE_NEED_TYPE;
+final class ASN1InputStreamReader extends AbstractASN1Reader implements ASN1Reader {
+    private int state = ELEMENT_READ_STATE_NEED_TYPE;
 
-  private byte peekType = 0;
+    private byte peekType = 0;
 
-  private int peekLength = -1;
+    private int peekLength = -1;
 
-  private int lengthBytesNeeded = 0;
+    private int lengthBytesNeeded = 0;
 
-  private final int maxElementSize;
+    private final int maxElementSize;
 
-  private InputStream in;
+    private InputStream in;
 
-  private final LinkedList<InputStream> streamStack;
+    private final LinkedList<InputStream> streamStack;
 
-  private byte[] buffer;
+    private byte[] buffer;
 
-
-
-  /**
-   * Creates a new ASN1 reader whose source is the provided input stream and
-   * having a user defined maximum BER element size.
-   *
-   * @param stream
-   *          The input stream to be read.
-   * @param maxElementSize
-   *          The maximum BER element size, or <code>0</code> to indicate that
-   *          there is no limit.
-   */
-  ASN1InputStreamReader(final InputStream stream, final int maxElementSize)
-  {
-    this.in = stream;
-    this.streamStack = new LinkedList<InputStream>();
-    this.buffer = new byte[512];
-    this.maxElementSize = maxElementSize;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void close() throws IOException
-  {
-    // Calling close of SizeLimitInputStream should close the parent
-    // stream.
-    in.close();
-    streamStack.clear();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean elementAvailable() throws IOException
-  {
-    if ((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(false, false))
-    {
-      return false;
-    }
-    if ((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
-        && !needFirstLengthByteState(false, false))
-    {
-      return false;
-    }
-    if ((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
-        && !needAdditionalLengthBytesState(false, false))
-    {
-      return false;
+    /**
+     * Creates a new ASN1 reader whose source is the provided input stream and
+     * having a user defined maximum BER element size.
+     *
+     * @param stream
+     *            The input stream to be read.
+     * @param maxElementSize
+     *            The maximum BER element size, or <code>0</code> to indicate
+     *            that there is no limit.
+     */
+    ASN1InputStreamReader(final InputStream stream, final int maxElementSize) {
+        this.in = stream;
+        this.streamStack = new LinkedList<InputStream>();
+        this.buffer = new byte[512];
+        this.maxElementSize = maxElementSize;
     }
 
-    return peekLength <= in.available();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean hasNextElement() throws IOException
-  {
-    if (!streamStack.isEmpty())
-    {
-      // We are reading a sub sequence. Return true as long as we
-      // haven't exhausted the size limit for the sub sequence sub input
-      // stream.
-      final SizeLimitInputStream subSq = (SizeLimitInputStream) in;
-      return (subSq.getSizeLimit() - subSq.getBytesRead() > 0);
+    /**
+     * {@inheritDoc}
+     */
+    public void close() throws IOException {
+        // Calling close of SizeLimitInputStream should close the parent
+        // stream.
+        in.close();
+        streamStack.clear();
     }
 
-    return (state != ELEMENT_READ_STATE_NEED_TYPE)
-        || needTypeState(true, false);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public int peekLength() throws IOException
-  {
-    peekType();
-
-    switch (state)
-    {
-    case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE:
-      needFirstLengthByteState(true, true);
-      break;
-
-    case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES:
-      needAdditionalLengthBytesState(true, true);
-    }
-
-    return peekLength;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public byte peekType() throws IOException
-  {
-    if (state == ELEMENT_READ_STATE_NEED_TYPE)
-    {
-      needTypeState(true, true);
-    }
-
-    return peekType;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean readBoolean() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    if (peekLength != 1)
-    {
-      final LocalizableMessage message = ERR_ASN1_BOOLEAN_INVALID_LENGTH
-          .get(peekLength);
-      throw DecodeException.fatalError(message);
-    }
-
-    final int readByte = in.read();
-    if (readByte == -1)
-    {
-      final LocalizableMessage message = ERR_ASN1_BOOLEAN_TRUNCATED_VALUE
-          .get(peekLength);
-      throw DecodeException.fatalError(message);
-    }
-
-    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-    {
-      StaticUtils.DEBUG_LOG.finest(String.format(
-          "READ ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)", peekType,
-          peekLength, String.valueOf(readByte != 0x00)));
-    }
-
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-    return readByte != 0x00;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void readEndSequence() throws IOException
-  {
-    if (streamStack.isEmpty())
-    {
-      final LocalizableMessage message = ERR_ASN1_SEQUENCE_READ_NOT_STARTED
-          .get();
-      throw new IllegalStateException(message.toString());
-    }
-
-    // Ignore all unused trailing components.
-    final SizeLimitInputStream subSq = (SizeLimitInputStream) in;
-    if (subSq.getSizeLimit() - subSq.getBytesRead() > 0)
-    {
-      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE))
-      {
-        StaticUtils.DEBUG_LOG.fine(String.format(
-            "Ignoring %d unused trailing bytes in ASN.1 SEQUENCE", subSq
-                .getSizeLimit()
-                - subSq.getBytesRead()));
-      }
-
-      subSq.skip(subSq.getSizeLimit() - subSq.getBytesRead());
-    }
-
-    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-    {
-      StaticUtils.DEBUG_LOG.finest(String.format("READ ASN.1 END SEQUENCE"));
-    }
-
-    in = streamStack.removeFirst();
-
-    // Reset the state
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void readEndSet() throws IOException
-  {
-    // From an implementation point of view, a set is equivalent to a
-    // sequence.
-    readEndSequence();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public int readEnumerated() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    if ((peekLength < 1) || (peekLength > 4))
-    {
-      final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH
-          .get(peekLength);
-      throw DecodeException.fatalError(message);
-    }
-
-    // From an implementation point of view, an enumerated value is
-    // equivalent to an integer.
-    return (int) readInteger();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public long readInteger() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    if ((peekLength < 1) || (peekLength > 8))
-    {
-      final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH
-          .get(peekLength);
-      throw DecodeException.fatalError(message);
-    }
-
-    if (peekLength > 4)
-    {
-      long longValue = 0;
-      for (int i = 0; i < peekLength; i++)
-      {
-        final int readByte = in.read();
-        if (readByte == -1)
-        {
-          final LocalizableMessage message = ERR_ASN1_INTEGER_TRUNCATED_VALUE
-              .get(peekLength);
-          throw DecodeException.fatalError(message);
+    /**
+     * {@inheritDoc}
+     */
+    public boolean elementAvailable() throws IOException {
+        if ((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(false, false)) {
+            return false;
         }
-        if ((i == 0) && (((byte) readByte) < 0))
-        {
-          longValue = 0xFFFFFFFFFFFFFFFFL;
+        if ((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
+                && !needFirstLengthByteState(false, false)) {
+            return false;
         }
-        longValue = (longValue << 8) | (readByte & 0xFF);
-      }
-
-      state = ELEMENT_READ_STATE_NEED_TYPE;
-      return longValue;
-    }
-    else
-    {
-      int intValue = 0;
-      for (int i = 0; i < peekLength; i++)
-      {
-        final int readByte = in.read();
-        if (readByte == -1)
-        {
-          final LocalizableMessage message = ERR_ASN1_INTEGER_TRUNCATED_VALUE
-              .get(peekLength);
-          throw DecodeException.fatalError(message);
+        if ((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
+                && !needAdditionalLengthBytesState(false, false)) {
+            return false;
         }
-        if ((i == 0) && (((byte) readByte) < 0))
-        {
-          intValue = 0xFFFFFFFF;
+
+        return peekLength <= in.available();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean hasNextElement() throws IOException {
+        if (!streamStack.isEmpty()) {
+            // We are reading a sub sequence. Return true as long as we
+            // haven't exhausted the size limit for the sub sequence sub input
+            // stream.
+            final SizeLimitInputStream subSq = (SizeLimitInputStream) in;
+            return (subSq.getSizeLimit() - subSq.getBytesRead() > 0);
         }
-        intValue = (intValue << 8) | (readByte & 0xFF);
-      }
 
-      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-      {
-        StaticUtils.DEBUG_LOG.finest(String.format(
-            "READ ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", peekType,
-            peekLength, intValue));
-      }
-
-      state = ELEMENT_READ_STATE_NEED_TYPE;
-      return intValue;
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void readNull() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    // Make sure that the decoded length is exactly zero byte.
-    if (peekLength != 0)
-    {
-      final LocalizableMessage message = ERR_ASN1_NULL_INVALID_LENGTH
-          .get(peekLength);
-      throw DecodeException.fatalError(message);
+        return (state != ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(true, false);
     }
 
-    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-    {
-      StaticUtils.DEBUG_LOG.finest(String.format(
-          "READ ASN.1 NULL(type=0x%x, length=%d)", peekType, peekLength));
-    }
+    /**
+     * {@inheritDoc}
+     */
+    public int peekLength() throws IOException {
+        peekType();
 
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-  }
+        switch (state) {
+        case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE:
+            needFirstLengthByteState(true, true);
+            break;
 
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public ByteString readOctetString() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    if (peekLength == 0)
-    {
-      state = ELEMENT_READ_STATE_NEED_TYPE;
-      return ByteString.empty();
-    }
-
-    // Copy the value and construct the element to return.
-    final byte[] value = new byte[peekLength];
-    int bytesNeeded = peekLength;
-    int bytesRead;
-    while (bytesNeeded > 0)
-    {
-      bytesRead = in.read(value, peekLength - bytesNeeded, bytesNeeded);
-      if (bytesRead < 0)
-      {
-        final LocalizableMessage message = ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE
-            .get(peekLength);
-        throw DecodeException.fatalError(message);
-      }
-
-      bytesNeeded -= bytesRead;
-    }
-
-    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-    {
-      StaticUtils.DEBUG_LOG
-          .finest(String.format("READ ASN.1 OCTETSTRING(type=0x%x, length=%d)",
-              peekType, peekLength));
-    }
-
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-    return ByteString.wrap(value);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public ByteStringBuilder readOctetString(final ByteStringBuilder builder)
-      throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    if (peekLength == 0)
-    {
-      state = ELEMENT_READ_STATE_NEED_TYPE;
-      return builder;
-    }
-
-    // Copy the value and construct the element to return.
-    int bytesNeeded = peekLength;
-    int bytesRead;
-    while (bytesNeeded > 0)
-    {
-      bytesRead = builder.append(in, bytesNeeded);
-      if (bytesRead < 0)
-      {
-        final LocalizableMessage message = ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE
-            .get(peekLength);
-        throw DecodeException.fatalError(message);
-      }
-      bytesNeeded -= bytesRead;
-    }
-
-    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-    {
-      StaticUtils.DEBUG_LOG
-          .finest(String.format("READ ASN.1 OCTETSTRING(type=0x%x, length=%d)",
-              peekType, peekLength));
-    }
-
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-    return builder;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public String readOctetStringAsString() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    if (peekLength == 0)
-    {
-      state = ELEMENT_READ_STATE_NEED_TYPE;
-      return "";
-    }
-
-    // Resize the temp buffer if needed
-    if (peekLength > buffer.length)
-    {
-      buffer = new byte[peekLength];
-    }
-
-    int bytesNeeded = peekLength;
-    int bytesRead;
-    while (bytesNeeded > 0)
-    {
-      bytesRead = in.read(buffer, peekLength - bytesNeeded, bytesNeeded);
-      if (bytesRead < 0)
-      {
-        final LocalizableMessage message = ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE
-            .get(peekLength);
-        throw DecodeException.fatalError(message);
-      }
-      bytesNeeded -= bytesRead;
-    }
-
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-
-    String str;
-    try
-    {
-      str = new String(buffer, 0, peekLength, "UTF-8");
-    }
-    catch (final Exception e)
-    {
-      if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
-      {
-        StaticUtils.DEBUG_LOG.warning("Unable to decode ASN.1 OCTETSTRING "
-            + "bytes as UTF-8 string: " + e.toString());
-      }
-
-      str = new String(buffer, 0, peekLength);
-    }
-
-    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-    {
-      StaticUtils.DEBUG_LOG.finest(String.format(
-          "READ ASN.1 OCTETSTRING(type=0x%x, length=%d, value=%s)", peekType,
-          peekLength, str));
-    }
-
-    return str;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void readStartSequence() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    final SizeLimitInputStream subStream = new SizeLimitInputStream(in,
-        peekLength);
-
-    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
-    {
-      StaticUtils.DEBUG_LOG.finest(String.format(
-          "READ ASN.1 START SEQUENCE(type=0x%x, length=%d)", peekType,
-          peekLength));
-    }
-
-    streamStack.addFirst(in);
-    in = subStream;
-
-    // Reset the state
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void readStartSet() throws IOException
-  {
-    // From an implementation point of view, a set is equivalent to a
-    // sequence.
-    readStartSequence();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public ASN1Reader skipElement() throws IOException
-  {
-    // Read the header if haven't done so already
-    peekLength();
-
-    final long bytesSkipped = in.skip(peekLength);
-    if (bytesSkipped != peekLength)
-    {
-      final LocalizableMessage message = ERR_ASN1_SKIP_TRUNCATED_VALUE
-          .get(peekLength);
-      throw DecodeException.fatalError(message);
-    }
-    state = ELEMENT_READ_STATE_NEED_TYPE;
-    return this;
-  }
-
-
-
-  /**
-   * Internal helper method reading the additional ASN.1 length bytes and
-   * transition to the next state if successful.
-   *
-   * @param isBlocking
-   *          <code>true</code> to block if the type byte is not available or
-   *          <code>false</code> to check for availability first.
-   * @param throwEofException
-   *          <code>true</code> to throw an exception when an EOF is encountered
-   *          or <code>false</code> to return false.
-   * @return <code>true</code> if the length bytes was successfully read.
-   * @throws IOException
-   *           If an error occurs while reading from the stream.
-   */
-  private boolean needAdditionalLengthBytesState(final boolean isBlocking,
-      final boolean throwEofException) throws IOException
-  {
-    if (!isBlocking && (in.available() < lengthBytesNeeded))
-    {
-      return false;
-    }
-
-    int readByte;
-    while (lengthBytesNeeded > 0)
-    {
-      readByte = in.read();
-      if (readByte == -1)
-      {
-        state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
-        if (throwEofException)
-        {
-          final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTES
-              .get(lengthBytesNeeded);
-          throw DecodeException.fatalError(message);
+        case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES:
+            needAdditionalLengthBytesState(true, true);
         }
-        return false;
-      }
-      peekLength = (peekLength << 8) | (readByte & 0xFF);
-      lengthBytesNeeded--;
+
+        return peekLength;
     }
 
-    // Make sure that the element is not larger than the maximum allowed
-    // message size.
-    if ((maxElementSize > 0) && (peekLength > maxElementSize))
-    {
-      final LocalizableMessage message = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
-          .get(peekLength, maxElementSize);
-      throw DecodeException.fatalError(message);
-    }
-    state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
-    return true;
-  }
+    /**
+     * {@inheritDoc}
+     */
+    public byte peekType() throws IOException {
+        if (state == ELEMENT_READ_STATE_NEED_TYPE) {
+            needTypeState(true, true);
+        }
 
-
-
-  /**
-   * Internal helper method reading the first length bytes and transition to the
-   * next state if successful.
-   *
-   * @param isBlocking
-   *          <code>true</code> to block if the type byte is not available or
-   *          <code>false</code> to check for availability first.
-   * @param throwEofException
-   *          <code>true</code> to throw an exception when an EOF is encountered
-   *          or <code>false</code> to return false.
-   * @return <code>true</code> if the length bytes was successfully read
-   * @throws IOException
-   *           If an error occurs while reading from the stream.
-   */
-  private boolean needFirstLengthByteState(final boolean isBlocking,
-      final boolean throwEofException) throws IOException
-  {
-    if (!isBlocking && (in.available() <= 0))
-    {
-      return false;
+        return peekType;
     }
 
-    int readByte = in.read();
-    if (readByte == -1)
-    {
-      if (throwEofException)
-      {
-        final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
-        throw DecodeException.fatalError(message);
-      }
-      return false;
-    }
-    peekLength = (readByte & 0x7F);
-    if (peekLength != readByte)
-    {
-      lengthBytesNeeded = peekLength;
-      if (lengthBytesNeeded > 4)
-      {
-        final LocalizableMessage message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES
-            .get(lengthBytesNeeded);
-        throw DecodeException.fatalError(message);
-      }
-      peekLength = 0x00;
+    /**
+     * {@inheritDoc}
+     */
+    public boolean readBoolean() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
 
-      if (!isBlocking && (in.available() < lengthBytesNeeded))
-      {
-        state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
-        return false;
-      }
-
-      while (lengthBytesNeeded > 0)
-      {
-        readByte = in.read();
-        if (readByte == -1)
-        {
-          state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
-          if (throwEofException)
-          {
-            final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTES
-                .get(lengthBytesNeeded);
+        if (peekLength != 1) {
+            final LocalizableMessage message = ERR_ASN1_BOOLEAN_INVALID_LENGTH.get(peekLength);
             throw DecodeException.fatalError(message);
-          }
-          return false;
         }
-        peekLength = (peekLength << 8) | (readByte & 0xFF);
-        lengthBytesNeeded--;
-      }
+
+        final int readByte = in.read();
+        if (readByte == -1) {
+            final LocalizableMessage message = ERR_ASN1_BOOLEAN_TRUNCATED_VALUE.get(peekLength);
+            throw DecodeException.fatalError(message);
+        }
+
+        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+            StaticUtils.DEBUG_LOG.finest(String.format(
+                    "READ ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)", peekType, peekLength,
+                    String.valueOf(readByte != 0x00)));
+        }
+
+        state = ELEMENT_READ_STATE_NEED_TYPE;
+        return readByte != 0x00;
     }
 
-    // Make sure that the element is not larger than the maximum allowed
-    // message size.
-    if ((maxElementSize > 0) && (peekLength > maxElementSize))
-    {
-      final LocalizableMessage message = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
-          .get(peekLength, maxElementSize);
-      throw DecodeException.fatalError(message);
-    }
-    state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
-    return true;
-  }
+    /**
+     * {@inheritDoc}
+     */
+    public void readEndSequence() throws IOException {
+        if (streamStack.isEmpty()) {
+            final LocalizableMessage message = ERR_ASN1_SEQUENCE_READ_NOT_STARTED.get();
+            throw new IllegalStateException(message.toString());
+        }
 
+        // Ignore all unused trailing components.
+        final SizeLimitInputStream subSq = (SizeLimitInputStream) in;
+        if (subSq.getSizeLimit() - subSq.getBytesRead() > 0) {
+            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE)) {
+                StaticUtils.DEBUG_LOG.fine(String.format(
+                        "Ignoring %d unused trailing bytes in ASN.1 SEQUENCE", subSq.getSizeLimit()
+                                - subSq.getBytesRead()));
+            }
 
+            subSq.skip(subSq.getSizeLimit() - subSq.getBytesRead());
+        }
 
-  /**
-   * Internal helper method reading the ASN.1 type byte and transition to the
-   * next state if successful.
-   *
-   * @param isBlocking
-   *          <code>true</code> to block if the type byte is not available or
-   *          <code>false</code> to check for availability first.
-   * @param throwEofException
-   *          <code>true</code> to throw an exception when an EOF is encountered
-   *          or <code>false</code> to return false.
-   * @return <code>true</code> if the type byte was successfully read
-   * @throws IOException
-   *           If an error occurs while reading from the stream.
-   */
-  private boolean needTypeState(final boolean isBlocking,
-      final boolean throwEofException) throws IOException
-  {
-    // Read just the type.
-    if (!isBlocking && (in.available() <= 0))
-    {
-      return false;
+        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+            StaticUtils.DEBUG_LOG.finest(String.format("READ ASN.1 END SEQUENCE"));
+        }
+
+        in = streamStack.removeFirst();
+
+        // Reset the state
+        state = ELEMENT_READ_STATE_NEED_TYPE;
     }
 
-    final int type = in.read();
-    if (type == -1)
-    {
-      if (throwEofException)
-      {
-        final LocalizableMessage message = ERR_ASN1_TRUCATED_TYPE_BYTE.get();
-        throw DecodeException.fatalError(message);
-      }
-      return false;
+    /**
+     * {@inheritDoc}
+     */
+    public void readEndSet() throws IOException {
+        // From an implementation point of view, a set is equivalent to a
+        // sequence.
+        readEndSequence();
     }
 
-    peekType = (byte) type;
-    state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
-    return true;
-  }
+    /**
+     * {@inheritDoc}
+     */
+    public int readEnumerated() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        if ((peekLength < 1) || (peekLength > 4)) {
+            final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength);
+            throw DecodeException.fatalError(message);
+        }
+
+        // From an implementation point of view, an enumerated value is
+        // equivalent to an integer.
+        return (int) readInteger();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public long readInteger() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        if ((peekLength < 1) || (peekLength > 8)) {
+            final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength);
+            throw DecodeException.fatalError(message);
+        }
+
+        if (peekLength > 4) {
+            long longValue = 0;
+            for (int i = 0; i < peekLength; i++) {
+                final int readByte = in.read();
+                if (readByte == -1) {
+                    final LocalizableMessage message =
+                            ERR_ASN1_INTEGER_TRUNCATED_VALUE.get(peekLength);
+                    throw DecodeException.fatalError(message);
+                }
+                if ((i == 0) && (((byte) readByte) < 0)) {
+                    longValue = 0xFFFFFFFFFFFFFFFFL;
+                }
+                longValue = (longValue << 8) | (readByte & 0xFF);
+            }
+
+            state = ELEMENT_READ_STATE_NEED_TYPE;
+            return longValue;
+        } else {
+            int intValue = 0;
+            for (int i = 0; i < peekLength; i++) {
+                final int readByte = in.read();
+                if (readByte == -1) {
+                    final LocalizableMessage message =
+                            ERR_ASN1_INTEGER_TRUNCATED_VALUE.get(peekLength);
+                    throw DecodeException.fatalError(message);
+                }
+                if ((i == 0) && (((byte) readByte) < 0)) {
+                    intValue = 0xFFFFFFFF;
+                }
+                intValue = (intValue << 8) | (readByte & 0xFF);
+            }
+
+            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+                StaticUtils.DEBUG_LOG.finest(String.format(
+                        "READ ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", peekType, peekLength,
+                        intValue));
+            }
+
+            state = ELEMENT_READ_STATE_NEED_TYPE;
+            return intValue;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void readNull() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        // Make sure that the decoded length is exactly zero byte.
+        if (peekLength != 0) {
+            final LocalizableMessage message = ERR_ASN1_NULL_INVALID_LENGTH.get(peekLength);
+            throw DecodeException.fatalError(message);
+        }
+
+        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+            StaticUtils.DEBUG_LOG.finest(String.format("READ ASN.1 NULL(type=0x%x, length=%d)",
+                    peekType, peekLength));
+        }
+
+        state = ELEMENT_READ_STATE_NEED_TYPE;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ByteString readOctetString() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        if (peekLength == 0) {
+            state = ELEMENT_READ_STATE_NEED_TYPE;
+            return ByteString.empty();
+        }
+
+        // Copy the value and construct the element to return.
+        final byte[] value = new byte[peekLength];
+        int bytesNeeded = peekLength;
+        int bytesRead;
+        while (bytesNeeded > 0) {
+            bytesRead = in.read(value, peekLength - bytesNeeded, bytesNeeded);
+            if (bytesRead < 0) {
+                final LocalizableMessage message =
+                        ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE.get(peekLength);
+                throw DecodeException.fatalError(message);
+            }
+
+            bytesNeeded -= bytesRead;
+        }
+
+        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+            StaticUtils.DEBUG_LOG.finest(String.format(
+                    "READ ASN.1 OCTETSTRING(type=0x%x, length=%d)", peekType, peekLength));
+        }
+
+        state = ELEMENT_READ_STATE_NEED_TYPE;
+        return ByteString.wrap(value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ByteStringBuilder readOctetString(final ByteStringBuilder builder) throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        if (peekLength == 0) {
+            state = ELEMENT_READ_STATE_NEED_TYPE;
+            return builder;
+        }
+
+        // Copy the value and construct the element to return.
+        int bytesNeeded = peekLength;
+        int bytesRead;
+        while (bytesNeeded > 0) {
+            bytesRead = builder.append(in, bytesNeeded);
+            if (bytesRead < 0) {
+                final LocalizableMessage message =
+                        ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE.get(peekLength);
+                throw DecodeException.fatalError(message);
+            }
+            bytesNeeded -= bytesRead;
+        }
+
+        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+            StaticUtils.DEBUG_LOG.finest(String.format(
+                    "READ ASN.1 OCTETSTRING(type=0x%x, length=%d)", peekType, peekLength));
+        }
+
+        state = ELEMENT_READ_STATE_NEED_TYPE;
+        return builder;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String readOctetStringAsString() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        if (peekLength == 0) {
+            state = ELEMENT_READ_STATE_NEED_TYPE;
+            return "";
+        }
+
+        // Resize the temp buffer if needed
+        if (peekLength > buffer.length) {
+            buffer = new byte[peekLength];
+        }
+
+        int bytesNeeded = peekLength;
+        int bytesRead;
+        while (bytesNeeded > 0) {
+            bytesRead = in.read(buffer, peekLength - bytesNeeded, bytesNeeded);
+            if (bytesRead < 0) {
+                final LocalizableMessage message =
+                        ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE.get(peekLength);
+                throw DecodeException.fatalError(message);
+            }
+            bytesNeeded -= bytesRead;
+        }
+
+        state = ELEMENT_READ_STATE_NEED_TYPE;
+
+        String str;
+        try {
+            str = new String(buffer, 0, peekLength, "UTF-8");
+        } catch (final Exception e) {
+            if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING)) {
+                StaticUtils.DEBUG_LOG.warning("Unable to decode ASN.1 OCTETSTRING "
+                        + "bytes as UTF-8 string: " + e.toString());
+            }
+
+            str = new String(buffer, 0, peekLength);
+        }
+
+        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+            StaticUtils.DEBUG_LOG.finest(String.format(
+                    "READ ASN.1 OCTETSTRING(type=0x%x, length=%d, value=%s)", peekType, peekLength,
+                    str));
+        }
+
+        return str;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void readStartSequence() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        final SizeLimitInputStream subStream = new SizeLimitInputStream(in, peekLength);
+
+        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST)) {
+            StaticUtils.DEBUG_LOG.finest(String.format(
+                    "READ ASN.1 START SEQUENCE(type=0x%x, length=%d)", peekType, peekLength));
+        }
+
+        streamStack.addFirst(in);
+        in = subStream;
+
+        // Reset the state
+        state = ELEMENT_READ_STATE_NEED_TYPE;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void readStartSet() throws IOException {
+        // From an implementation point of view, a set is equivalent to a
+        // sequence.
+        readStartSequence();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ASN1Reader skipElement() throws IOException {
+        // Read the header if haven't done so already
+        peekLength();
+
+        final long bytesSkipped = in.skip(peekLength);
+        if (bytesSkipped != peekLength) {
+            final LocalizableMessage message = ERR_ASN1_SKIP_TRUNCATED_VALUE.get(peekLength);
+            throw DecodeException.fatalError(message);
+        }
+        state = ELEMENT_READ_STATE_NEED_TYPE;
+        return this;
+    }
+
+    /**
+     * Internal helper method reading the additional ASN.1 length bytes and
+     * transition to the next state if successful.
+     *
+     * @param isBlocking
+     *            <code>true</code> to block if the type byte is not available
+     *            or <code>false</code> to check for availability first.
+     * @param throwEofException
+     *            <code>true</code> to throw an exception when an EOF is
+     *            encountered or <code>false</code> to return false.
+     * @return <code>true</code> if the length bytes was successfully read.
+     * @throws IOException
+     *             If an error occurs while reading from the stream.
+     */
+    private boolean needAdditionalLengthBytesState(final boolean isBlocking,
+            final boolean throwEofException) throws IOException {
+        if (!isBlocking && (in.available() < lengthBytesNeeded)) {
+            return false;
+        }
+
+        int readByte;
+        while (lengthBytesNeeded > 0) {
+            readByte = in.read();
+            if (readByte == -1) {
+                state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
+                if (throwEofException) {
+                    final LocalizableMessage message =
+                            ERR_ASN1_TRUNCATED_LENGTH_BYTES.get(lengthBytesNeeded);
+                    throw DecodeException.fatalError(message);
+                }
+                return false;
+            }
+            peekLength = (peekLength << 8) | (readByte & 0xFF);
+            lengthBytesNeeded--;
+        }
+
+        // Make sure that the element is not larger than the maximum allowed
+        // message size.
+        if ((maxElementSize > 0) && (peekLength > maxElementSize)) {
+            final LocalizableMessage message =
+                    ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
+                            .get(peekLength, maxElementSize);
+            throw DecodeException.fatalError(message);
+        }
+        state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
+        return true;
+    }
+
+    /**
+     * Internal helper method reading the first length bytes and transition to
+     * the next state if successful.
+     *
+     * @param isBlocking
+     *            <code>true</code> to block if the type byte is not available
+     *            or <code>false</code> to check for availability first.
+     * @param throwEofException
+     *            <code>true</code> to throw an exception when an EOF is
+     *            encountered or <code>false</code> to return false.
+     * @return <code>true</code> if the length bytes was successfully read
+     * @throws IOException
+     *             If an error occurs while reading from the stream.
+     */
+    private boolean needFirstLengthByteState(final boolean isBlocking,
+            final boolean throwEofException) throws IOException {
+        if (!isBlocking && (in.available() <= 0)) {
+            return false;
+        }
+
+        int readByte = in.read();
+        if (readByte == -1) {
+            if (throwEofException) {
+                final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
+                throw DecodeException.fatalError(message);
+            }
+            return false;
+        }
+        peekLength = (readByte & 0x7F);
+        if (peekLength != readByte) {
+            lengthBytesNeeded = peekLength;
+            if (lengthBytesNeeded > 4) {
+                final LocalizableMessage message =
+                        ERR_ASN1_INVALID_NUM_LENGTH_BYTES.get(lengthBytesNeeded);
+                throw DecodeException.fatalError(message);
+            }
+            peekLength = 0x00;
+
+            if (!isBlocking && (in.available() < lengthBytesNeeded)) {
+                state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
+                return false;
+            }
+
+            while (lengthBytesNeeded > 0) {
+                readByte = in.read();
+                if (readByte == -1) {
+                    state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
+                    if (throwEofException) {
+                        final LocalizableMessage message =
+                                ERR_ASN1_TRUNCATED_LENGTH_BYTES.get(lengthBytesNeeded);
+                        throw DecodeException.fatalError(message);
+                    }
+                    return false;
+                }
+                peekLength = (peekLength << 8) | (readByte & 0xFF);
+                lengthBytesNeeded--;
+            }
+        }
+
+        // Make sure that the element is not larger than the maximum allowed
+        // message size.
+        if ((maxElementSize > 0) && (peekLength > maxElementSize)) {
+            final LocalizableMessage message =
+                    ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
+                            .get(peekLength, maxElementSize);
+            throw DecodeException.fatalError(message);
+        }
+        state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
+        return true;
+    }
+
+    /**
+     * Internal helper method reading the ASN.1 type byte and transition to the
+     * next state if successful.
+     *
+     * @param isBlocking
+     *            <code>true</code> to block if the type byte is not available
+     *            or <code>false</code> to check for availability first.
+     * @param throwEofException
+     *            <code>true</code> to throw an exception when an EOF is
+     *            encountered or <code>false</code> to return false.
+     * @return <code>true</code> if the type byte was successfully read
+     * @throws IOException
+     *             If an error occurs while reading from the stream.
+     */
+    private boolean needTypeState(final boolean isBlocking, final boolean throwEofException)
+            throws IOException {
+        // Read just the type.
+        if (!isBlocking && (in.available() <= 0)) {
+            return false;
+        }
+
+        final int type = in.read();
+        if (type == -1) {
+            if (throwEofException) {
+                final LocalizableMessage message = ERR_ASN1_TRUCATED_TYPE_BYTE.get();
+                throw DecodeException.fatalError(message);
+            }
+            return false;
+        }
+
+        peekType = (byte) type;
+        state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
+        return true;
+    }
 }

--
Gitblit v1.10.0