You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by nd...@apache.org on 2021/12/15 15:59:19 UTC

[tika] branch TIKA-3446-1.x-port created (now 434ec58)

This is an automated email from the ASF dual-hosted git repository.

ndipiazza pushed a change to branch TIKA-3446-1.x-port
in repository https://gitbox.apache.org/repos/asf/tika.git.


      at 434ec58  port the TIKA-3446 work from the 2.x branch.

This branch includes the following new commits:

     new 434ec58  port the TIKA-3446 work from the 2.x branch.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[tika] 01/01: port the TIKA-3446 work from the 2.x branch.

Posted by nd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ndipiazza pushed a commit to branch TIKA-3446-1.x-port
in repository https://gitbox.apache.org/repos/asf/tika.git

commit 434ec584104aae238a32c0882411a389949296ac
Author: Nicholas DiPiazza <ni...@lucidworks.com>
AuthorDate: Wed Dec 15 09:59:04 2021 -0600

    port the TIKA-3446 work from the 2.x branch.
---
 .../tika/parser/microsoft/onenote/CompactID.java   |  10 +-
 .../tika/parser/microsoft/onenote/Error.java       |  12 +-
 .../parser/microsoft/onenote/ExtendedGUID.java     |  37 +-
 .../microsoft/onenote/FileChunkReference.java      |  21 +-
 .../microsoft/onenote/FileDataStoreObject.java     |   1 +
 .../tika/parser/microsoft/onenote/FileNode.java    | 105 ++--
 .../parser/microsoft/onenote/FileNodeList.java     |   1 +
 .../microsoft/onenote/FileNodeListHeader.java      |  35 +-
 .../tika/parser/microsoft/onenote/FileNodePtr.java |  10 +-
 .../microsoft/onenote/FileNodePtrBackPush.java     |   1 +
 .../parser/microsoft/onenote/FileNodeUnion.java    |  34 +-
 .../microsoft/onenote/FndStructureConstants.java   |  44 +-
 .../apache/tika/parser/microsoft/onenote/GUID.java |  80 +--
 .../tika/parser/microsoft/onenote/IndentUtil.java  |   7 +-
 .../tika/parser/microsoft/onenote/Int24.java       |   1 +
 .../apache/tika/parser/microsoft/onenote/JCID.java |  45 +-
 .../microsoft/onenote/JCIDPropertySetTypeEnum.java |  76 ++-
 .../onenote/ObjectDeclarationWithRefCount.java     |  27 +-
 .../onenote/ObjectDeclarationWithRefCountBody.java |   3 +-
 .../onenote/ObjectSpaceObjectPropSet.java          |  14 +-
 ...ctSpaceObjectStreamOfOIDsOSIDsOrContextIDs.java |   6 +-
 .../onenote/OneNoteDirectFileResource.java         |  11 +-
 .../parser/microsoft/onenote/OneNoteDocument.java  |  14 +-
 .../parser/microsoft/onenote/OneNoteHeader.java    |  36 +-
 .../onenote/OneNoteLegacyDumpStrings.java          |  52 +-
 .../parser/microsoft/onenote/OneNoteParser.java    | 188 ++++---
 .../microsoft/onenote/OneNotePropertyEnum.java     | 213 +++-----
 .../microsoft/onenote/OneNotePropertyId.java       |  14 +-
 .../tika/parser/microsoft/onenote/OneNotePtr.java  | 521 ++++++++++---------
 .../microsoft/onenote/OneNoteTreeWalker.java       | 210 ++++----
 .../onenote/OneNoteTreeWalkerOptions.java          |  17 +-
 .../parser/microsoft/onenote/PropertyIDType.java   |   8 +-
 .../tika/parser/microsoft/onenote/PropertySet.java |  38 +-
 .../parser/microsoft/onenote/PropertyValue.java    |  20 +-
 .../tika/parser/microsoft/onenote/Revision.java    |  24 +-
 .../parser/microsoft/onenote/RevisionManifest.java |   1 +
 .../microsoft/onenote/RootObjectReference.java     |   3 +-
 .../IFSSHTTPBSerializable.java}                    |  26 +-
 .../onenote/fsshttpb/MSOneStorePackage.java        | 307 ++++++++++++
 .../onenote/fsshttpb/MSOneStoreParser.java         | 199 ++++++++
 .../exception/DataElementParseErrorException.java} |  19 +-
 .../onenote/fsshttpb/property/ArrayNumber.java     |  52 ++
 .../fsshttpb/property/EightBytesOfData.java        |  52 ++
 .../onenote/fsshttpb/property/FourBytesOfData.java |  51 ++
 .../property/IProperty.java}                       |  42 +-
 .../property/NoData.java}                          |  41 +-
 .../onenote/fsshttpb/property/OneByteOfData.java   |  49 ++
 .../property/PrtArrayOfPropertyValues.java         |  77 +++
 .../PrtFourBytesOfLengthFollowedByData.java        |  68 +++
 .../onenote/fsshttpb/property/TwoBytesOfData.java  |  52 ++
 .../streamobj/CellManifestCurrentRevision.java     |  72 +++
 .../streamobj/CellManifestDataElementData.java     |  64 +++
 .../onenote/fsshttpb/streamobj/DataElement.java    | 189 +++++++
 .../fsshttpb/streamobj/DataElementData.java        |  46 ++
 .../fsshttpb/streamobj/DataElementHash.java        |  80 +++
 .../fsshttpb/streamobj/DataElementPackage.java     |  81 +++
 .../onenote/fsshttpb/streamobj/DataHashObject.java | 103 ++++
 .../onenote/fsshttpb/streamobj/DataSizeObject.java |  71 +++
 .../streamobj/EncryptionObject.java}               |  20 +-
 .../streamobj/FileDataObject.java}                 |  19 +-
 .../fsshttpb/streamobj/IntermediateNodeObject.java | 114 +++++
 .../onenote/fsshttpb/streamobj/JCIDObject.java     |  45 ++
 .../onenote/fsshttpb/streamobj/LeafNodeObject.java | 257 ++++++++++
 .../onenote/fsshttpb/streamobj/NodeObject.java     |  45 ++
 .../fsshttpb/streamobj/ObjectGroupData.java        | 115 +++++
 .../streamobj/ObjectGroupDataElementData.java      | 286 +++++++++++
 .../streamobj/ObjectGroupDeclarations.java         | 112 +++++
 .../fsshttpb/streamobj/ObjectGroupMetadata.java    |  83 +++
 .../streamobj/ObjectGroupMetadataDeclarations.java |  97 ++++
 .../ObjectGroupObjectBLOBDataDeclaration.java      | 103 ++++
 .../fsshttpb/streamobj/ObjectGroupObjectData.java  |  83 +++
 .../ObjectGroupObjectDataBLOBReference.java        |  86 ++++
 .../streamobj/ObjectGroupObjectDeclare.java        | 103 ++++
 .../onenote/fsshttpb/streamobj/PropertySet.java    | 136 +++++
 .../fsshttpb/streamobj/PropertySetObject.java      |  46 ++
 .../fsshttpb/streamobj/RevisionManifest.java       |  75 +++
 .../streamobj/RevisionManifestDataElementData.java | 109 ++++
 .../RevisionManifestObjectGroupReferences.java     |  85 ++++
 .../streamobj/RevisionManifestRootDeclare.java     |  77 +++
 .../streamobj/RevisionStoreObject.java}            |  40 +-
 .../streamobj/RevisionStoreObjectGroup.java        | 118 +++++
 .../fsshttpb/streamobj/SignatureObject.java        |  82 +++
 .../streamobj/StorageIndexCellMapping.java         |  84 ++++
 .../streamobj/StorageIndexDataElementData.java     | 119 +++++
 .../streamobj/StorageIndexManifestMapping.java     |  76 +++
 .../streamobj/StorageIndexRevisionMapping.java     |  83 +++
 .../streamobj/StorageManifestDataElementData.java  |  96 ++++
 .../streamobj/StorageManifestRootDeclare.java      |  79 +++
 .../streamobj/StorageManifestSchemaGUID.java       |  77 +++
 .../onenote/fsshttpb/streamobj/StreamObject.java   | 329 ++++++++++++
 .../streamobj/StreamObjectHeaderEnd.java}          |  19 +-
 .../streamobj/StreamObjectHeaderEnd16bit.java      | 117 +++++
 .../streamobj/StreamObjectHeaderEnd8bit.java       | 121 +++++
 .../streamobj/StreamObjectHeaderStart.java         |  94 ++++
 .../streamobj/StreamObjectHeaderStart16bit.java    | 144 ++++++
 .../streamobj/StreamObjectHeaderStart32bit.java    | 145 ++++++
 .../streamobj/StreamObjectParseErrorException.java |  54 ++
 .../streamobj/StreamObjectTypeHeaderEnd.java       | 175 +++++++
 .../streamobj/StreamObjectTypeHeaderStart.java     | 457 +++++++++++++++++
 .../fsshttpb/streamobj/basic/AdapterHelper.java    |  65 +++
 .../streamobj/basic/AlternativePackaging.java      |  81 +++
 .../fsshttpb/streamobj/basic/BasicObject.java      |  79 +++
 .../fsshttpb/streamobj/basic/BinaryItem.java       |  89 ++++
 .../onenote/fsshttpb/streamobj/basic/CellID.java   | 127 +++++
 .../fsshttpb/streamobj/basic/CellIDArray.java      | 101 ++++
 .../fsshttpb/streamobj/basic/Compact64bitInt.java  | 224 +++++++++
 .../fsshttpb/streamobj/basic/CompactID.java        |  59 +++
 .../fsshttpb/streamobj/basic/DataElementType.java  |  90 ++++
 .../streamobj/basic/DataNodeObjectData.java        |  52 ++
 .../fsshttpb/streamobj/basic/ExGUIDArray.java      | 118 +++++
 .../onenote/fsshttpb/streamobj/basic/ExGuid.java   | 201 ++++++++
 .../fsshttpb/streamobj/basic/HeaderCell.java       |  61 +++
 .../onenote/fsshttpb/streamobj/basic/JCID.java     |  75 +++
 .../fsshttpb/streamobj/basic/PropertyID.java       |  65 +++
 .../fsshttpb/streamobj/basic/PropertyType.java     | 110 ++++
 .../fsshttpb/streamobj/basic/RequestTypes.java     |  73 +++
 .../fsshttpb/streamobj/basic/SerialNumber.java     | 112 +++++
 .../fsshttpb/streamobj/basic/ZipHeader.java        |  47 ++
 .../streamobj/chunking/AbstractChunking.java}      |  42 +-
 .../streamobj/chunking/ChunkingFactory.java        | 120 +++++
 .../streamobj/chunking/ChunkingMethod.java}        |  25 +-
 .../streamobj/chunking/RDCAnalysisChunking.java    | 250 +++++++++
 .../streamobj/chunking/SimpleChunking.java         |  89 ++++
 .../streamobj/chunking/ZipFilesChunking.java       | 222 ++++++++
 .../streamobj/space/ObjectSpaceObjectPropSet.java  |  87 ++++
 .../space/ObjectSpaceObjectStreamHeader.java       |  63 +++
 .../space/ObjectSpaceObjectStreamOfContextIDs.java |  72 +++
 .../space/ObjectSpaceObjectStreamOfOIDs.java       |  73 +++
 .../space/ObjectSpaceObjectStreamOfOSIDs.java      |  72 +++
 .../microsoft/onenote/fsshttpb/unsigned/UByte.java | 312 ++++++++++++
 .../onenote/fsshttpb/unsigned/UInteger.java        | 381 ++++++++++++++
 .../microsoft/onenote/fsshttpb/unsigned/ULong.java | 289 +++++++++++
 .../microsoft/onenote/fsshttpb/unsigned/UMath.java | 113 +++++
 .../unsigned/UNumber.java}                         |  30 +-
 .../onenote/fsshttpb/unsigned/UShort.java          | 191 +++++++
 .../onenote/fsshttpb/unsigned/Unsigned.java        | 193 +++++++
 .../microsoft/onenote/fsshttpb/util/Bit.java       |  54 ++
 .../onenote/fsshttpb/util/BitConverter.java        | 137 +++++
 .../microsoft/onenote/fsshttpb/util/BitReader.java | 197 ++++++++
 .../microsoft/onenote/fsshttpb/util/BitWriter.java | 125 +++++
 .../util/ByteUtil.java}                            |  36 +-
 .../onenote/fsshttpb/util/DataElementUtils.java    | 557 +++++++++++++++++++++
 .../util/GuidUtil.java}                            |  16 +-
 .../fsshttpb/util/LittleEndianBitConverter.java    | 177 +++++++
 .../fsshttpb/util/SequenceNumberGenerator.java     |  93 ++++
 .../util/UuidUtils.java}                           |  23 +-
 .../microsoft/onenote/OneNoteParserTest.java       | 167 +++---
 ...OrEarlier1.one => testOneNote2007OrEarlier.one} | Bin
 .../test-documents/testOneNote2007OrEarlier2.one   | Bin 36786 -> 0 bytes
 .../test-documents/testOneNoteFromOffice365-2.one  | Bin 0 -> 69986 bytes
 .../test-documents/testOneNoteFromOffice365.one    | Bin 0 -> 29387 bytes
 .../test-documents/testOneNoteNonAscii.one         | Bin 13528 -> 0 bytes
 152 files changed, 13177 insertions(+), 1144 deletions(-)

diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/CompactID.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/CompactID.java
index bc7378b..df61e59 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/CompactID.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/CompactID.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 class CompactID {
@@ -49,12 +50,7 @@ class CompactID {
     }
 
     public String getCompactIDString() {
-        return new StringBuilder()
-          .append(guid)
-          .append(", index=")
-          .append(guidIndex)
-          .append(", n=")
-          .append((int) n)
-          .toString();
+        return new StringBuilder().append(guid).append(", index=").append(guidIndex).append(", n=")
+                .append((int) n).toString();
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Error.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Error.java
index 1239231..8277ebf 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Error.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Error.java
@@ -14,16 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 public enum Error {
-    OK,
-    SEGV,
-    RESERVED_NONZERO,
-    UNKNOWN_ENUM,
-    INVALID_CONSTANT,
-    STRING_TOO_SHORT,
-    HEX_OUT_OF_RANGE,
-    COMPACT_ID_MISSING,
-    UNKNOWN_GUID,
+    OK, SEGV, RESERVED_NONZERO, UNKNOWN_ENUM, INVALID_CONSTANT, STRING_TOO_SHORT, HEX_OUT_OF_RANGE,
+    COMPACT_ID_MISSING, UNKNOWN_GUID,
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ExtendedGUID.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ExtendedGUID.java
index 2b46de2..812250c 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ExtendedGUID.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ExtendedGUID.java
@@ -14,24 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 
-class ExtendedGUID implements Comparable<ExtendedGUID> {
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitConverter;
+
+public class ExtendedGUID implements Comparable<ExtendedGUID> {
     GUID guid;
     long n;
 
+    public ExtendedGUID() {
+
+    }
+
     public ExtendedGUID(GUID guid, long n) {
         this.guid = guid;
         this.n = n;
     }
 
+    public static ExtendedGUID nil() {
+        return new ExtendedGUID(GUID.nil(), 0);
+    }
+
     @Override
     public int compareTo(ExtendedGUID other) {
         if (other.guid.equals(guid)) {
-            new Long(n).compareTo(other.n);
+            return Long.compare(n, other.n);
         }
         return guid.compareTo(other.guid);
     }
@@ -45,8 +58,7 @@ class ExtendedGUID implements Comparable<ExtendedGUID> {
             return false;
         }
         ExtendedGUID that = (ExtendedGUID) o;
-        return n == that.n &&
-          Objects.equals(guid, that.guid);
+        return n == that.n && Objects.equals(guid, that.guid);
     }
 
     @Override
@@ -54,10 +66,6 @@ class ExtendedGUID implements Comparable<ExtendedGUID> {
         return Objects.hash(guid, n);
     }
 
-    public static ExtendedGUID nil() {
-        return new ExtendedGUID(GUID.nil(), 0);
-    }
-
     @Override
     public String toString() {
         return String.format(Locale.US, "%s [%d]", guid, n);
@@ -84,4 +92,17 @@ class ExtendedGUID implements Comparable<ExtendedGUID> {
         this.n = n;
         return this;
     }
+
+    /**
+     * This method is used to convert the element of ExtendedGUID object into a byte List.
+     *
+     * @return Return the byte list which store the byte information of ExtendedGUID
+     */
+    public List<Byte> SerializeToByteList() {
+        List<Byte> byteList = new ArrayList<>(guid.toByteArray());
+        for (byte b : BitConverter.getBytes(n)) {
+            byteList.add(b);
+        }
+        return byteList;
+    }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileChunkReference.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileChunkReference.java
index 04d1cb1..046106d 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileChunkReference.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileChunkReference.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 import java.util.Objects;
@@ -23,17 +24,21 @@ import java.util.Objects;
  * <p>
  * Each file chunk reference contains an <pre>stp</pre> field and a <pre>cb</pre> field.
  * <p>
- * The <pre>stp</pre> field is a stream pointer that specifies the offset, in bytes, from the beginning of the file where the referenced
+ * The <pre>stp</pre> field is a stream pointer that specifies the offset, in bytes, from the
+ * beginning of the file where the referenced
  * data is located.
  * <p>
- * The <pre>cb</pre> field specifies the size, in bytes, of the referenced data. The sizes, in bytes, of the
+ * The <pre>cb</pre> field specifies the size, in bytes, of the referenced data. The sizes, in
+ * bytes, of the
  * stp and cb fields are specified by the structures in this section.
  * <p>
  * There are some Special values:
  * <p>
- * fcrNil - Specifies a file chunk reference where all bits of the stp field are set to 1, and all bits of the cb field are set to zero.
+ * fcrNil - Specifies a file chunk reference where all bits of the stp field are set to 1, and
+ * all bits of the cb field are set to zero.
  * <p>
- * fcrZero - Specifies a file chunk reference where all bits of the stp and cb fields are set to zero.
+ * fcrZero - Specifies a file chunk reference where all bits of the stp and cb fields are set to
+ * zero.
  */
 class FileChunkReference {
 
@@ -55,10 +60,7 @@ class FileChunkReference {
 
     @Override
     public String toString() {
-        return "FileChunkReference{" +
-          "stp=" + stp +
-          ", cb=" + cb +
-          '}';
+        return "FileChunkReference{" + "stp=" + stp + ", cb=" + cb + '}';
     }
 
     @Override
@@ -70,8 +72,7 @@ class FileChunkReference {
             return false;
         }
         FileChunkReference that = (FileChunkReference) o;
-        return stp == that.stp &&
-          cb == that.cb;
+        return stp == that.stp && cb == that.cb;
     }
 
     @Override
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileDataStoreObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileDataStoreObject.java
index f48019b..ee7af09 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileDataStoreObject.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileDataStoreObject.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 class FileDataStoreObject {
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNode.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNode.java
index f27e877..5481af7 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNode.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNode.java
@@ -14,45 +14,56 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.tika.exception.TikaMemoryLimitException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+package org.apache.tika.parser.microsoft.onenote;
 
 import java.io.IOException;
 import java.util.Objects;
 
+import org.apache.tika.exception.TikaException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
  * A FileNode structure is the basic unit for holding and referencing data in the file.
  * FileNode structures are organized into file node lists
  * <p>
- * A FileNode structure is divided into header fields and a data field, fnd. The header fields specify what type of FileNode structure it
+ * A FileNode structure is divided into header fields and a data field, fnd. The header fields
+ * specify what type of FileNode structure it
  * is,
  * and what format the fnd field is in.
  * <p>
- * The fnd field can be empty, or it can contain data directly, or it can contain a reference to another block of the file by
+ * The fnd field can be empty, or it can contain data directly, or it can contain a reference to
+ * another block of the file by
  * byte position and byte count, or it can contain both data and a reference.
  */
 class FileNode {
     private static final Logger LOG = LoggerFactory.getLogger(FileNode.class);
 
     /**
-     * An unsigned integer that specifies the type of this FileNode structure. The meaning of this value is specified by the fnd field.
+     * An unsigned integer that specifies the type of this FileNode structure. The meaning of
+     * this value is specified by the fnd field.
      */
     long id;
     long size;
 
     /**
-     * An unsigned integer that specifies whether the structure specified by fnd contains a FileNodeChunkReference structure.
-     * 0 - This FileNode structure does not reference other data. The data structure specified by fnd MUST NOT contain a
+     * An unsigned integer that specifies whether the structure specified by fnd contains a
+     * FileNodeChunkReference structure.
+     * 0 - This FileNode structure does not reference other data. The data structure specified
+     * by fnd MUST NOT contain a
      * FileNodeChunkReference structure. The StpFormat and CbFormat fields MUST be ignored.
-     * 1 - This FileNode structure contains a reference to data. The first field in the data structure specified by an fnd field MUST be a
-     * FileNodeChunkReference structure that specifies the location and size of the referenced data.
-     * The type of the FileNodeChunkReference structure is specified by the StpFormat and CbFormat fields.
+     * 1 - This FileNode structure contains a reference to data. The first field in the data
+     * structure specified by an fnd field MUST be a
+     * FileNodeChunkReference structure that specifies the location and size of the referenced
+     * data.
+     * The type of the FileNodeChunkReference structure is specified by the StpFormat and
+     * CbFormat fields.
      * 2 - This FileNode structure contains a reference to a file node list.
-     * The first field in the data structure specified by the fnd field MUST be a FileNodeChunkReference structure that specifies the
-     * location and size of a file node list. The type of the FileNodeChunkReference is specified by the StpFormat and CbFormat fields.
+     * The first field in the data structure specified by the fnd field MUST be a
+     * FileNodeChunkReference structure that specifies the
+     * location and size of a file node list. The type of the FileNodeChunkReference is
+     * specified by the StpFormat and CbFormat fields.
      */
     long baseType;
 
@@ -96,28 +107,25 @@ class FileNode {
             return false;
         }
         FileNode fileNode = (FileNode) o;
-        return id == fileNode.id &&
-          size == fileNode.size &&
-          baseType == fileNode.baseType &&
-          isFileData == fileNode.isFileData &&
-          Objects.equals(gosid, fileNode.gosid) &&
-          Objects.equals(gctxid, fileNode.gctxid) &&
-          Objects.equals(fileDataStoreReference, fileNode.fileDataStoreReference) &&
-          Objects.equals(ref, fileNode.ref) &&
-          Objects.equals(propertySet, fileNode.propertySet) &&
-          Objects.equals(childFileNodeList, fileNode.childFileNodeList) &&
-          Objects.equals(subType, fileNode.subType);
+        return id == fileNode.id && size == fileNode.size && baseType == fileNode.baseType &&
+                isFileData == fileNode.isFileData && Objects.equals(gosid, fileNode.gosid) &&
+                Objects.equals(gctxid, fileNode.gctxid) &&
+                Objects.equals(fileDataStoreReference, fileNode.fileDataStoreReference) &&
+                Objects.equals(ref, fileNode.ref) &&
+                Objects.equals(propertySet, fileNode.propertySet) &&
+                Objects.equals(childFileNodeList, fileNode.childFileNodeList) &&
+                Objects.equals(subType, fileNode.subType);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, size, baseType, gosid, gctxid, fileDataStoreReference, ref, propertySet, isFileData, childFileNodeList,
-          subType);
+        return Objects.hash(id, size, baseType, gosid, gctxid, fileDataStoreReference, ref,
+                propertySet, isFileData, childFileNodeList, subType);
     }
 
     public boolean hasGctxid() {
-        return id == FndStructureConstants.RevisionRoleAndContextDeclarationFND
-          || id == FndStructureConstants.RevisionManifestStart7FND;
+        return id == FndStructureConstants.RevisionRoleAndContextDeclarationFND ||
+                id == FndStructureConstants.RevisionManifestStart7FND;
     }
 
     public long getId() {
@@ -219,10 +227,12 @@ class FileNode {
         return this;
     }
 
-    public void print(OneNoteDocument document, OneNotePtr pointer, int indentLevel) throws IOException, TikaMemoryLimitException {
+    public void print(OneNoteDocument document, OneNotePtr pointer, int indentLevel)
+            throws IOException, TikaException {
         boolean shouldPrintHeader = FndStructureConstants.nameOf(id).contains("ObjectDec");
         if (gosid.equals(ExtendedGUID.nil()) && shouldPrintHeader) {
-            LOG.debug("{}[beg {}]:{}", IndentUtil.getIndent(indentLevel + 1), FndStructureConstants.nameOf(id), gosid);
+            LOG.debug("{}[beg {}]:{}", IndentUtil.getIndent(indentLevel + 1),
+                    FndStructureConstants.nameOf(id), gosid);
         }
         propertySet.print(document, pointer, indentLevel + 1);
         if (!childFileNodeList.children.isEmpty()) {
@@ -233,25 +243,26 @@ class FileNode {
                 child.print(document, pointer, indentLevel + 1);
             }
         }
-        if (id == FndStructureConstants.RevisionRoleDeclarationFND
-          || id == FndStructureConstants.RevisionRoleAndContextDeclarationFND) {
+        if (id == FndStructureConstants.RevisionRoleDeclarationFND ||
+                id == FndStructureConstants.RevisionRoleAndContextDeclarationFND) {
             LOG.debug("{}[Revision Role {}]", IndentUtil.getIndent(indentLevel + 1),
-              subType.revisionRoleDeclaration.revisionRole);
+                    subType.revisionRoleDeclaration.revisionRole);
 
         }
-        if (id == FndStructureConstants.RevisionManifestStart4FND || id == FndStructureConstants.RevisionManifestStart6FND
-          || id == FndStructureConstants.RevisionManifestStart7FND) {
+        if (id == FndStructureConstants.RevisionManifestStart4FND ||
+                id == FndStructureConstants.RevisionManifestStart6FND ||
+                id == FndStructureConstants.RevisionManifestStart7FND) {
             LOG.debug("{}[revisionRole {}]", IndentUtil.getIndent(indentLevel + 1),
-              subType.revisionManifest.revisionRole);
+                    subType.revisionManifest.revisionRole);
 
         }
-        if ((gctxid != ExtendedGUID.nil() || id == FndStructureConstants.RevisionManifestStart7FND)
-          && shouldPrintHeader) {
+        if ((!gctxid.equals(ExtendedGUID.nil()) ||
+                id == FndStructureConstants.RevisionManifestStart7FND) && shouldPrintHeader) {
             LOG.debug("{}[gctxid {}]", IndentUtil.getIndent(indentLevel + 1), gctxid);
         }
-        if (gosid != ExtendedGUID.nil() && shouldPrintHeader) {
-            LOG.debug("{}[end {}]:{}", IndentUtil.getIndent(indentLevel + 1), FndStructureConstants.nameOf(id),
-              gosid);
+        if (!gosid.equals(ExtendedGUID.nil()) && shouldPrintHeader) {
+            LOG.debug("{}[end {}]:{}", IndentUtil.getIndent(indentLevel + 1),
+                    FndStructureConstants.nameOf(id), gosid);
 
         }
     }
@@ -267,12 +278,8 @@ class FileNode {
 
     @Override
     public String toString() {
-        return new StringBuilder().append("FileNodeID=0x")
-          .append(Long.toHexString(id))
-          .append(", gosid=")
-          .append(gosid)
-          .append(", baseType=0x")
-          .append(Long.toHexString(baseType))
-          .toString();
+        return new StringBuilder().append("FileNodeID=0x").append(Long.toHexString(id))
+                .append(", gosid=").append(gosid).append(", baseType=0x")
+                .append(Long.toHexString(baseType)).toString();
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
index aa01c18..1d678a0 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 import java.util.ArrayList;
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeListHeader.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeListHeader.java
index 1f8ee22..a0bd3c7 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeListHeader.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeListHeader.java
@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.tika.exception.TikaException;
 
 class FileNodeListHeader {
     public static final long UNIT_MAGIC_CONSTANT = 0xA4567AB1F5F7F4C4L;
@@ -29,23 +31,28 @@ class FileNodeListHeader {
      *
      * @param position          Position of the file where this header starts.
      * @param uintMagic         An unsigned integer; MUST be "0xA4567AB1F5F7F4C4"
-     * @param fileNodeListId    An unsigned integer that specifies the identity of the file node list
-     *                          this fragment belongs to. MUST be equal to or greater than 0x00000010. The pair of
-     *                          FileNodeListID and nFragmentSequence fields MUST be unique relative to other
-     *                          FileNodeListFragment structures in the file.
+     * @param fileNodeListId    An unsigned integer that specifies the identity of
+     *                          the file node list this fragment belongs to. MUST be equal to or
+     *                          greater than 0x00000010. The pair of
+     *                          FileNodeListID and nFragmentSequence fields MUST be unique
+     *                          relative to other FileNodeListFragment structures in the file.
      * @param nFragmentSequence An unsigned integer that specifies the index of the fragment in the
-     *                          file node list containing the fragment. The nFragmentSequence field of the first fragment in a
-     *                          given file node list MUST be 0 and the nFragmentSequence fields of all subsequent fragments in
+     *                          file node list containing the fragment. The nFragmentSequence
+     *                          field of the first fragment in a given file node list MUST be 0
+     *                          and the nFragmentSequence fields of all subsequent fragments in
      *                          this list MUST be sequential.
      */
-    public FileNodeListHeader(long position, long uintMagic, long fileNodeListId, long nFragmentSequence) {
+    public FileNodeListHeader(long position, long uintMagic, long fileNodeListId,
+                              long nFragmentSequence) throws TikaException {
         if (uintMagic != UNIT_MAGIC_CONSTANT) {
-            throw new RuntimeException("unitMagic must always be: 0x" + Long.toHexString(UNIT_MAGIC_CONSTANT));
+            throw new TikaException(
+                    "unitMagic must always be: 0x" + Long.toHexString(UNIT_MAGIC_CONSTANT));
         }
         this.position = position;
         this.fileNodeListId = fileNodeListId;
         if (fileNodeListId < 0x00000010) {
-            throw new RuntimeException("FileNodeListHeader.fileNodeListId MUST be equal to or greater than 0x00000010");
+            throw new TikaException("FileNodeListHeader.fileNodeListId MUST be equal " +
+                    "to or greater than 0x00000010");
         }
         this.nFragmentSequence = nFragmentSequence;
     }
@@ -78,15 +85,13 @@ class FileNodeListHeader {
     }
 
     public String getPositionHex() {
-        return "0x" + StringUtils.leftPad(Long.toHexString(position), 8, "0");
+        return "0x" + StringUtils.leftPad(Long.toHexString(position), 8, '0');
     }
 
     @Override
     public String toString() {
-        return "FileNodeListHeader{" +
-                "position=" + "0x" + StringUtils.leftPad(Long.toHexString(position), 8, "0") +
-                ", fileNodeListId=" + fileNodeListId +
-                ", nFragmentSequence=" + nFragmentSequence +
-                '}';
+        return "FileNodeListHeader{" + "position=" + "0x" +
+                StringUtils.leftPad(Long.toHexString(position), 8, '0') + ", fileNodeListId=" +
+                fileNodeListId + ", nFragmentSequence=" + nFragmentSequence + '}';
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtr.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtr.java
index a0e9e25..27cd96c 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtr.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtr.java
@@ -14,11 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.tika.exception.TikaException;
+
 /**
  * Stores a list that represents how to get to the file node in the data structure.
  */
@@ -44,17 +47,18 @@ class FileNodePtr {
      * <p>
      * For example 0, 4, 15 would mean
      * <p>
-     * document.root.children.get(0).childFileNodeList.children.get(4).childFileNodeList.children.get(15)
+     * document.root.children.get(0).childFileNodeList.children.get(4).
+     * childFileNodeList.children.get(15)
      *
      * @param document
      * @return
      */
-    public FileNode dereference(OneNoteDocument document) {
+    public FileNode dereference(OneNoteDocument document) throws TikaException {
         if (nodeListPositions.isEmpty()) {
             return null;
         }
         if (nodeListPositions.get(0) >= document.root.children.size()) {
-            throw new RuntimeException("Exceeded root child size");
+            throw new TikaException("Exceeded root child size");
         }
         FileNode cur = document.root.children.get(nodeListPositions.get(0));
         for (int i = 1, ie = nodeListPositions.size(); i < ie; ++i) {
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
index b79ef8a..393de25 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 class FileNodePtrBackPush {
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeUnion.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeUnion.java
index 169c394..7092b61 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeUnion.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeUnion.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 class FileNodeUnion {
@@ -24,9 +25,12 @@ class FileNodeUnion {
     GlobalIdTableEntryFNDX globalIdTableEntryFNDX = new GlobalIdTableEntryFNDX();
     GlobalIdTableEntry2FNDX globalIdTableEntry2FNDX = new GlobalIdTableEntry2FNDX();
     GlobalIdTableEntry3FNDX globalIdTableEntry3FNDX = new GlobalIdTableEntry3FNDX();
-    ObjectRevisionWithRefCountFNDX objectRevisionWithRefCountFNDX = new ObjectRevisionWithRefCountFNDX();
-    ObjectInfoDependencyOverrides objectInfoDependencyOverrides = new ObjectInfoDependencyOverrides();
-    ObjectDeclarationWithRefCount objectDeclarationWithRefCount = new ObjectDeclarationWithRefCount();
+    ObjectRevisionWithRefCountFNDX objectRevisionWithRefCountFNDX =
+            new ObjectRevisionWithRefCountFNDX();
+    ObjectInfoDependencyOverrides objectInfoDependencyOverrides =
+            new ObjectInfoDependencyOverrides();
+    ObjectDeclarationWithRefCount objectDeclarationWithRefCount =
+            new ObjectDeclarationWithRefCount();
     RootObjectReference rootObjectReference = new RootObjectReference();
     FileDataStoreObjectReference fileDataStoreObjectReference = new FileDataStoreObjectReference();
 
@@ -34,7 +38,8 @@ class FileNodeUnion {
         return revisionManifestListStart;
     }
 
-    public FileNodeUnion setRevisionManifestListStart(RevisionManifestListStart revisionManifestListStart) {
+    public FileNodeUnion setRevisionManifestListStart(
+            RevisionManifestListStart revisionManifestListStart) {
         this.revisionManifestListStart = revisionManifestListStart;
         return this;
     }
@@ -52,7 +57,8 @@ class FileNodeUnion {
         return revisionRoleDeclaration;
     }
 
-    public FileNodeUnion setRevisionRoleDeclaration(RevisionRoleDeclaration revisionRoleDeclaration) {
+    public FileNodeUnion setRevisionRoleDeclaration(
+            RevisionRoleDeclaration revisionRoleDeclaration) {
         this.revisionRoleDeclaration = revisionRoleDeclaration;
         return this;
     }
@@ -79,7 +85,8 @@ class FileNodeUnion {
         return globalIdTableEntry2FNDX;
     }
 
-    public FileNodeUnion setGlobalIdTableEntry2FNDX(GlobalIdTableEntry2FNDX globalIdTableEntry2FNDX) {
+    public FileNodeUnion setGlobalIdTableEntry2FNDX(
+            GlobalIdTableEntry2FNDX globalIdTableEntry2FNDX) {
         this.globalIdTableEntry2FNDX = globalIdTableEntry2FNDX;
         return this;
     }
@@ -88,7 +95,8 @@ class FileNodeUnion {
         return globalIdTableEntry3FNDX;
     }
 
-    public FileNodeUnion setGlobalIdTableEntry3FNDX(GlobalIdTableEntry3FNDX globalIdTableEntry3FNDX) {
+    public FileNodeUnion setGlobalIdTableEntry3FNDX(
+            GlobalIdTableEntry3FNDX globalIdTableEntry3FNDX) {
         this.globalIdTableEntry3FNDX = globalIdTableEntry3FNDX;
         return this;
     }
@@ -97,7 +105,8 @@ class FileNodeUnion {
         return objectRevisionWithRefCountFNDX;
     }
 
-    public FileNodeUnion setObjectRevisionWithRefCountFNDX(ObjectRevisionWithRefCountFNDX objectRevisionWithRefCountFNDX) {
+    public FileNodeUnion setObjectRevisionWithRefCountFNDX(
+            ObjectRevisionWithRefCountFNDX objectRevisionWithRefCountFNDX) {
         this.objectRevisionWithRefCountFNDX = objectRevisionWithRefCountFNDX;
         return this;
     }
@@ -106,7 +115,8 @@ class FileNodeUnion {
         return objectInfoDependencyOverrides;
     }
 
-    public FileNodeUnion setObjectInfoDependencyOverrides(ObjectInfoDependencyOverrides objectInfoDependencyOverrides) {
+    public FileNodeUnion setObjectInfoDependencyOverrides(
+            ObjectInfoDependencyOverrides objectInfoDependencyOverrides) {
         this.objectInfoDependencyOverrides = objectInfoDependencyOverrides;
         return this;
     }
@@ -115,7 +125,8 @@ class FileNodeUnion {
         return objectDeclarationWithRefCount;
     }
 
-    public FileNodeUnion setObjectDeclarationWithRefCount(ObjectDeclarationWithRefCount objectDeclarationWithRefCount) {
+    public FileNodeUnion setObjectDeclarationWithRefCount(
+            ObjectDeclarationWithRefCount objectDeclarationWithRefCount) {
         this.objectDeclarationWithRefCount = objectDeclarationWithRefCount;
         return this;
     }
@@ -133,7 +144,8 @@ class FileNodeUnion {
         return fileDataStoreObjectReference;
     }
 
-    public FileNodeUnion setFileDataStoreObjectReference(FileDataStoreObjectReference fileDataStoreObjectReference) {
+    public FileNodeUnion setFileDataStoreObjectReference(
+            FileDataStoreObjectReference fileDataStoreObjectReference) {
         this.fileDataStoreObjectReference = fileDataStoreObjectReference;
         return this;
     }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FndStructureConstants.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FndStructureConstants.java
index 88f543e..74fb6cb 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FndStructureConstants.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FndStructureConstants.java
@@ -25,10 +25,6 @@ package org.apache.tika.parser.microsoft.onenote;
  * The value of each constant corresponds to the FileNodeID property for the file node.
  */
 final class FndStructureConstants {
-    private FndStructureConstants() {
-        // no op
-    }
-
     static final long ObjectSpaceManifestRootFND = 0x04;
     static final long ObjectSpaceManifestListReferenceFND = 0x08;
     static final long ObjectSpaceManifestListStartFND = 0x0c;
@@ -44,24 +40,6 @@ final class FndStructureConstants {
     static final long GlobalIdTableEntry2FNDX = 0x25;
     static final long GlobalIdTableEntry3FNDX = 0x26;
     static final long GlobalIdTableEndFNDX = 0x28;
-
-    public static final class CanRevise {
-        private CanRevise() {
-            // no op
-        }
-
-        static final long ObjectDeclarationWithRefCountFNDX = 0x2d;
-        static final long ObjectDeclarationWithRefCount2FNDX = 0x2e;
-        static final long ObjectRevisionWithRefCountFNDX = 0x041;
-        static final long ObjectRevisionWithRefCount2FNDX = 0x42;
-        static final long ObjectDeclaration2RefCountFND = 0x0A4;
-        static final long ObjectDeclaration2LargeRefCountFND = 0xA5;
-        static final long ReadOnlyObjectDeclaration2RefCountFND = 0xc4;
-        static final long ReadOnlyObjectDeclaration2LargeRefCountFND = 0xc5;
-        static final long ObjectDeclarationFileData3RefCountFND = 0x72;
-        static final long ObjectDeclarationFileData3LargeRefCountFND = 0x73;
-    }
-
     static final long RootObjectReference2FNDX = 0x59;
     static final long RootObjectReference3FND = 0x5a; // each root object must have a differe
     static final long RevisionRoleDeclarationFND = 0x5c;
@@ -75,9 +53,12 @@ final class FndStructureConstants {
     static final long ObjectGroupStartFND = 0xb4;
     static final long ObjectGroupEndFND = 0xb8;
     static final long HashedChunkDescriptor2FND = 0xc2;
-
     static final long ChunkTerminatorFND = 0xff;
 
+    private FndStructureConstants() {
+        // no op
+    }
+
     static String nameOf(long type) {
         switch (new Long(type).intValue()) {
             case (int) ObjectSpaceManifestRootFND:
@@ -163,4 +144,21 @@ final class FndStructureConstants {
                 return "UnknownFND";
         }
     }
+
+    public static final class CanRevise {
+        static final long ObjectDeclarationWithRefCountFNDX = 0x2d;
+        static final long ObjectDeclarationWithRefCount2FNDX = 0x2e;
+        static final long ObjectRevisionWithRefCountFNDX = 0x041;
+        static final long ObjectRevisionWithRefCount2FNDX = 0x42;
+        static final long ObjectDeclaration2RefCountFND = 0x0A4;
+        static final long ObjectDeclaration2LargeRefCountFND = 0xA5;
+        static final long ReadOnlyObjectDeclaration2RefCountFND = 0xc4;
+        static final long ReadOnlyObjectDeclaration2LargeRefCountFND = 0xc5;
+        static final long ObjectDeclarationFileData3RefCountFND = 0x72;
+        static final long ObjectDeclarationFileData3LargeRefCountFND = 0x73;
+
+        private CanRevise() {
+            // no op
+        }
+    }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/GUID.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/GUID.java
index 371e328..27f016b 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/GUID.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/GUID.java
@@ -14,42 +14,71 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.commons.lang3.StringUtils;
 
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
-class GUID implements Comparable<GUID> {
+import org.apache.commons.lang3.StringUtils;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitConverter;
+
+public class GUID implements Comparable<GUID> {
     int[] guid;
 
+    public GUID(int[] guid) {
+        this.guid = guid;
+    }
+
     /**
-     * Converts a GUID of format: {AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE} (in bytes) to a GUID object.
+     * Converts a GUID of format: {AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE} (in bytes) to a GUID
+     * object.
      *
-     * @param guid The bytes that contain string in UTF-16 format of {AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}
+     * @param guid The bytes that contains string in UTF-16 format of {AAAAAAAA-BBBB-CCCC-DDDD
+     *             -EEEEEEEEEEEE}
      * @return GUID object parsed from guid bytes.
      */
     public static GUID fromCurlyBraceUTF16Bytes(byte[] guid) {
         int[] intGuid = new int[16];
         String utf16Str = new String(guid, StandardCharsets.UTF_16LE).replaceAll("\\{", "")
-          .replaceAll("-", "").replaceAll("}", "");
+                .replaceAll("-", "").replaceAll("}", "");
         for (int i = 0; i < utf16Str.length(); i += 2) {
-            intGuid[i / 2] = Integer.parseUnsignedInt("" + utf16Str.charAt(i) + utf16Str.charAt(i + 1), 16);
+            intGuid[i / 2] =
+                    Integer.parseUnsignedInt("" + utf16Str.charAt(i) + utf16Str.charAt(i + 1), 16);
         }
         return new GUID(intGuid);
     }
 
+    public static int memcmp(int[] b1, int[] b2, int sz) {
+        for (int i = 0; i < sz; i++) {
+            if (b1[i] != b2[i]) {
+                if ((b1[i] >= 0 && b2[i] >= 0) || (b1[i] < 0 && b2[i] < 0)) {
+                    return b1[i] - b2[i];
+                }
+                if (b1[i] < 0 && b2[i] >= 0) {
+                    return 1;
+                }
+                if (b2[i] < 0 && b1[i] >= 0) {
+                    return -1;
+                }
+            }
+        }
+        return 0;
+    }
+
+    public static GUID nil() {
+        return new GUID(new int[16]);
+    }
+
     @Override
     public int compareTo(GUID o) {
         return memcmp(guid, o.guid, 16);
     }
 
-    public GUID(int[] guid) {
-        this.guid = guid;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -67,23 +96,6 @@ class GUID implements Comparable<GUID> {
         return Arrays.hashCode(guid);
     }
 
-    public static int memcmp(int b1[], int b2[], int sz) {
-        for (int i = 0; i < sz; i++) {
-            if (b1[i] != b2[i]) {
-                if ((b1[i] >= 0 && b2[i] >= 0) || (b1[i] < 0 && b2[i] < 0)) {
-                    return b1[i] - b2[i];
-                }
-                if (b1[i] < 0 && b2[i] >= 0) {
-                    return 1;
-                }
-                if (b2[i] < 0 && b1[i] >= 0) {
-                    return -1;
-                }
-            }
-        }
-        return 0;
-    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -111,10 +123,6 @@ class GUID implements Comparable<GUID> {
         return sb.toString().toUpperCase(Locale.US);
     }
 
-    public static GUID nil() {
-        return new GUID(new int[16]);
-    }
-
     public int[] getGuid() {
         return guid;
     }
@@ -127,4 +135,14 @@ class GUID implements Comparable<GUID> {
     public String getGuidString() {
         return guid.toString();
     }
+
+    public List<Byte> toByteArray() {
+        List<Byte> byteList = new ArrayList<>();
+        for (int nextInt : guid) {
+            for (byte b : BitConverter.getBytes(nextInt)) {
+                byteList.add(b);
+            }
+        }
+        return byteList;
+    }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/IndentUtil.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/IndentUtil.java
index 50c381f..bb6d516 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/IndentUtil.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/IndentUtil.java
@@ -14,14 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 class IndentUtil {
     public static String getIndent(int indentLevel) {
-        String retval = "";
+        StringBuilder retval = new StringBuilder();
         for (int i = 0; i < indentLevel; ++i) {
-            retval += "  ";
+            retval.append("  ");
         }
-        return retval;
+        return retval.toString();
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Int24.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Int24.java
index 8fd7133..034948a 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Int24.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Int24.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 class Int24 {
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCID.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCID.java
index 745ff55..26daccb 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCID.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCID.java
@@ -14,11 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
+import org.apache.tika.exception.TikaException;
+
 /**
- * The JCID structure specifies the type of object and the type of data the object contains. A JCID structure can be
- * considered to be an unsigned integer of size four bytes as specified by property set and
+ * The JCID structure specifies the type of object and the type of data the object contains.
+ * A JCID structure can be considered to be an unsigned integer of size four bytes as specified
+ * by property set and
  * file data object.
  *
  * <pre>[0,15] - the index</pre>
@@ -31,17 +35,19 @@ package org.apache.tika.parser.microsoft.onenote;
  * <p>
  * index (2 bytes): An unsigned integer that specifies the type of object.
  * <p>
- * A - IsBinary (1 bit): Specifies whether the object contains encryption data transmitted over the File Synchronization via SOAP over
- * HTTP Protocol, as specified in [MS-FSSHTTP].
+ * A - IsBinary (1 bit): Specifies whether the object contains encryption data  transmitted over
+ * the File Synchronization via SOAP over HTTP Protocol, as specified in [MS-FSSHTTP].
  * <p>
  * B - IsPropertySet (1 bit): Specifies whether the object contains a property set.
  * <p>
  * C - IsGraphNode (1 bit): Undefined and MUST be ignored.
  * <p>
- * D - IsFileData (1 bit): Specifies whether the object is a file data object. If the value of IsFileData is "true", then the values of
- * the IsBinary, IsPropertySet, IsGraphNode, and IsReadOnly fields MUST all be false.
+ * D - IsFileData (1 bit): Specifies whether the object is a file data object. If the value of
+ * IsFileData is "true", then the values of the IsBinary, IsPropertySet, IsGraphNode, and
+ * IsReadOnly fields MUST all be false.
  * <p>
- * E - IsReadOnly (1 bit): Specifies whether the object's data MUST NOT be changed when the object is revised.
+ * E - IsReadOnly (1 bit): Specifies whether the object's data MUST NOT be changed when the
+ * object is revised.
  * <p>
  * reserved (11 bits): MUST be zero, and MUST be ignored.
  */
@@ -55,16 +61,18 @@ class JCID {
     boolean isReadOnly;
 
     /**
-     * If the value of the JCID.IsPropertySet field is "true" or if only JCID.index is specified, then the data
-     * for the Object Space Object structure MUST be an ObjectSpaceObjectPropSet structure.
+     * If the value of the JCID.IsPropertySet field is "true" or if only JCID.index is specified,
+     * then the data for the Object Space Object structure MUST be an ObjectSpaceObjectPropSet
+     * structure.
      *
      * @return true if is ObjectSpaceObjectPropSet. false otherwise.
      */
     public boolean isObjectSpaceObjectPropSet() {
-        return isPropertySet || !isBinary && !isGraphNode && !isFileData && !isReadOnly && index > 0;
+        return isPropertySet ||
+                !isBinary && !isGraphNode && !isFileData && !isReadOnly && index > 0;
     }
 
-    public void loadFrom32BitIndex(long fullIndex) {
+    public void loadFrom32BitIndex(long fullIndex) throws TikaException {
         jcid = fullIndex;
         index = fullIndex & 0xffff;
         isBinary = ((fullIndex >> 16) & 1) == 1;
@@ -73,21 +81,16 @@ class JCID {
         isFileData = ((fullIndex >> 19) & 1) == 1;
         isReadOnly = ((fullIndex >> 20) & 1) == 1;
         if ((fullIndex >> 21) != 0) {
-            throw new RuntimeException("RESERVED_NONZERO");
+            throw new TikaException("RESERVED_NONZERO");
         }
     }
 
     @Override
     public String toString() {
-        return "JCID{" +
-            "jcid=" + JCIDPropertySetTypeEnum.of(jcid) + " (0x" + Long.toHexString(jcid) + ")" +
-            ", index=" + index +
-            ", isBinary=" + isBinary +
-            ", isPropertySet=" + isPropertySet +
-            ", isGraphNode=" + isGraphNode +
-            ", isFileData=" + isFileData +
-            ", isReadOnly=" + isReadOnly +
-            '}';
+        return "JCID{" + "jcid=" + JCIDPropertySetTypeEnum.of(jcid) + " (0x" +
+                Long.toHexString(jcid) + ")" + ", index=" + index + ", isBinary=" + isBinary +
+                ", isPropertySet=" + isPropertySet + ", isGraphNode=" + isGraphNode +
+                ", isFileData=" + isFileData + ", isReadOnly=" + isReadOnly + '}';
     }
 
     public long getJcid() {
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCIDPropertySetTypeEnum.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCIDPropertySetTypeEnum.java
index 4b30da0..6617bad 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCIDPropertySetTypeEnum.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/JCIDPropertySetTypeEnum.java
@@ -25,55 +25,39 @@ import java.util.Map;
  * specification.
  */
 enum JCIDPropertySetTypeEnum {
-  jcidReadOnlyPersistablePropertyContainerForAuthor(0x00120001),
-  jcidPersistablePropertyContainerForTOC(0x00020001),
-  jcidPersistablePropertyContainerForTOCSection(0x00020001),
-  jcidSectionNode(0x00060007),
-  jcidPageSeriesNode(0x00060008),
-  jcidPageNode(0x0006000B),
-  jcidOutlineNode(0x0006000C),
-  jcidOutlineElementNode(0x0006000D),
-  jcidRichTextOENode(0x0006000E),
-  jcidImageNode(0x00060011),
-  jcidNumberListNode(0x00060012),
-  jcidOutlineGroup(0x00060019),
-  jcidTableNode(0x00060022),
-  jcidTableRowNode(0x00060023),
-  jcidTableCellNode(0x00060024),
-  jcidTitleNode(0x0006002C),
-  jcidPageMetaData(0x00020030),
-  jcidSectionMetaData(0x00020031),
-  jcidEmbeddedFileNode(0x00060035),
-  jcidPageManifestNode(0x00060037),
-  jcidConflictPageMetaData(0x00020038),
-  jcidVersionHistoryContent(0x0006003C),
-  jcidVersionProxy(0x0006003D),
-  jcidNoteTagSharedDefinitionContainer(0x00120043),
-  jcidRevisionMetaData(0x00020044),
-  jcidVersionHistoryMetaData(0x00020046),
-  jcidParagraphStyleObject(0x0012004D),
-  jcidParagraphStyleObjectForText(0x0012004D),
-  unknown(0x0);
-
-  private long jcid;
-
-  JCIDPropertySetTypeEnum(long jcid) {
-    this.jcid = jcid;
-  }
+    jcidReadOnlyPersistablePropertyContainerForAuthor(0x00120001),
+    jcidPersistablePropertyContainerForTOC(0x00020001),
+    jcidPersistablePropertyContainerForTOCSection(0x00020001), jcidSectionNode(0x00060007),
+    jcidPageSeriesNode(0x00060008), jcidPageNode(0x0006000B), jcidOutlineNode(0x0006000C),
+    jcidOutlineElementNode(0x0006000D), jcidRichTextOENode(0x0006000E), jcidImageNode(0x00060011),
+    jcidNumberListNode(0x00060012), jcidOutlineGroup(0x00060019), jcidTableNode(0x00060022),
+    jcidTableRowNode(0x00060023), jcidTableCellNode(0x00060024), jcidTitleNode(0x0006002C),
+    jcidPageMetaData(0x00020030), jcidSectionMetaData(0x00020031), jcidEmbeddedFileNode(0x00060035),
+    jcidPageManifestNode(0x00060037), jcidConflictPageMetaData(0x00020038),
+    jcidVersionHistoryContent(0x0006003C), jcidVersionProxy(0x0006003D),
+    jcidNoteTagSharedDefinitionContainer(0x00120043), jcidRevisionMetaData(0x00020044),
+    jcidVersionHistoryMetaData(0x00020046), jcidParagraphStyleObject(0x0012004D),
+    jcidParagraphStyleObjectForText(0x0012004D), unknown(0x0);
+
+    private static final Map<Long, JCIDPropertySetTypeEnum> BY_ID = new HashMap<>();
+
+    static {
+        for (JCIDPropertySetTypeEnum e : values()) {
+            BY_ID.put(e.jcid, e);
+        }
+    }
 
-  private static final Map<Long, JCIDPropertySetTypeEnum> BY_ID = new HashMap<>();
+    private final long jcid;
 
-  static {
-    for (JCIDPropertySetTypeEnum e : values()) {
-      BY_ID.put(e.jcid, e);
+    JCIDPropertySetTypeEnum(long jcid) {
+        this.jcid = jcid;
     }
-  }
 
-  public static JCIDPropertySetTypeEnum of(Long id) {
-    JCIDPropertySetTypeEnum result = BY_ID.get(id);
-    if (result == null) {
-      return unknown;
+    public static JCIDPropertySetTypeEnum of(Long id) {
+        JCIDPropertySetTypeEnum result = BY_ID.get(id);
+        if (result == null) {
+            return unknown;
+        }
+        return result;
     }
-    return result;
-  }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCount.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCount.java
index f3831e5..e6f1771 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCount.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCount.java
@@ -21,20 +21,6 @@ class ObjectDeclarationWithRefCount {
     ObjectSpaceObjectPropSet objectRef;
     ObjectDeclarationWithRefCountBody body = new ObjectDeclarationWithRefCountBody();
     long cRef;
-
-    public static class ReadOnly {
-        byte[] md5;
-
-        public byte[] getMd5() {
-            return md5;
-        }
-
-        public ReadOnly setMd5(byte[] md5) {
-            this.md5 = md5;
-            return this;
-        }
-    }
-
     ReadOnly readOnly = new ReadOnly();
 
     public ObjectSpaceObjectPropSet getObjectRef() {
@@ -72,4 +58,17 @@ class ObjectDeclarationWithRefCount {
         this.readOnly = readOnly;
         return this;
     }
+
+    public static class ReadOnly {
+        byte[] md5;
+
+        public byte[] getMd5() {
+            return md5;
+        }
+
+        public ReadOnly setMd5(byte[] md5) {
+            this.md5 = md5;
+            return this;
+        }
+    }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCountBody.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCountBody.java
index 476aeb5..7b7edf5 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCountBody.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectDeclarationWithRefCountBody.java
@@ -66,7 +66,8 @@ class ObjectDeclarationWithRefCountBody {
         return file_data_store_reference;
     }
 
-    public ObjectDeclarationWithRefCountBody setFile_data_store_reference(boolean file_data_store_reference) {
+    public ObjectDeclarationWithRefCountBody setFile_data_store_reference(
+            boolean file_data_store_reference) {
         this.file_data_store_reference = file_data_store_reference;
         return this;
     }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectPropSet.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectPropSet.java
index d555fc9..bd6f28a 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectPropSet.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectPropSet.java
@@ -16,10 +16,13 @@
  */
 package org.apache.tika.parser.microsoft.onenote;
 
-class ObjectSpaceObjectPropSet {
-    ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs oids = new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
-    ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs osids = new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
-    ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs contextIDs = new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
+public class ObjectSpaceObjectPropSet {
+    ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs oids =
+            new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
+    ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs osids =
+            new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
+    ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs contextIDs =
+            new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
     PropertySet body = new PropertySet();
 
     public ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs getOids() {
@@ -44,7 +47,8 @@ class ObjectSpaceObjectPropSet {
         return contextIDs;
     }
 
-    public ObjectSpaceObjectPropSet setContextIDs(ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs contextIDs) {
+    public ObjectSpaceObjectPropSet setContextIDs(
+            ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs contextIDs) {
         this.contextIDs = contextIDs;
         return this;
     }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs.java
index 458b69a..eb53fb7 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs.java
@@ -38,7 +38,8 @@ class ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs {
         return extendedStreamsPresent;
     }
 
-    public ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs setExtendedStreamsPresent(long extendedStreamsPresent) {
+    public ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs setExtendedStreamsPresent(
+            long extendedStreamsPresent) {
         this.extendedStreamsPresent = extendedStreamsPresent;
         return this;
     }
@@ -47,7 +48,8 @@ class ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs {
         return osidsStreamNotPresent;
     }
 
-    public ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs setOsidsStreamNotPresent(long osidsStreamNotPresent) {
+    public ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs setOsidsStreamNotPresent(
+            long osidsStreamNotPresent) {
         this.osidsStreamNotPresent = osidsStreamNotPresent;
         return this;
     }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDirectFileResource.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDirectFileResource.java
index 475b680..d1d71ea 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDirectFileResource.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDirectFileResource.java
@@ -24,16 +24,17 @@ import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 
 /**
- * This is copied mostly from the {@link org.apache.tika.parser.mp4.DirectFileReadDataSource}.
+ * This is copied mostly from the the
+ * former org.apache.tika.parser.mp4.DirectFileReadDataSource
  * <p>
- * Implements a simple way to encapsulate a {@link org.apache.tika.io.TikaInputStream} that you will have to seek,read,repeat
- * while parsing OneNote contents.
+ * Implements a simple way to encapsulate a {@link org.apache.tika.io.TikaInputStream} that you
+ * will have to seek,read,repeat while parsing OneNote contents.
  */
 class OneNoteDirectFileResource implements Closeable {
 
     private static final int TRANSFER_SIZE = 8192;
 
-    private RandomAccessFile raf;
+    private final RandomAccessFile raf;
 
     public OneNoteDirectFileResource(File f) throws IOException {
         this.raf = new RandomAccessFile(f, "r");
@@ -84,4 +85,4 @@ class OneNoteDirectFileResource implements Closeable {
         raf.close();
     }
 
-}
\ No newline at end of file
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDocument.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDocument.java
index 83402a2..a949b07 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDocument.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteDocument.java
@@ -14,15 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.commons.lang3.tuple.Pair;
+package org.apache.tika.parser.microsoft.onenote;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.lang3.tuple.Pair;
+
 class OneNoteDocument {
     OneNoteHeader header;
     List<ExtendedGUID> revisionListOrder = new ArrayList<>();
@@ -60,7 +61,8 @@ class OneNoteDocument {
         currentRevision = fn.gosid;
     }
 
-    public void registerAdditionalRevisionRole(ExtendedGUID gosid, long revisionRole, ExtendedGUID gctxid) {
+    public void registerAdditionalRevisionRole(ExtendedGUID gosid, long revisionRole,
+                                               ExtendedGUID gctxid) {
         revisionRoleMap.put(gosid, Pair.of(revisionRole, gctxid));
     }
 
@@ -86,7 +88,8 @@ class OneNoteDocument {
         return revisionManifestLists;
     }
 
-    public OneNoteDocument setRevisionManifestLists(Map<ExtendedGUID, FileNodePtr> revisionManifestLists) {
+    public OneNoteDocument setRevisionManifestLists(
+            Map<ExtendedGUID, FileNodePtr> revisionManifestLists) {
         this.revisionManifestLists = revisionManifestLists;
         return this;
     }
@@ -113,7 +116,8 @@ class OneNoteDocument {
         return revisionRoleMap;
     }
 
-    public OneNoteDocument setRevisionRoleMap(Map<ExtendedGUID, Pair<Long, ExtendedGUID>> revisionRoleMap) {
+    public OneNoteDocument setRevisionRoleMap(
+            Map<ExtendedGUID, Pair<Long, ExtendedGUID>> revisionRoleMap) {
         this.revisionRoleMap = revisionRoleMap;
         return this;
     }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteHeader.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteHeader.java
index a6dc733..d93979f 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteHeader.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteHeader.java
@@ -14,9 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.tika.exception.TikaException;
+package org.apache.tika.parser.microsoft.onenote;
 
 import java.io.Serializable;
 
@@ -60,14 +59,15 @@ class OneNoteHeader implements Serializable {
     long buildNumberLastWroteToFile;
     long buildNumberOldestWritten;
     long buildNumberNewestWritten;
+    private boolean legacyOrAlternativePackaging;
 
     /**
-     * Determine if this OneNote file pre-dates the open specs published by
-     * microsoft.
+     * Determine if this file is saved in the OnPrem OneNote 2013 or greater spec.
+     *
      * @return True if file is based on the MS-ONE and MS-ONESTORE specs. False otherwise.
      */
-    public boolean isLegacy() {
-        return !GUID.nil().equals(guidLegacyFileVersion);
+    public boolean isMsOneStoreFormat() {
+        return GUID.nil().equals(guidLegacyFileVersion);
     }
 
     public GUID getGuidFileType() {
@@ -119,7 +119,8 @@ class OneNoteHeader implements Serializable {
         return ffvOldestCodeThatHasWrittenToThisFile;
     }
 
-    public OneNoteHeader setFfvOldestCodeThatHasWrittenToThisFile(long ffvOldestCodeThatHasWrittenToThisFile) {
+    public OneNoteHeader setFfvOldestCodeThatHasWrittenToThisFile(
+            long ffvOldestCodeThatHasWrittenToThisFile) {
         this.ffvOldestCodeThatHasWrittenToThisFile = ffvOldestCodeThatHasWrittenToThisFile;
         return this;
     }
@@ -128,7 +129,8 @@ class OneNoteHeader implements Serializable {
         return ffvNewestCodeThatHasWrittenToThisFile;
     }
 
-    public OneNoteHeader setFfvNewestCodeThatHasWrittenToThisFile(long ffvNewestCodeThatHasWrittenToThisFile) {
+    public OneNoteHeader setFfvNewestCodeThatHasWrittenToThisFile(
+            long ffvNewestCodeThatHasWrittenToThisFile) {
         this.ffvNewestCodeThatHasWrittenToThisFile = ffvNewestCodeThatHasWrittenToThisFile;
         return this;
     }
@@ -137,7 +139,8 @@ class OneNoteHeader implements Serializable {
         return ffvOldestCodeThatMayReadThisFile;
     }
 
-    public OneNoteHeader setFfvOldestCodeThatMayReadThisFile(long ffvOldestCodeThatMayReadThisFile) {
+    public OneNoteHeader setFfvOldestCodeThatMayReadThisFile(
+            long ffvOldestCodeThatMayReadThisFile) {
         this.ffvOldestCodeThatMayReadThisFile = ffvOldestCodeThatMayReadThisFile;
         return this;
     }
@@ -191,7 +194,8 @@ class OneNoteHeader implements Serializable {
         return fcrLegacyFileNodeListRoot;
     }
 
-    public OneNoteHeader setFcrLegacyFileNodeListRoot(FileChunkReference fcrLegacyFileNodeListRoot) {
+    public OneNoteHeader setFcrLegacyFileNodeListRoot(
+            FileChunkReference fcrLegacyFileNodeListRoot) {
         this.fcrLegacyFileNodeListRoot = fcrLegacyFileNodeListRoot;
         return this;
     }
@@ -200,7 +204,8 @@ class OneNoteHeader implements Serializable {
         return cbLegacyFreeSpaceInFreeChunkList;
     }
 
-    public OneNoteHeader setCbLegacyFreeSpaceInFreeChunkList(long cbLegacyFreeSpaceInFreeChunkList) {
+    public OneNoteHeader setCbLegacyFreeSpaceInFreeChunkList(
+            long cbLegacyFreeSpaceInFreeChunkList) {
         this.cbLegacyFreeSpaceInFreeChunkList = cbLegacyFreeSpaceInFreeChunkList;
         return this;
     }
@@ -402,4 +407,13 @@ class OneNoteHeader implements Serializable {
         this.buildNumberNewestWritten = buildNumberNewestWritten;
         return this;
     }
+
+    public boolean isLegacyOrAlternativePackaging() {
+        return legacyOrAlternativePackaging;
+    }
+
+    public OneNoteHeader setLegacyOrAlternativePackaging(boolean legacyOrAlternativePackaging) {
+        this.legacyOrAlternativePackaging = legacyOrAlternativePackaging;
+        return this;
+    }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteLegacyDumpStrings.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteLegacyDumpStrings.java
index bdcff02..a1bebf0 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteLegacyDumpStrings.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteLegacyDumpStrings.java
@@ -16,21 +16,23 @@
  */
 package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.sax.XHTMLContentHandler;
-import org.xml.sax.SAXException;
-
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.xml.sax.SAXException;
+
 /**
- * OneNote versions before OneNote 2010 do not have a published OpenSpec document, and the older formats are drastically
+ * OneNote versions before OneNote 2010 do not have a published OpenSpec document, and the older
+ * formats are drastically
  * incompatible with the later OpenSpecs.
- * Therefore, we resort to scraping out useful ASCII and UTF16LE strings using a similar algorithm used by the GNU "strings"
+ * Therefore, we resort to scraping out useful ASCII and UTF16LE strings using a similar
+ * algorithm used by the GNU "strings"
  * program.
- *
+ * <p>
  * This is only needed for OneNote versions prior to 2010.
  */
 class OneNoteLegacyDumpStrings {
@@ -44,13 +46,15 @@ class OneNoteLegacyDumpStrings {
     OneNoteDirectFileResource oneNoteDirectFileResource;
     XHTMLContentHandler xhtml;
 
-    public OneNoteLegacyDumpStrings(OneNoteDirectFileResource oneNoteDirectFileResource, XHTMLContentHandler xhtml) {
+    public OneNoteLegacyDumpStrings(OneNoteDirectFileResource oneNoteDirectFileResource,
+                                    XHTMLContentHandler xhtml) {
         this.oneNoteDirectFileResource = oneNoteDirectFileResource;
         this.xhtml = xhtml;
     }
 
     /**
      * Dump all "useful" Ascii and UTF16LE strings found in the file to the XHTMLContentHandler.
+     *
      * @throws TikaException
      * @throws SAXException
      */
@@ -60,7 +64,8 @@ class OneNoteLegacyDumpStrings {
     }
 
     /**
-     * Based on GNU "strings" implementation. Pulls out ascii text segments and writes them to the XHTMLContentHandler.
+     * Based on GNU "strings" implementation. Pulls out ascii text segments and writes them to
+     * the XHTMLContentHandler.
      */
     private void dumpAscii() throws SAXException, TikaException {
         try {
@@ -73,7 +78,7 @@ class OneNoteLegacyDumpStrings {
                 if (sz - pos < BUFFER_SIZE) {
                     nextBufferSize = sz - pos;
                 }
-                ByteBuffer byteBuffer = ByteBuffer.allocate((int)nextBufferSize);
+                ByteBuffer byteBuffer = ByteBuffer.allocate((int) nextBufferSize);
                 oneNoteDirectFileResource.read(byteBuffer);
                 for (long i = 0; i < nextBufferSize - 1; ++i) {
                     int b = byteBuffer.get((int) i);
@@ -94,8 +99,10 @@ class OneNoteLegacyDumpStrings {
             throw new TikaException("Could not extract text from legacy OneNote document", e);
         }
     }
+
     /**
-     * Based on GNU "strings" implementation. Pulls out UTF16 LE text segments and writes them to the XHTMLContentHandler.
+     * Based on GNU "strings" implementation. Pulls out UTF16 LE text segments and writes them to
+     * the XHTMLContentHandler.
      */
     private void dumpUtf16LE() throws SAXException, TikaException {
         try {
@@ -114,15 +121,15 @@ class OneNoteLegacyDumpStrings {
                 if (sz - pos < bufSize) {
                     nextBufferSize = sz - pos;
                 }
-                ByteBuffer byteBuffer = ByteBuffer.allocate((int)nextBufferSize);
+                ByteBuffer byteBuffer = ByteBuffer.allocate((int) nextBufferSize);
                 oneNoteDirectFileResource.read(byteBuffer);
+                for (long i = 0; i < nextBufferSize - 1; i++) {
+                    int c1 = byteBuffer.get((int) i) & 0xff;
+                    int c2 = byteBuffer.get((int) i + 1);
 
-                for (long i = 0; i < nextBufferSize - 1; ++i) {
-                    int c1 = byteBuffer.get((int)i);
-                    int c2 = byteBuffer.get((int)i+1);
-                    if (c1 == 0x00 && c2 >= 0x20 && c2 < 0x7F) {
+                    if (c2 == 0x00 && c1 >= 0x20) { // add this back? && c1 < 0x7F) {
                         ++i;
-                        os.write(c2);
+                        os.write(c1);
                     } else {
                         if (os.size() >= MIN_STRING_LENGTH) {
                             writeIfUseful(os);
@@ -138,14 +145,17 @@ class OneNoteLegacyDumpStrings {
             throw new TikaException("Could not extract text from legacy OneNote document", e);
         }
     }
+
     /**
-     * Writes a buffer of output characters if the (num alpha chars in the buffer) / (number of chars in the buffer) >
+     * Writes a buffer of output characters if the (num alpha chars in the buffer) / (number of
+     * chars in the buffer) >
      * ACCEPTABLE_ALPHA_TO_OTHER_CHAR_RATIO.
+     *
      * @param os Byte array output stream containing the buffer.
      */
     private void writeIfUseful(ByteArrayOutputStream os) throws SAXException {
-        String str = new String(os.toByteArray(), StandardCharsets.US_ASCII);
-        String [] spl = str.split(" ");
+        String str = new String(os.toByteArray(), StandardCharsets.ISO_8859_1);
+        String[] spl = str.split(" ");
         if (spl.length > 1) {
             int numAlpha = 0;
             for (int i = 0; i < str.length(); ++i) {
@@ -160,4 +170,4 @@ class OneNoteLegacyDumpStrings {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java
index 6c27505..69e4e2c 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteParser.java
@@ -14,31 +14,36 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.Property;
+import org.apache.tika.metadata.TikaCoreProperties;
 import org.apache.tika.mime.MediaType;
 import org.apache.tika.parser.AbstractParser;
 import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.MSOneStorePackage;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.MSOneStoreParser;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.AlternativePackaging;
 import org.apache.tika.sax.XHTMLContentHandler;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * OneNote tika parser capable of parsing Microsoft OneNote files.
  * <p>
@@ -46,20 +51,23 @@ import java.util.Set;
  */
 public class OneNoteParser extends AbstractParser {
 
-    private static final Map<MediaType, List<String>> typesMap = new HashMap<>();
+    public static final String ONE_NOTE_PREFIX = "onenote:";
+    private static final Map<MediaType, List<String>> TYPES_MAP = new HashMap<>();
     /**
      * Serial version UID
      */
     private static final long serialVersionUID = -5504243905998074168L;
+    private static final Set<MediaType> SUPPORTED_TYPES =
+            Collections.unmodifiableSet(TYPES_MAP.keySet());
 
     static {
         // All types should be 4 bytes long, space padded as needed
-        typesMap.put(MediaType.application("onenote; format=one"), Arrays.asList("ONE "));
+        TYPES_MAP.put(MediaType.application("onenote; format=one"),
+                Collections.singletonList("ONE "));
         // TODO - add onetoc and other onenote mime types
     }
 
-    private static final Set<MediaType> SUPPORTED_TYPES =
-            Collections.unmodifiableSet(typesMap.keySet());
+    private final OneNoteTreeWalkerOptions options = new OneNoteTreeWalkerOptions();
 
     @Override
     public Set<MediaType> getSupportedTypes(ParseContext context) {
@@ -67,105 +75,157 @@ public class OneNoteParser extends AbstractParser {
     }
 
     @Override
-    public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException,
-            SAXException, TikaException {
+    public void parse(InputStream stream, ContentHandler handler, Metadata metadata,
+                      ParseContext context) throws IOException, SAXException, TikaException {
+        byte[] oneStoreFileBytes = IOUtils.toByteArray(stream);
 
         try (TemporaryResources temporaryResources = new TemporaryResources();
-             TikaInputStream tikaInputStream = TikaInputStream.get(stream, temporaryResources);
-             OneNoteDirectFileResource oneNoteDirectFileResource = new OneNoteDirectFileResource(tikaInputStream.getFile())) {
-
-            temporaryResources.addResource(oneNoteDirectFileResource);
+                TikaInputStream tikaInputStream = TikaInputStream.get(oneStoreFileBytes);
+                OneNoteDirectFileResource oneNoteDirectFileResource = new OneNoteDirectFileResource(
+                        tikaInputStream.getFile())) {
             XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata);
             xhtml.startDocument();
-            OneNoteDocument oneNoteDocument = createOneNoteDocumentFromDirectFileResource(oneNoteDirectFileResource);
-
-            if (!oneNoteDocument.header.isLegacy()) {
-                metadata.set("buildNumberCreated", "0x" + Long.toHexString(oneNoteDocument.header.buildNumberCreated));
-                metadata.set("buildNumberLastWroteToFile", "0x" + Long.toHexString(oneNoteDocument.header.buildNumberLastWroteToFile));
-                metadata.set("buildNumberNewestWritten", "0x" + Long.toHexString(oneNoteDocument.header.buildNumberNewestWritten));
-                metadata.set("buildNumberOldestWritten", "0x" + Long.toHexString(oneNoteDocument.header.buildNumberOldestWritten));
-                metadata.set("cbExpectedFileLength", "0x" + Long.toHexString(oneNoteDocument.header.cbExpectedFileLength));
-                metadata.set("cbFreeSpaceInFreeChunkList", "0x" + Long.toHexString(oneNoteDocument.header.cbFreeSpaceInFreeChunkList));
-                metadata.set("cbLegacyExpectedFileLength", "0x" + Long.toHexString(oneNoteDocument.header.cbLegacyExpectedFileLength));
-                metadata.set("cbLegacyFreeSpaceInFreeChunkList",
-                    "0x" + Long.toHexString(oneNoteDocument.header.cbLegacyFreeSpaceInFreeChunkList));
-                metadata.set("crcName", "0x" + Long.toHexString(oneNoteDocument.header.crcName));
-                metadata.set("cTransactionsInLog", "0x" + Long.toHexString(oneNoteDocument.header.cTransactionsInLog));
-                metadata.set("ffvLastCodeThatWroteToThisFile", "0x" + Long.toHexString(oneNoteDocument.header.ffvLastCodeThatWroteToThisFile));
-                metadata.set("ffvNewestCodeThatHasWrittenToThisFile", "0x" + Long.toHexString(oneNoteDocument.header.ffvNewestCodeThatHasWrittenToThisFile));
-                metadata.set("ffvOldestCodeThatMayReadThisFile", "0x" + Long.toHexString(oneNoteDocument.header.ffvOldestCodeThatMayReadThisFile));
-                metadata.set("ffvOldestCodeThatHasWrittenToThisFile", "0x" + Long.toHexString(oneNoteDocument.header.ffvOldestCodeThatHasWrittenToThisFile));
-                metadata.set("grfDebugLogFlags", "0x" + Long.toHexString(oneNoteDocument.header.grfDebugLogFlags));
-                metadata.set("nFileVersionGeneration", "0x" + Long.toHexString(oneNoteDocument.header.nFileVersionGeneration));
-                metadata.set("rgbPlaceholder", "0x" + Long.toHexString(oneNoteDocument.header.rgbPlaceholder));
+            temporaryResources.addResource(oneNoteDirectFileResource);
+            OneNoteDocument oneNoteDocument =
+                    createOneNoteDocumentFromDirectFileResource(oneNoteDirectFileResource);
+
+            OneNoteHeader header = oneNoteDocument.header;
+
+            if (header.isMsOneStoreFormat()) {
+                metadata.set(ONE_NOTE_PREFIX + "buildNumberCreated",
+                        "0x" + Long.toHexString(oneNoteDocument.header.buildNumberCreated));
+                metadata.set(ONE_NOTE_PREFIX + "buildNumberLastWroteToFile",
+                        "0x" + Long.toHexString(oneNoteDocument.header.buildNumberLastWroteToFile));
+                metadata.set(ONE_NOTE_PREFIX + "buildNumberNewestWritten",
+                        "0x" + Long.toHexString(oneNoteDocument.header.buildNumberNewestWritten));
+                metadata.set(ONE_NOTE_PREFIX + "buildNumberOldestWritten",
+                        "0x" + Long.toHexString(oneNoteDocument.header.buildNumberOldestWritten));
+                metadata.set(ONE_NOTE_PREFIX + "cbExpectedFileLength",
+                        "0x" + Long.toHexString(oneNoteDocument.header.cbExpectedFileLength));
+                metadata.set(ONE_NOTE_PREFIX + "cbFreeSpaceInFreeChunkList",
+                        "0x" + Long.toHexString(oneNoteDocument.header.cbFreeSpaceInFreeChunkList));
+                metadata.set(ONE_NOTE_PREFIX + "cbLegacyExpectedFileLength",
+                        "0x" + Long.toHexString(oneNoteDocument.header.cbLegacyExpectedFileLength));
+                metadata.set(ONE_NOTE_PREFIX + "cbLegacyFreeSpaceInFreeChunkList", "0x" +
+                        Long.toHexString(oneNoteDocument.header.cbLegacyFreeSpaceInFreeChunkList));
+                metadata.set(ONE_NOTE_PREFIX + "crcName", "0x" + Long.toHexString(oneNoteDocument.header.crcName));
+                metadata.set(ONE_NOTE_PREFIX + "cTransactionsInLog",
+                        "0x" + Long.toHexString(oneNoteDocument.header.cTransactionsInLog));
+                metadata.set(ONE_NOTE_PREFIX + "ffvLastCodeThatWroteToThisFile", "0x" +
+                        Long.toHexString(oneNoteDocument.header.ffvLastCodeThatWroteToThisFile));
+                metadata.set(ONE_NOTE_PREFIX + "ffvNewestCodeThatHasWrittenToThisFile", "0x" + Long.toHexString(
+                        oneNoteDocument.header.ffvNewestCodeThatHasWrittenToThisFile));
+                metadata.set(ONE_NOTE_PREFIX + "ffvOldestCodeThatMayReadThisFile", "0x" +
+                        Long.toHexString(oneNoteDocument.header.ffvOldestCodeThatMayReadThisFile));
+                metadata.set(ONE_NOTE_PREFIX + "ffvOldestCodeThatHasWrittenToThisFile", "0x" + Long.toHexString(
+                        oneNoteDocument.header.ffvOldestCodeThatHasWrittenToThisFile));
+                metadata.set(ONE_NOTE_PREFIX + "grfDebugLogFlags",
+                        "0x" + Long.toHexString(oneNoteDocument.header.grfDebugLogFlags));
+                metadata.set(ONE_NOTE_PREFIX + "nFileVersionGeneration",
+                        "0x" + Long.toHexString(oneNoteDocument.header.nFileVersionGeneration));
+                metadata.set(ONE_NOTE_PREFIX + "rgbPlaceholder",
+                        "0x" + Long.toHexString(oneNoteDocument.header.rgbPlaceholder));
 
                 Pair<Long, ExtendedGUID> roleAndContext = Pair.of(1L, ExtendedGUID.nil());
-                OneNoteTreeWalker oneNoteTreeWalker = new OneNoteTreeWalker(
-                    new OneNoteTreeWalkerOptions(), oneNoteDocument,
-                    oneNoteDirectFileResource, xhtml, metadata, context, roleAndContext);
+                OneNoteTreeWalker oneNoteTreeWalker =
+                        new OneNoteTreeWalker(options, oneNoteDocument, oneNoteDirectFileResource,
+                                xhtml, metadata, context, roleAndContext);
 
                 oneNoteTreeWalker.walkTree();
 
                 if (!oneNoteTreeWalker.getAuthors().isEmpty()) {
-                    metadata.set(Property.externalTextBag("authors"), oneNoteTreeWalker.getAuthors().toArray(new String[] {}));
+                    metadata.set(TikaCoreProperties.CREATOR,
+                            oneNoteTreeWalker.getAuthors().toArray(new String[]{}));
                 }
                 if (!oneNoteTreeWalker.getMostRecentAuthors().isEmpty()) {
-                    metadata.set(Property.externalTextBag("mostRecentAuthors"), oneNoteTreeWalker.getMostRecentAuthors().toArray(new String[] {}));
+                    metadata.set(Property.externalTextBag(ONE_NOTE_PREFIX + "mostRecentAuthors"),
+                            oneNoteTreeWalker.getMostRecentAuthors().toArray(new String[]{}));
                 }
                 if (!oneNoteTreeWalker.getOriginalAuthors().isEmpty()) {
-                    metadata.set(Property.externalTextBag("originalAuthors"), oneNoteTreeWalker.getOriginalAuthors().toArray(new String[] {}));
+                    metadata.set(Property.externalTextBag(ONE_NOTE_PREFIX + "originalAuthors"),
+                            oneNoteTreeWalker.getOriginalAuthors().toArray(new String[]{}));
                 }
-                if (!Instant.MAX.equals(oneNoteTreeWalker.getCreationTimestamp())) {
-                    metadata.set("creationTimestamp", String.valueOf(oneNoteTreeWalker.getCreationTimestamp()));
+                if (!Instant.MAX.equals(
+                        Instant.ofEpochMilli(oneNoteTreeWalker.getCreationTimestamp()))) {
+                    metadata.set(ONE_NOTE_PREFIX + "creationTimestamp",
+                            String.valueOf(oneNoteTreeWalker.getCreationTimestamp()));
                 }
                 if (!Instant.MIN.equals(oneNoteTreeWalker.getLastModifiedTimestamp())) {
-                    metadata.set("lastModifiedTimestamp", String.valueOf(oneNoteTreeWalker.getLastModifiedTimestamp().toEpochMilli()));
+                    metadata.set(ONE_NOTE_PREFIX + "lastModifiedTimestamp", String.valueOf(
+                            oneNoteTreeWalker.getLastModifiedTimestamp().toEpochMilli()));
                 }
                 if (oneNoteTreeWalker.getLastModified() > Long.MIN_VALUE) {
-                    metadata.set("lastModified", String.valueOf(oneNoteTreeWalker.getLastModified()));
+                    metadata.set(TikaCoreProperties.MODIFIED,
+                            String.valueOf(oneNoteTreeWalker.getLastModified()));
+                }
+            } else if (header.isLegacyOrAlternativePackaging()) {
+                try {
+                    AlternativePackaging alternatePackageOneStoreFile = new AlternativePackaging();
+                    alternatePackageOneStoreFile.doDeserializeFromByteArray(oneStoreFileBytes, 0);
+
+                    MSOneStoreParser onenoteParser = new MSOneStoreParser();
+                    MSOneStorePackage pkg =
+                            onenoteParser.parse(alternatePackageOneStoreFile.dataElementPackage);
+
+                    pkg.walkTree(options, metadata, xhtml);
+                } catch (Exception e) {
+                    OneNoteLegacyDumpStrings dumpStrings =
+                            new OneNoteLegacyDumpStrings(oneNoteDirectFileResource, xhtml);
+                    dumpStrings.dump();
                 }
             } else {
-                OneNoteLegacyDumpStrings dumpStrings = new OneNoteLegacyDumpStrings(oneNoteDirectFileResource, xhtml);
-                dumpStrings.dump();
+                throw new TikaException("Invalid OneStore document - could not parse headers");
             }
             xhtml.endDocument();
         }
+
+
     }
 
     /**
      * Create a OneNoteDocument object.
      * <p>
-     * This won't actually have the binary data of any of the sections, but it's more of a metadata structure that contains
-     * the general structure of the container and contains offset positions of where to find the binary data we care about.
+     * This won't actually have the binary data of the sections, but it's more of a
+     * metadata structure that contains
+     * the general structure of the container and contains offset positions of where to find the
+     * binary data we care about.
      * <p>
      * OneNote files are of format:
      * <p>
-     * The header (section 2.3.1 in MS-ONESTORE) is the first 1024 bytes of the file. It contains references to the other structures in the
+     * The header (section 2.3.1 in MS-ONESTORE) is the first 1024 bytes of the file. It contains
+     * references to the other structures in the
      * file as well as metadata about the file.
-     * The free chunk list (section 2.3.2 in MS-ONESTORE) defines where there are free spaces in the file where data can be written.
-     * The transaction log (section 2.3.3 in MS-ONESTORE) stores the state and length of each file node list (section 2.4 in MS-ONESTORE)
+     * The free chunk list (section 2.3.2 in MS-ONESTORE) defines where there are free spaces in
+     * the file where data can be written.
+     * The transaction log (section 2.3.3 in MS-ONESTORE) stores the state and length of each
+     * file node list (section 2.4 in MS-ONESTORE)
      * in the file.
-     * The hashed chunk list (section 2.3.4 in MS-ONESTORE) stores read-only objects in the file that can be referenced by multiple
+     * The hashed chunk list (section 2.3.4 in MS-ONESTORE) stores read-only objects in the file
+     * that can be referenced by multiple
      * revisions (section 2.1.8 in MS-ONESTORE).
-     * The root file node list (section 2.1.14 in MS-ONESTORE) is the file node list that is the root of the tree of all file node lists in
+     * The root file node list (section 2.1.14 in MS-ONESTORE) is the file node list that is the
+     * root of the tree of all file node lists in
      * the file.
      * <p>
      * In this method we first parse the header.
      * <p>
      * After parsing the header, this results in header.fcrFileNodeListRoot that points to the first
      *
-     * @param oneNoteDirectFileResource A random access file resource used as the source of the content.
-     * @return A parsed one note document. This document does not contain any of the binary data, rather it just contains
+     * @param oneNoteDirectFileResource A random access file resource used as the source of the
+     *                                  content.
+     * @return A parsed one note document. This document does not contain any of the binary data,
+     * rather it just contains
      * the data pointers and metadata.
      * @throws IOException Will throw IOException in typical IO issue situations.
      */
-    public OneNoteDocument createOneNoteDocumentFromDirectFileResource(OneNoteDirectFileResource oneNoteDirectFileResource) throws IOException, TikaException {
+    public OneNoteDocument createOneNoteDocumentFromDirectFileResource(
+            OneNoteDirectFileResource oneNoteDirectFileResource) throws IOException, TikaException {
         OneNoteDocument oneNoteDocument = new OneNoteDocument();
         OneNotePtr oneNotePtr = new OneNotePtr(oneNoteDocument, oneNoteDirectFileResource);
         // First parse out the header.
         oneNoteDocument.header = oneNotePtr.deserializeHeader();
 
-        if (!oneNoteDocument.header.isLegacy()) {
+        if (oneNoteDocument.header.isMsOneStoreFormat()) {
             // Now that we parsed the header, the "root file node list"
             oneNotePtr.reposition(oneNoteDocument.header.fcrFileNodeListRoot);
             FileNodePtr curPath = new FileNodePtr();
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyEnum.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyEnum.java
index c47a5f7..f676b55 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyEnum.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyEnum.java
@@ -14,163 +14,70 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 import java.util.HashMap;
 import java.util.Map;
 
 @SuppressWarnings("unused")
-enum OneNotePropertyEnum {
-    LayoutTightLayout(0x08001C00),
-    PageWidth(0x14001C01),
-    PageHeight(0x14001C02),
-    OutlineElementChildLevel(0x0C001C03),
-    Bold(0x08001C04),
-    Italic(0x08001C05),
-    Underline(0x08001C06),
-    Strikethrough(0x08001C07),
-    Superscript(0x08001C08),
-    Subscript(0x08001C09),
-    Font(0x1C001C0A),
-    FontSize(0x10001C0B),
-    FontColor(0x14001C0C),
-    Highlight(0x14001C0D),
-    RgOutlineIndentDistance(0x1C001C12),
-    BodyTextAlignment(0x0C001C13),
-    OffsetFromParentHoriz(0x14001C14),
-    OffsetFromParentVert(0x14001C15),
-    NumberListFormat(0x1C001C1A),
-    LayoutMaxWidth(0x14001C1B),
-    LayoutMaxHeight(0x14001C1C),
-    ContentChildNodesOfOutlineElement(0x24001C1F),
-    ContentChildNodesOfPageManifest(0x24001C1F),
-    ElementChildNodesOfSection(0x24001C20),
-    ElementChildNodesOfPage(0x24001C20),
-    ElementChildNodesOfTitle(0x24001C20),
-    ElementChildNodesOfOutline(0x24001C20),
-    ElementChildNodesOfOutlineElement(0x24001C20),
-    ElementChildNodesOfTable(0x24001C20),
-    ElementChildNodesOfTableRow(0x24001C20),
-    ElementChildNodesOfTableCell(0x24001C20),
-    ElementChildNodesOfVersionHistory(0x24001C20),
-    EnableHistory(0x08001E1E),
-    RichEditTextUnicode(0x1C001C22),
-    ListNodes(0x24001C26),
-    NotebookManagementEntityGuid(0x1C001C30),
-    OutlineElementRTL(0x08001C34),
-    LanguageID(0x14001C3B),
-    LayoutAlignmentInParent(0x14001C3E),
-    PictureContainer(0x20001C3F),
-    PageMarginTop(0x14001C4C),
-    PageMarginBottom(0x14001C4D),
-    PageMarginLeft(0x14001C4E),
-    PageMarginRight(0x14001C4F),
-    ListFont(0x1C001C52),
-    TopologyCreationTimeStamp(0x18001C65),
-    LayoutAlignmentSelf(0x14001C84),
-    IsTitleTime(0x08001C87),
-    IsBoilerText(0x08001C88),
-    PageSize(0x14001C8B),
-    PortraitPage(0x08001C8E),
-    EnforceOutlineStructure(0x08001C91),
-    EditRootRTL(0x08001C92),
-    CannotBeSelected(0x08001CB2),
-    IsTitleText(0x08001CB4),
-    IsTitleDate(0x08001CB5),
-    ListRestart(0x14001CB7),
-    IsLayoutSizeSetByUser(0x08001CBD),
-    ListSpacingMu(0x14001CCB),
-    LayoutOutlineReservedWidth(0x14001CDB),
-    LayoutResolveChildCollisions(0x08001CDC),
-    IsReadOnly(0x08001CDE),
-    LayoutMinimumOutlineWidth(0x14001CEC),
-    LayoutCollisionPriority(0x14001CF1),
-    CachedTitleString(0x1C001CF3),
-    DescendantsCannotBeMoved(0x08001CF9),
-    RichEditTextLangID(0x10001CFE),
-    LayoutTightAlignment(0x08001CFF),
-    Charset(0x0C001D01),
-    CreationTimeStamp(0x14001D09),
-    Deletable(0x08001D0C),
-    ListMSAAIndex(0x10001D0E),
-    IsBackground(0x08001D13),
-    IRecordMedia(0x14001D24),
-    CachedTitleStringFromPage(0x1C001D3C),
-    RowCount(0x14001D57),
-    ColumnCount(0x14001D58),
-    TableBordersVisible(0x08001D5E),
-    StructureElementChildNodes(0x24001D5F),
-    ChildGraphSpaceElementNodes(0x2C001D63),
-    TableColumnWidths(0x1C001D66),
-    Author(0x1C001D75),
-    LastModifiedTimeStamp(0x18001D77),
-    AuthorOriginal(0x20001D78),
-    AuthorMostRecent(0x20001D79),
-    LastModifiedTime(0x14001D7A),
-    IsConflictPage(0x08001D7C),
-    TableColumnsLocked(0x1C001D7D),
-    SchemaRevisionInOrderToRead(0x14001D82),
-    IsConflictObjectForRender(0x08001D96),
-    EmbeddedFileContainer(0x20001D9B),
-    EmbeddedFileName(0x1C001D9C),
-    SourceFilepath(0x1C001D9D),
-    ConflictingUserName(0x1C001D9E),
-    ImageFilename(0x1C001DD7),
-    IsConflictObjectForSelection(0x08001DDB),
-    PageLevel(0x14001DFF),
-    TextRunIndex(0x1C001E12),
-    TextRunFormatting(0x24001E13),
-    Hyperlink(0x08001E14),
-    UnderlineType(0x0C001E15),
-    Hidden(0x08001E16),
-    HyperlinkProtected(0x08001E19),
-    TextRunIsEmbeddedObject(0x08001E22),
-    ImageAltText(0x1C001E58),
-    MathFormatting(0x08003401),
-    ParagraphStyle(0x2000342C),
-    ParagraphSpaceBefore(0x1400342E),
-    ParagraphSpaceAfter(0x1400342F),
-    ParagraphLineSpacingExact(0x14003430),
-    MetaDataObjectsAboveGraphSpace(0x24003442),
-    TextRunDataObject(0x24003458),
-    TextRunData(0x40003499),
-    ParagraphStyleId(0x1C00345A),
-    HasVersionPages(0x08003462),
-    ActionItemType(0x10003463),
-    NoteTagShape(0x10003464),
-    NoteTagHighlightColor(0x14003465),
-    NoteTagTextColor(0x14003466),
-    NoteTagPropertyStatus(0x14003467),
-    NoteTagLabel(0x1C003468),
-    NoteTagCreated(0x1400346E),
-    NoteTagCompleted(0x1400346F),
-    NoteTagDefinitionOid(0x20003488),
-    NoteTagStates(0x04003489),
-    ActionItemStatus(0x10003470),
-    ActionItemSchemaVersion(0x0C003473),
-    ReadingOrderRTL(0x08003476),
-    ParagraphAlignment(0x0C003477),
-    VersionHistoryGraphSpaceContextNodes(0x3400347B),
-    DisplayedPageNumber(0x14003480),
-    SectionDisplayName(0x1C00349B),
-    NextStyle(0x1C00348A),
-    WebPictureContainer14(0x200034C8),
-    ImageUploadState(0x140034CB),
-    TextExtendedAscii(0x1C003498),
-    PictureWidth(0x140034CD),
-    PictureHeight(0x140034CE),
-    PageMarginOriginX(0x14001D0F),
-    PageMarginOriginY(0x14001D10),
-    WzHyperlinkUrl(0x1C001E20),
-    TaskTagDueDate(0x1400346B),
+public enum OneNotePropertyEnum {
+    LayoutTightLayout(0x08001C00), PageWidth(0x14001C01), PageHeight(0x14001C02),
+    OutlineElementChildLevel(0x0C001C03), Bold(0x08001C04), Italic(0x08001C05),
+    Underline(0x08001C06), Strikethrough(0x08001C07), Superscript(0x08001C08),
+    Subscript(0x08001C09), Font(0x1C001C0A), FontSize(0x10001C0B), FontColor(0x14001C0C),
+    Highlight(0x14001C0D), RgOutlineIndentDistance(0x1C001C12), BodyTextAlignment(0x0C001C13),
+    OffsetFromParentHoriz(0x14001C14), OffsetFromParentVert(0x14001C15),
+    NumberListFormat(0x1C001C1A), LayoutMaxWidth(0x14001C1B), LayoutMaxHeight(0x14001C1C),
+    ContentChildNodesOfOutlineElement(0x24001C1F), ContentChildNodesOfPageManifest(0x24001C1F),
+    ElementChildNodesOfSection(0x24001C20), ElementChildNodesOfPage(0x24001C20),
+    ElementChildNodesOfTitle(0x24001C20), ElementChildNodesOfOutline(0x24001C20),
+    ElementChildNodesOfOutlineElement(0x24001C20), ElementChildNodesOfTable(0x24001C20),
+    ElementChildNodesOfTableRow(0x24001C20), ElementChildNodesOfTableCell(0x24001C20),
+    ElementChildNodesOfVersionHistory(0x24001C20), EnableHistory(0x08001E1E),
+    RichEditTextUnicode(0x1C001C22), ListNodes(0x24001C26),
+    NotebookManagementEntityGuid(0x1C001C30), OutlineElementRTL(0x08001C34), LanguageID(0x14001C3B),
+    LayoutAlignmentInParent(0x14001C3E), PictureContainer(0x20001C3F), PageMarginTop(0x14001C4C),
+    PageMarginBottom(0x14001C4D), PageMarginLeft(0x14001C4E), PageMarginRight(0x14001C4F),
+    ListFont(0x1C001C52), TopologyCreationTimeStamp(0x18001C65), LayoutAlignmentSelf(0x14001C84),
+    IsTitleTime(0x08001C87), IsBoilerText(0x08001C88), PageSize(0x14001C8B),
+    PortraitPage(0x08001C8E), EnforceOutlineStructure(0x08001C91), EditRootRTL(0x08001C92),
+    CannotBeSelected(0x08001CB2), IsTitleText(0x08001CB4), IsTitleDate(0x08001CB5),
+    ListRestart(0x14001CB7), IsLayoutSizeSetByUser(0x08001CBD), ListSpacingMu(0x14001CCB),
+    LayoutOutlineReservedWidth(0x14001CDB), LayoutResolveChildCollisions(0x08001CDC),
+    IsReadOnly(0x08001CDE), LayoutMinimumOutlineWidth(0x14001CEC),
+    LayoutCollisionPriority(0x14001CF1), CachedTitleString(0x1C001CF3),
+    DescendantsCannotBeMoved(0x08001CF9), RichEditTextLangID(0x10001CFE),
+    LayoutTightAlignment(0x08001CFF), Charset(0x0C001D01), CreationTimeStamp(0x14001D09),
+    Deletable(0x08001D0C), ListMSAAIndex(0x10001D0E), IsBackground(0x08001D13),
+    IRecordMedia(0x14001D24), CachedTitleStringFromPage(0x1C001D3C), RowCount(0x14001D57),
+    ColumnCount(0x14001D58), TableBordersVisible(0x08001D5E),
+    StructureElementChildNodes(0x24001D5F), ChildGraphSpaceElementNodes(0x2C001D63),
+    TableColumnWidths(0x1C001D66), Author(0x1C001D75), LastModifiedTimeStamp(0x18001D77),
+    AuthorOriginal(0x20001D78), AuthorMostRecent(0x20001D79), LastModifiedTime(0x14001D7A),
+    IsConflictPage(0x08001D7C), TableColumnsLocked(0x1C001D7D),
+    SchemaRevisionInOrderToRead(0x14001D82), IsConflictObjectForRender(0x08001D96),
+    EmbeddedFileContainer(0x20001D9B), EmbeddedFileName(0x1C001D9C), SourceFilepath(0x1C001D9D),
+    ConflictingUserName(0x1C001D9E), ImageFilename(0x1C001DD7),
+    IsConflictObjectForSelection(0x08001DDB), PageLevel(0x14001DFF), TextRunIndex(0x1C001E12),
+    TextRunFormatting(0x24001E13), Hyperlink(0x08001E14), UnderlineType(0x0C001E15),
+    Hidden(0x08001E16), HyperlinkProtected(0x08001E19), TextRunIsEmbeddedObject(0x08001E22),
+    ImageAltText(0x1C001E58), MathFormatting(0x08003401), ParagraphStyle(0x2000342C),
+    ParagraphSpaceBefore(0x1400342E), ParagraphSpaceAfter(0x1400342F),
+    ParagraphLineSpacingExact(0x14003430), MetaDataObjectsAboveGraphSpace(0x24003442),
+    TextRunDataObject(0x24003458), TextRunData(0x40003499), ParagraphStyleId(0x1C00345A),
+    HasVersionPages(0x08003462), ActionItemType(0x10003463), NoteTagShape(0x10003464),
+    NoteTagHighlightColor(0x14003465), NoteTagTextColor(0x14003466),
+    NoteTagPropertyStatus(0x14003467), NoteTagLabel(0x1C003468), NoteTagCreated(0x1400346E),
+    NoteTagCompleted(0x1400346F), NoteTagDefinitionOid(0x20003488), NoteTagStates(0x04003489),
+    ActionItemStatus(0x10003470), ActionItemSchemaVersion(0x0C003473), ReadingOrderRTL(0x08003476),
+    ParagraphAlignment(0x0C003477), VersionHistoryGraphSpaceContextNodes(0x3400347B),
+    DisplayedPageNumber(0x14003480), SectionDisplayName(0x1C00349B), NextStyle(0x1C00348A),
+    WebPictureContainer14(0x200034C8), ImageUploadState(0x140034CB), TextExtendedAscii(0x1C003498),
+    PictureWidth(0x140034CD), PictureHeight(0x140034CE), PageMarginOriginX(0x14001D0F),
+    PageMarginOriginY(0x14001D10), WzHyperlinkUrl(0x1C001E20), TaskTagDueDate(0x1400346B),
     Unknown(0x00000000);
 
-    private long id;
-
-    OneNotePropertyEnum(long id) {
-        this.id = id;
-    }
-
     private static final Map<Long, OneNotePropertyEnum> BY_ID = new HashMap<>();
 
     static {
@@ -179,6 +86,12 @@ enum OneNotePropertyEnum {
         }
     }
 
+    private final long id;
+
+    OneNotePropertyEnum(long id) {
+        this.id = id;
+    }
+
     public static OneNotePropertyEnum of(Long id) {
         OneNotePropertyEnum result = BY_ID.get(id);
         if (result == null) {
@@ -202,7 +115,7 @@ enum OneNotePropertyEnum {
             inlineBool = ((pid >> 31) & 0x1) > 0; // set the bool value from header
         } else {
             if (((pid >> 31) & 0x1) > 0) {
-                throw new RuntimeException("Reserved non-zero");
+                throw new IllegalArgumentException("Reserved non-zero");
             }
         }
         return inlineBool;
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyId.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyId.java
index 661b03e..6491a93 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyId.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePropertyId.java
@@ -14,8 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
+import org.apache.tika.exception.TikaException;
+
 class OneNotePropertyId {
     OneNotePropertyEnum propertyEnum;
     long pid;
@@ -25,7 +28,7 @@ class OneNotePropertyId {
     public OneNotePropertyId() {
     }
 
-    public OneNotePropertyId(long pid) {
+    public OneNotePropertyId(long pid) throws TikaException {
         this.pid = pid;
         propertyEnum = OneNotePropertyEnum.of(pid);
         type = pid >> 26 & 0x1f;
@@ -34,7 +37,7 @@ class OneNotePropertyId {
             inlineBool = ((pid >> 31) & 0x1) > 0; // set the bool value from header
         } else {
             if (((pid >> 31) & 0x1) > 0) {
-                throw new RuntimeException("Reserved non-zero");
+                throw new TikaException("Reserved non-zero");
             }
         }
     }
@@ -77,10 +80,7 @@ class OneNotePropertyId {
 
     @Override
     public String toString() {
-        return "{" + propertyEnum +
-          ", pid=0x" + Long.toHexString(pid) +
-          ", type=0x" + Long.toHexString(type) +
-          ", inlineBool=" + inlineBool +
-          '}';
+        return "{" + propertyEnum + ", pid=0x" + Long.toHexString(pid) + ", type=0x" +
+                Long.toHexString(type) + ", inlineBool=" + inlineBool + '}';
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePtr.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePtr.java
index 0dc13f3..ce0d20b 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePtr.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNotePtr.java
@@ -17,13 +17,6 @@
 
 package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.io.EndianUtils;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.exception.TikaMemoryLimitException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
@@ -33,63 +26,35 @@ import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.io.EndianUtils;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.exception.TikaMemoryLimitException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
- * This is the main class used during parsing. This will contain an offset position and end position for reading bytes
- * from the byte stream.
+ * This is the main class used during parsing. This will contain an offset position and end
+ * position for reading bytes from the byte stream.
  * <p>
- * It contains all the deserialize methods used to read the different data elements from a one note file.
+ * It contains all the deserialize methods used to read the different data elements from a one
+ * note file.
  * <p>
- * You can construct a new one note pointer and it will reposition the byte channel and will read until
+ * You can construct a new one note pointer and it will reposition the byte channel and will
+ * read until
  */
 class OneNotePtr {
 
-    private static final Logger LOG = LoggerFactory.getLogger(OneNoteParser.class);
-
     public static final long FOOTER_CONST = 0x8BC215C38233BA4BL;
     public static final String UNKNOWN = "unknown";
-    private static final byte[] IFNDF = new byte[] {
-        60, 0, 105, 0, 102, 0, 110, 0, 100, 0, 102, 0, 62, 0
-    };
-
-    private static final GUID FILE_DATA_STORE_OBJ_HEADER = new GUID(new int[] {
-        0xBD,
-        0xE3,
-        0x16,
-        0xE7,
-        0x26,
-        0x65,
-        0x45,
-        0x11,
-        0xA4,
-        0xC4,
-        0x8D,
-        0x4D,
-        0x0B,
-        0x7A,
-        0x9E,
-        0xAC
-    });
-
-    private static final GUID FILE_DATA_STORE_OBJ_FOOTER = new GUID(new int[] {
-        0x71,
-        0xFB,
-        0xA7,
-        0x22,
-        0x0F,
-        0x79,
-        0x4A,
-        0x0B,
-        0xBB,
-        0x13,
-        0x89,
-        0x92,
-        0x56,
-        0x42,
-        0x6B,
-        0x24});
-
     public static final int IFNDF_GUID_LENGTH = 38; // 36 char guid with a { and a } char.
     public static final int NUM_RESERVED_BYTES_AT_END_OF_HEADER = 728;
+    private static final Logger LOG = LoggerFactory.getLogger(OneNoteParser.class);
+    private static final byte[] IFNDF =
+            new byte[]{60, 0, 105, 0, 102, 0, 110, 0, 100, 0, 102, 0, 62, 0};
+    private static final String PACKAGE_STORAGE_FILE_FORMAT_GUID =
+            "{638DE92F-A6D4-4BC1-9A36-B3FC2511A5B7}";
+
     int indentLevel = 0;
 
     long offset;
@@ -98,7 +63,8 @@ class OneNotePtr {
     OneNoteDocument document;
     OneNoteDirectFileResource dif;
 
-    public OneNotePtr(OneNoteDocument document, OneNoteDirectFileResource oneNoteDirectFileResource) throws IOException {
+    public OneNotePtr(OneNoteDocument document, OneNoteDirectFileResource oneNoteDirectFileResource)
+            throws IOException {
         this.document = document;
         this.dif = oneNoteDirectFileResource;
         offset = oneNoteDirectFileResource.position();
@@ -115,44 +81,45 @@ class OneNotePtr {
 
     public OneNoteHeader deserializeHeader() throws IOException, TikaException {
         OneNoteHeader data = new OneNoteHeader();
-        data.setGuidFileType(deserializeGUID())
-            .setGuidFile(deserializeGUID())
-            .setGuidLegacyFileVersion(deserializeGUID())
-            .setGuidFileFormat(deserializeGUID())
-            .setFfvLastCodeThatWroteToThisFile(deserializeLittleEndianInt())
-            .setFfvOldestCodeThatHasWrittenToThisFile(deserializeLittleEndianInt())
-            .setFfvNewestCodeThatHasWrittenToThisFile(deserializeLittleEndianInt())
-            .setFfvOldestCodeThatMayReadThisFile(deserializeLittleEndianInt())
-            .setFcrLegacyFreeChunkList(deserializeFileChunkReference64())
-            .setFcrLegacyTransactionLog(deserializeFileChunkReference64())
-            .setcTransactionsInLog(deserializeLittleEndianInt())
-            .setCbExpectedFileLength(deserializeLittleEndianInt())
-            .setRgbPlaceholder(deserializeLittleEndianLong())
-            .setFcrLegacyFileNodeListRoot(deserializeFileChunkReference64())
-            .setCbLegacyFreeSpaceInFreeChunkList(deserializeLittleEndianInt())
-            .setIgnoredZeroA(deserializeLittleEndianChar())
-            .setIgnoredZeroB(deserializeLittleEndianChar())
-            .setIgnoredZeroC(deserializeLittleEndianChar())
-            .setIgnoredZeroD(deserializeLittleEndianChar())
-            .setGuidAncestor(deserializeGUID())
-            .setCrcName(deserializeLittleEndianInt())
-            .setFcrHashedChunkList(deserializeFileChunkReference64x32())
-            .setFcrTransactionLog(deserializeFileChunkReference64x32())
-            .setFcrFileNodeListRoot(deserializeFileChunkReference64x32())
-            .setFcrFreeChunkList(deserializeFileChunkReference64x32())
-            .setCbExpectedFileLength(deserializeLittleEndianLong())
-            .setCbFreeSpaceInFreeChunkList(deserializeLittleEndianLong())
-            .setGuidFileVersion(deserializeGUID())
-            .setnFileVersionGeneration(deserializeLittleEndianLong())
-            .setGuidDenyReadFileVersion(deserializeGUID())
-            .setGrfDebugLogFlags(deserializeLittleEndianInt())
-            .setFcrDebugLogA(deserializeFileChunkReference64x32())
-            .setFcrDebugLogB(deserializeFileChunkReference64x32())
-            .setBuildNumberCreated(deserializeLittleEndianInt())
-            .setBuildNumberLastWroteToFile(deserializeLittleEndianInt())
-            .setBuildNumberOldestWritten(deserializeLittleEndianInt())
-            .setBuildNumberNewestWritten(deserializeLittleEndianInt());
-        ByteBuffer reservedBytesAtEndOfHeader = ByteBuffer.allocate(NUM_RESERVED_BYTES_AT_END_OF_HEADER);
+        data.setGuidFileType(deserializeGUID()).setGuidFile(deserializeGUID())
+                .setGuidLegacyFileVersion(deserializeGUID()).setGuidFileFormat(deserializeGUID())
+                .setFfvLastCodeThatWroteToThisFile(deserializeLittleEndianInt())
+                .setFfvOldestCodeThatHasWrittenToThisFile(deserializeLittleEndianInt())
+                .setFfvNewestCodeThatHasWrittenToThisFile(deserializeLittleEndianInt())
+                .setFfvOldestCodeThatMayReadThisFile(deserializeLittleEndianInt())
+                .setFcrLegacyFreeChunkList(deserializeFileChunkReference64())
+                .setFcrLegacyTransactionLog(deserializeFileChunkReference64())
+                .setcTransactionsInLog(deserializeLittleEndianInt())
+                .setCbExpectedFileLength(deserializeLittleEndianInt())
+                .setRgbPlaceholder(deserializeLittleEndianLong())
+                .setFcrLegacyFileNodeListRoot(deserializeFileChunkReference64())
+                .setCbLegacyFreeSpaceInFreeChunkList(deserializeLittleEndianInt())
+                .setIgnoredZeroA(deserializeLittleEndianChar())
+                .setIgnoredZeroB(deserializeLittleEndianChar())
+                .setIgnoredZeroC(deserializeLittleEndianChar())
+                .setIgnoredZeroD(deserializeLittleEndianChar()).setGuidAncestor(deserializeGUID())
+                .setCrcName(deserializeLittleEndianInt())
+                .setFcrHashedChunkList(deserializeFileChunkReference64x32())
+                .setFcrTransactionLog(deserializeFileChunkReference64x32())
+                .setFcrFileNodeListRoot(deserializeFileChunkReference64x32())
+                .setFcrFreeChunkList(deserializeFileChunkReference64x32())
+                .setCbExpectedFileLength(deserializeLittleEndianLong())
+                .setCbFreeSpaceInFreeChunkList(deserializeLittleEndianLong())
+                .setGuidFileVersion(deserializeGUID())
+                .setnFileVersionGeneration(deserializeLittleEndianLong())
+                .setGuidDenyReadFileVersion(deserializeGUID())
+                .setGrfDebugLogFlags(deserializeLittleEndianInt())
+                .setFcrDebugLogA(deserializeFileChunkReference64x32())
+                .setFcrDebugLogB(deserializeFileChunkReference64x32())
+                .setBuildNumberCreated(deserializeLittleEndianInt())
+                .setBuildNumberLastWroteToFile(deserializeLittleEndianInt())
+                .setBuildNumberOldestWritten(deserializeLittleEndianInt())
+                .setBuildNumberNewestWritten(deserializeLittleEndianInt());
+        if (data.getGuidFileFormat().toString().equals(PACKAGE_STORAGE_FILE_FORMAT_GUID)) {
+            return data.setLegacyOrAlternativePackaging(true);
+        }
+        ByteBuffer reservedBytesAtEndOfHeader =
+                ByteBuffer.allocate(NUM_RESERVED_BYTES_AT_END_OF_HEADER);
         deserializeBytes(reservedBytesAtEndOfHeader);
         return data;
     }
@@ -250,18 +217,17 @@ class OneNotePtr {
         }
         int c1 = dif.read();
         int c2 = dif.read();
-        long res = (((c1 & 0xff) << 0) +
-            ((c2 & 0xff) << 8));
+        long res = (((c1 & 0xff)) + ((c2 & 0xff) << 8));
         offset = dif.position();
         return res;
     }
 
     private String getIndent() {
-        String retval = "";
+        StringBuilder retval = new StringBuilder();
         for (int i = 0; i < indentLevel; ++i) {
-            retval += "  ";
+            retval.append("  ");
         }
-        return retval;
+        return retval.toString();
     }
 
     public void reposition(FileChunkReference loc) throws IOException {
@@ -279,20 +245,21 @@ class OneNotePtr {
      * <p>
      * A file node list can be divided into one or more FileNodeListFragment
      * structures. Each fragment can specify whether there are more fragments in the list and
-     * the location of the next fragment. Each fragment specifies a sub-sequence of FileNode structures
-     * from the file node list.
+     * the location of the next fragment. Each fragment specifies a sub-sequence
+     * of FileNode structures from the file node list.
      * <p>
-     * When specifying the structure of a specific file node list in this document, the division of the list into
-     * fragments is ignored and FileNode structures with FileNode.FileNodeID field values equal to 0x0FF
-     * ("ChunkTerminatorFND") are not specified.
+     * When specifying the structure of a specific file node list in this document, the division
+     * of the list into fragments is ignored and FileNode structures with FileNode.FileNodeID
+     * field values equal to 0x0FF ("ChunkTerminatorFND") are not specified.
      *
      * @param ptr          The current OneNotePtr we are at currently.
      * @param fileNodeList The file node list to populate as we parse.
      * @param curPath      The current FileNodePtr.
      * @return The resulting one note pointer after node lists are all parsed.
      */
-    public OneNotePtr internalDeserializeFileNodeList(OneNotePtr ptr, FileNodeList fileNodeList, FileNodePtr curPath) throws IOException,
-        TikaException {
+    public OneNotePtr internalDeserializeFileNodeList(OneNotePtr ptr, FileNodeList fileNodeList,
+                                                      FileNodePtr curPath)
+            throws IOException, TikaException {
         OneNotePtr localPtr = new OneNotePtr(document, dif);
         FileNodePtrBackPush bp = new FileNodePtrBackPush(curPath);
         try {
@@ -312,24 +279,27 @@ class OneNotePtr {
     }
 
 
-    public OneNotePtr deserializeFileNodeList(FileNodeList fileNodeList, FileNodePtr curPath) throws IOException, TikaException {
+    public OneNotePtr deserializeFileNodeList(FileNodeList fileNodeList, FileNodePtr curPath)
+            throws IOException, TikaException {
         return internalDeserializeFileNodeList(this, fileNodeList, curPath);
     }
 
     /**
      * Deserializes a FileNodeListFragment.
      * <p>
-     * The FileNodeListFragment structure specifies a sequence of file nodes from a file node list. The size of the
-     * FileNodeListFragment structure is specified by the structure that references it.
+     * The FileNodeListFragment structure specifies a sequence of file nodes from a file node
+     * list. The size of the FileNodeListFragment structure is specified by the structure that
+     * references it.
      * <p>
-     * All fragments in the same file node list MUST have the same FileNodeListFragment.header.FileNodeListID field.
+     * All fragments in the same file node list MUST have the same FileNodeListFragment.header
+     * .FileNodeListID field.
      *
      * @param data    List of file nodes that we collect while deserializing.
      * @param next    The next file chunk we are referencing.
      * @param curPath The current FileNodePtr.
      */
-    void deserializeFileNodeListFragment(FileNodeList data, FileChunkReference next, FileNodePtr curPath) throws IOException,
-        TikaException {
+    void deserializeFileNodeListFragment(FileNodeList data, FileChunkReference next,
+                                         FileNodePtr curPath) throws IOException, TikaException {
         data.fileNodeListHeader = deserializeFileNodeListHeader();
         boolean terminated = false;
         while (offset + 24 <= end) { // while there are at least 24 bytes free
@@ -338,7 +308,8 @@ class OneNotePtr {
             CheckedFileNodePushBack pushBack = new CheckedFileNodePushBack(data);
             try {
                 long initialOffset = offset;
-                FileNode fileNode = deserializeFileNode(data.children.get(data.children.size() - 1), curPath);
+                FileNode fileNode =
+                        deserializeFileNode(data.children.get(data.children.size() - 1), curPath);
                 if (initialOffset == offset) {
                     //nothing read; avoid an infinite loop
                     break;
@@ -350,9 +321,12 @@ class OneNotePtr {
                 pushBack.commit();
                 FileNode dereference = curPath.dereference(document);
                 FileNode lastChild = data.children.get(data.children.size() - 1);
-                assert dereference.equals(lastChild); // is this correct? or should we be checking the pointer?
-                Integer curPathOffset = curPath.nodeListPositions.get(curPath.nodeListPositions.size() - 1);
-                curPath.nodeListPositions.set(curPath.nodeListPositions.size() - 1, curPathOffset + 1);
+                assert dereference.equals(lastChild); // is this correct? or should we be
+                // checking the pointer?
+                Integer curPathOffset =
+                        curPath.nodeListPositions.get(curPath.nodeListPositions.size() - 1);
+                curPath.nodeListPositions.set(curPath.nodeListPositions.size() - 1,
+                        curPathOffset + 1);
             } finally {
                 pushBack.popBackIfNotCommitted();
             }
@@ -362,17 +336,20 @@ class OneNotePtr {
         next.cb = nextChunkRef.cb;
         next.stp = nextChunkRef.stp;
         if (terminated) {
-            LOG.debug("{}Chunk terminator found NextChunkRef.cb={}, NextChunkRef.stp={}, Offset={}, End={}", getIndent(), nextChunkRef.cb
-                , nextChunkRef.stp, offset, end);
+            LOG.debug("{}Chunk terminator found NextChunkRef.cb={}, NextChunkRef.stp={}," +
+                            " Offset={}, End={}", getIndent(), nextChunkRef.cb, nextChunkRef.stp, offset,
+                    end);
             // TODO check that next is OK
         }
         long footer = deserializeLittleEndianLong();
         if (footer != FOOTER_CONST) {
-            throw new TikaException("Invalid footer constant. Expected " + FOOTER_CONST + " but was " + footer);
+            throw new TikaException(
+                    "Invalid footer constant. Expected " + FOOTER_CONST + " but was " + footer);
         }
     }
 
-    private FileNode deserializeFileNode(FileNode data, FileNodePtr curPath) throws IOException, TikaException {
+    private FileNode deserializeFileNode(FileNode data, FileNodePtr curPath)
+            throws IOException, TikaException {
         OneNotePtr backup = new OneNotePtr(this);
         long reserved;
 
@@ -383,7 +360,8 @@ class OneNotePtr {
         if (data.id == 0) {
             return data;
         }
-        LOG.debug("{}Start Node {} ({}) - Offset={}, End={}", getIndent(), FndStructureConstants.nameOf(data.id), data.id, offset, end);
+        LOG.debug("{}Start Node {} ({}) - Offset={}, End={}", getIndent(),
+                FndStructureConstants.nameOf(data.id), data.id, offset, end);
 
         ++indentLevel;
 
@@ -409,15 +387,16 @@ class OneNotePtr {
             data.gosid = deserializeExtendedGUID();
         } else if (data.id == FndStructureConstants.ObjectGroupEndFND) {
             // no data
-        } else if (data.id == FndStructureConstants.ObjectSpaceManifestRootFND
-            || data.id == FndStructureConstants.ObjectSpaceManifestListStartFND) {
+        } else if (data.id == FndStructureConstants.ObjectSpaceManifestRootFND ||
+                data.id == FndStructureConstants.ObjectSpaceManifestListStartFND) {
             if (data.id == FndStructureConstants.ObjectSpaceManifestRootFND) {
                 data.idDesc = "gosidRoot";
             } else {
                 data.idDesc = "gosid";
             }
-            // Specifies the identity of the object space being specified by this object space manifest list.
-            // MUST match the ObjectSpaceManifestListReferenceFND.gosid field of the FileNode structure that referenced
+            // Specifies the identity of the object space being specified by this object
+            // space manifest list. MUST match the ObjectSpaceManifestListReferenceFND.gosid
+            // field of the FileNode structure that referenced
             // this file node list.
             data.gosid = deserializeExtendedGUID();
             //LOG.debug("{}gosid {}", getIndent(), data.gosid.toString().c_str());
@@ -440,20 +419,22 @@ class OneNotePtr {
             data.idDesc = "rid";
             //LOG.debug("{}gosid {}", getIndent(), data.gosid.toString().c_str());
             data.subType.revisionManifest.ridDependent = deserializeExtendedGUID(); // the rid
-            LOG.debug("{}dependent gosid {}", getIndent(), data.subType.revisionManifest.ridDependent);
+            LOG.debug("{}dependent gosid {}", getIndent(),
+                    data.subType.revisionManifest.ridDependent);
             data.subType.revisionManifest.timeCreation = deserializeLittleEndianLong();
             data.subType.revisionManifest.revisionRole = deserializeLittleEndianInt();
             data.subType.revisionManifest.odcsDefault = deserializeLittleEndianShort();
 
             data.gctxid = ExtendedGUID.nil();
             document.registerRevisionManifest(data);
-        } else if (data.id == FndStructureConstants.RevisionManifestStart6FND
-            || data.id == FndStructureConstants.RevisionManifestStart7FND) {
+        } else if (data.id == FndStructureConstants.RevisionManifestStart6FND ||
+                data.id == FndStructureConstants.RevisionManifestStart7FND) {
             data.gosid = deserializeExtendedGUID(); // the rid
             data.idDesc = "rid";
             //LOG.debug("{}gosid {}", getIndent(), data.gosid.toString().c_str());
             data.subType.revisionManifest.ridDependent = deserializeExtendedGUID(); // the rid
-            LOG.debug("{}dependent gosid {}", getIndent(), data.subType.revisionManifest.ridDependent);
+            LOG.debug("{}dependent gosid {}", getIndent(),
+                    data.subType.revisionManifest.ridDependent);
             data.subType.revisionManifest.revisionRole = deserializeLittleEndianInt();
             data.subType.revisionManifest.odcsDefault = deserializeLittleEndianShort();
 
@@ -461,7 +442,8 @@ class OneNotePtr {
             if (data.id == FndStructureConstants.RevisionManifestStart7FND) {
                 data.gctxid = deserializeExtendedGUID(); // the rid
             }
-            document.registerAdditionalRevisionRole(data.gosid, data.subType.revisionManifest.revisionRole, data.gctxid);
+            document.registerAdditionalRevisionRole(data.gosid,
+                    data.subType.revisionManifest.revisionRole, data.gctxid);
             document.registerRevisionManifest(data);
         } else if (data.id == FndStructureConstants.GlobalIdTableStartFNDX) {
             data.subType.globalIdTableStartFNDX.reserved = deserializeLittleEndianChar();
@@ -471,20 +453,23 @@ class OneNotePtr {
 
             data.subType.globalIdTableEntryFNDX.guid = deserializeGUID();
 
-            document.revisionMap.get(document.currentRevision).globalId.put(data.subType.globalIdTableEntryFNDX.index,
-                data.subType.globalIdTableEntryFNDX.guid);
+            document.revisionMap.get(document.currentRevision).globalId.put(
+                    data.subType.globalIdTableEntryFNDX.index,
+                    data.subType.globalIdTableEntryFNDX.guid);
         } else if (data.id == FndStructureConstants.GlobalIdTableEntry2FNDX) {
             data.subType.globalIdTableEntry2FNDX.indexMapFrom = deserializeLittleEndianInt();
             data.subType.globalIdTableEntry2FNDX.indexMapTo = deserializeLittleEndianInt();
 
             ExtendedGUID dependentRevision =
-                document.revisionMap.get(document.currentRevision).dependent;
+                    document.revisionMap.get(document.currentRevision).dependent;
             // Get the compactId from the revisionMap's globalId map.
-            GUID compactId = document.revisionMap.get(dependentRevision).globalId.get(data.subType.globalIdTableEntry2FNDX.indexMapFrom);
+            GUID compactId = document.revisionMap.get(dependentRevision).globalId.get(
+                    data.subType.globalIdTableEntry2FNDX.indexMapFrom);
             if (compactId == null) {
                 throw new TikaException("COMPACT_ID_MISSING");
             }
-            document.revisionMap.get(document.currentRevision).globalId.put(data.subType.globalIdTableEntry2FNDX.indexMapTo, compactId);
+            document.revisionMap.get(document.currentRevision).globalId.put(
+                    data.subType.globalIdTableEntry2FNDX.indexMapTo, compactId);
         } else if (data.id == FndStructureConstants.GlobalIdTableEntry3FNDX) {
             data.subType.globalIdTableEntry3FNDX.indexCopyFromStart = deserializeLittleEndianInt();
 
@@ -492,18 +477,20 @@ class OneNotePtr {
 
             data.subType.globalIdTableEntry3FNDX.indexCopyToStart = deserializeLittleEndianInt();
 
-            ExtendedGUID dependent_revision = document.revisionMap.get(document.currentRevision).dependent;
+            ExtendedGUID dependent_revision =
+                    document.revisionMap.get(document.currentRevision).dependent;
             for (int i = 0; i < data.subType.globalIdTableEntry3FNDX.entriesToCopy; ++i) {
                 Map<Long, GUID> globalIdMap = document.revisionMap.get(dependent_revision).globalId;
-                GUID compactId = globalIdMap.get(data.subType.globalIdTableEntry3FNDX.indexCopyFromStart + i);
+                GUID compactId = globalIdMap.get(
+                        data.subType.globalIdTableEntry3FNDX.indexCopyFromStart + i);
                 if (compactId == null) {
                     throw new TikaException("COMPACT_ID_MISSING");
                 }
-                document.revisionMap.get(document.currentRevision).globalId.put(data.subType.globalIdTableEntry3FNDX.indexCopyToStart + i
-                    , compactId);
+                document.revisionMap.get(document.currentRevision).globalId.put(
+                        data.subType.globalIdTableEntry3FNDX.indexCopyToStart + i, compactId);
             }
-        } else if (data.id == FndStructureConstants.CanRevise.ObjectRevisionWithRefCountFNDX
-            || data.id == FndStructureConstants.CanRevise.ObjectRevisionWithRefCount2FNDX) {
+        } else if (data.id == FndStructureConstants.CanRevise.ObjectRevisionWithRefCountFNDX ||
+                data.id == FndStructureConstants.CanRevise.ObjectRevisionWithRefCount2FNDX) {
             data.subType.objectRevisionWithRefCountFNDX.oid = deserializeCompactID(); // the oid
 
             if (data.id == FndStructureConstants.CanRevise.ObjectRevisionWithRefCountFNDX) {
@@ -527,20 +514,22 @@ class OneNotePtr {
 
             data.idDesc = "oidRoot";
             data.gosid = data.subType.rootObjectReference.oidRoot.guid;
-            data.subType.rootObjectReference.rootObjectReferenceBase.rootRole = deserializeLittleEndianInt();
+            data.subType.rootObjectReference.rootObjectReferenceBase.rootRole =
+                    deserializeLittleEndianInt();
 
             LOG.debug("{}Root role {}", getIndent(),
-                data.subType.rootObjectReference.rootObjectReferenceBase.rootRole);
+                    data.subType.rootObjectReference.rootObjectReferenceBase.rootRole);
         } else if (data.id == FndStructureConstants.RootObjectReference3FND) {
             data.idDesc = "oidRoot";
             data.gosid = deserializeExtendedGUID();
 
-            data.subType.rootObjectReference.rootObjectReferenceBase.rootRole = deserializeLittleEndianInt();
+            data.subType.rootObjectReference.rootObjectReferenceBase.rootRole =
+                    deserializeLittleEndianInt();
 
             LOG.debug("{}Root role {}", getIndent(),
-                data.subType.rootObjectReference.rootObjectReferenceBase.rootRole);
-        } else if (data.id == FndStructureConstants.RevisionRoleDeclarationFND
-            || data.id == FndStructureConstants.RevisionRoleAndContextDeclarationFND) {
+                    data.subType.rootObjectReference.rootObjectReferenceBase.rootRole);
+        } else if (data.id == FndStructureConstants.RevisionRoleDeclarationFND ||
+                data.id == FndStructureConstants.RevisionRoleAndContextDeclarationFND) {
             data.gosid = deserializeExtendedGUID();
 
             data.subType.revisionRoleDeclaration.revisionRole = deserializeLittleEndianInt();
@@ -550,15 +539,15 @@ class OneNotePtr {
 
             }
             document.registerAdditionalRevisionRole(data.gosid,
-                data.subType.revisionRoleDeclaration.revisionRole,
-                data.gctxid);
+                    data.subType.revisionRoleDeclaration.revisionRole, data.gctxid);
             // FIXME: deal with ObjectDataEncryptionKey
         } else if (data.id == FndStructureConstants.ObjectInfoDependencyOverridesFND) {
             OneNotePtr content = new OneNotePtr(this);
             if (!data.ref.equals(FileChunkReference.nil())) {
                 content.reposition(data.ref); // otherwise it's positioned right at this node
             }
-            data.subType.objectInfoDependencyOverrides.data = content.deserializeObjectInfoDependencyOverrideData();
+            data.subType.objectInfoDependencyOverrides.data =
+                    content.deserializeObjectInfoDependencyOverrideData();
         } else if (data.id == FndStructureConstants.FileDataStoreListReferenceFND) {
             // already processed this
         } else if (data.id == FndStructureConstants.FileDataStoreObjectReferenceFND) {
@@ -570,33 +559,37 @@ class OneNotePtr {
             OneNotePtr fileDataStorePtr = new OneNotePtr(this);
             fileDataStorePtr.reposition(data.ref);
 
-            data.subType.fileDataStoreObjectReference.ref = fileDataStorePtr.deserializeFileDataStoreObject();
-
-        } else if (data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCountFNDX
-            || data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCount2FNDX
-            || data.id == FndStructureConstants.CanRevise.ObjectDeclaration2RefCountFND
-            || data.id == FndStructureConstants.CanRevise.ObjectDeclaration2LargeRefCountFND
-            || data.id == FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2RefCountFND
-            || data.id == FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2LargeRefCountFND) {
-            data.subType.objectDeclarationWithRefCount.body.file_data_store_reference =
-                false;
-            if (data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCountFNDX
-                || data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCount2FNDX) {
-                data.subType.objectDeclarationWithRefCount.body = deserializeObjectDeclarationWithRefCountBody();
+            data.subType.fileDataStoreObjectReference.ref =
+                    fileDataStorePtr.deserializeFileDataStoreObject();
+
+        } else if (data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCountFNDX ||
+                data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCount2FNDX ||
+                data.id == FndStructureConstants.CanRevise.ObjectDeclaration2RefCountFND ||
+                data.id == FndStructureConstants.CanRevise.ObjectDeclaration2LargeRefCountFND ||
+                data.id == FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2RefCountFND ||
+                data.id ==
+                        FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2LargeRefCountFND) {
+            data.subType.objectDeclarationWithRefCount.body.file_data_store_reference = false;
+            if (data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCountFNDX ||
+                    data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCount2FNDX) {
+                data.subType.objectDeclarationWithRefCount.body =
+                        deserializeObjectDeclarationWithRefCountBody();
             } else { // one of the other 4 that use the ObjectDeclaration2Body
-                data.subType.objectDeclarationWithRefCount.body = deserializeObjectDeclaration2Body();
+                data.subType.objectDeclarationWithRefCount.body =
+                        deserializeObjectDeclaration2Body();
             }
-            if (data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCountFNDX
-                || data.id == FndStructureConstants.CanRevise.ObjectDeclaration2RefCountFND
-                || data.id == FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2RefCountFND) {
-                long refCnt = deserializeLittleEndianChar();
-                data.subType.objectDeclarationWithRefCount.cRef = refCnt;
+            if (data.id == FndStructureConstants.CanRevise.ObjectDeclarationWithRefCountFNDX ||
+                    data.id == FndStructureConstants.CanRevise.ObjectDeclaration2RefCountFND ||
+                    data.id ==
+                            FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2RefCountFND) {
+                data.subType.objectDeclarationWithRefCount.cRef = deserializeLittleEndianChar();
             } else {
                 data.subType.objectDeclarationWithRefCount.cRef = deserializeLittleEndianInt();
             }
 
-            if (data.id == FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2RefCountFND
-                || data.id == FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2LargeRefCountFND) {
+            if (data.id == FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2RefCountFND ||
+                    data.id ==
+                            FndStructureConstants.CanRevise.ReadOnlyObjectDeclaration2LargeRefCountFND) {
                 ByteBuffer md5Buffer = ByteBuffer.allocate(16);
                 deserializeBytes(md5Buffer);
                 data.subType.objectDeclarationWithRefCount.readOnly.md5 = md5Buffer.array();
@@ -605,9 +598,11 @@ class OneNotePtr {
             postprocessObjectDeclarationContents(data, curPath);
 
             LOG.debug("{}Ref Count JCID {}", getIndent(),
-                data.subType.objectDeclarationWithRefCount.body.jcid);
-        } else if (data.id == FndStructureConstants.CanRevise.ObjectDeclarationFileData3RefCountFND
-            || data.id == FndStructureConstants.CanRevise.ObjectDeclarationFileData3LargeRefCountFND) {
+                    data.subType.objectDeclarationWithRefCount.body.jcid);
+        } else if (
+                data.id == FndStructureConstants.CanRevise.ObjectDeclarationFileData3RefCountFND ||
+                        data.id ==
+                                FndStructureConstants.CanRevise.ObjectDeclarationFileData3LargeRefCountFND) {
             data.subType.objectDeclarationWithRefCount.body.oid = deserializeCompactID();
 
             long jcid = deserializeLittleEndianInt();
@@ -624,22 +619,26 @@ class OneNotePtr {
 
             long roomLeftLong = roomLeft();
             if (cch > roomLeftLong) { // not a valid guid
-                throw new TikaException("Data out of bounds - cch " + cch + " is > room left = " + roomLeftLong);
+                throw new TikaException(
+                        "Data out of bounds - cch " + cch + " is > room left = " + roomLeftLong);
             }
 
             if (cch > dif.size()) {
-                throw new TikaMemoryLimitException("CCH=" + cch + " was found that was greater" +
-                        " than file size " + dif.size());
+                throw new TikaMemoryLimitException(
+                        "CCH=" + cch + " was found that was greater" + " than file size " +
+                                dif.size());
             }
             ByteBuffer dataSpaceBuffer = ByteBuffer.allocate((int) cch * 2);
             dif.read(dataSpaceBuffer);
             byte[] dataSpaceBufferBytes = dataSpaceBuffer.array();
             offset += dataSpaceBufferBytes.length;
             if (dataSpaceBufferBytes.length == (IFNDF_GUID_LENGTH * 2 + IFNDF.length) &&
-                Arrays.equals(IFNDF, Arrays.copyOfRange(dataSpaceBufferBytes, 0, IFNDF.length))) {
+                    Arrays.equals(IFNDF,
+                            Arrays.copyOfRange(dataSpaceBufferBytes, 0, IFNDF.length))) {
                 data.subType.objectDeclarationWithRefCount.body.file_data_store_reference = true;
-                GUID guid = GUID.fromCurlyBraceUTF16Bytes(Arrays.copyOfRange(dataSpaceBufferBytes, IFNDF.length,
-                    dataSpaceBufferBytes.length));
+                GUID guid = GUID.fromCurlyBraceUTF16Bytes(
+                        Arrays.copyOfRange(dataSpaceBufferBytes, IFNDF.length,
+                                dataSpaceBufferBytes.length));
                 ExtendedGUID extendedGUID = new ExtendedGUID(guid, 0);
                 FileChunkReference fileChunk = document.getAssocGuidToRef(extendedGUID);
                 if (fileChunk == null) {
@@ -648,7 +647,8 @@ class OneNotePtr {
                     // TODO - call postprocessObjectDeclarationContents on this object?
                 }
             } else {
-                LOG.debug("{}Ignoring an external reference {}", getIndent(), new String(dataSpaceBufferBytes, StandardCharsets.UTF_16LE));
+                LOG.debug("{}Ignoring an external reference {}", getIndent(),
+                        new String(dataSpaceBufferBytes, StandardCharsets.UTF_16LE));
             }
         } else if (data.id == FndStructureConstants.ObjectGroupListReferenceFND) {
             data.idDesc = "object_group_id";
@@ -670,7 +670,9 @@ class OneNotePtr {
             Revision currentRevision = document.revisionMap.get(document.currentRevision);
             currentRevision.manifestList.add(curPath);
         } else {
-            LOG.debug("No fnd needed to be parsed for data.id=0x" + Long.toHexString(data.id) + " (" + FndStructureConstants.nameOf(data.id) + ")");
+            LOG.debug(
+                    "No fnd needed to be parsed for data.id=0x" + Long.toHexString(data.id) + " (" +
+                            FndStructureConstants.nameOf(data.id) + ")");
         }
         if (data.baseType == 2) {
             // Generic baseType == 2 parser - means we have children to parse.
@@ -699,12 +701,12 @@ class OneNotePtr {
         }
         --indentLevel;
         if (data.gosid.equals(ExtendedGUID.nil())) {
-            LOG.debug("{}End Node {} ({}) - Offset={}, End={}", getIndent(), FndStructureConstants.nameOf(data.id), (int) data.id, offset
-                , end);
+            LOG.debug("{}End Node {} ({}) - Offset={}, End={}", getIndent(),
+                    FndStructureConstants.nameOf(data.id), (int) data.id, offset, end);
         } else {
-            LOG.debug("{}End Node {} ({}) {}:[{}] - Offset={}, End={}", getIndent(), FndStructureConstants.nameOf(data.id), (int) data.id
-                , data.idDesc,
-                data.gosid, offset, end);
+            LOG.debug("{}End Node {} ({}) {}:[{}] - Offset={}, End={}", getIndent(),
+                    FndStructureConstants.nameOf(data.id), (int) data.id, data.idDesc, data.gosid,
+                    offset, end);
         }
         return data;
     }
@@ -717,7 +719,8 @@ class OneNotePtr {
         offset = dif.position();
     }
 
-    private ObjectDeclarationWithRefCountBody deserializeObjectDeclarationWithRefCountBody() throws IOException, TikaException {
+    private ObjectDeclarationWithRefCountBody deserializeObjectDeclarationWithRefCountBody()
+            throws IOException, TikaException {
         ObjectDeclarationWithRefCountBody data = new ObjectDeclarationWithRefCountBody();
         data.oid = deserializeCompactID();
         long jci_odcs_etc = deserializeLittleEndianInt();
@@ -738,7 +741,8 @@ class OneNotePtr {
         return data;
     }
 
-    private ObjectDeclarationWithRefCountBody deserializeObjectDeclaration2Body() throws IOException, TikaException {
+    private ObjectDeclarationWithRefCountBody deserializeObjectDeclaration2Body()
+            throws IOException, TikaException {
         ObjectDeclarationWithRefCountBody data = new ObjectDeclarationWithRefCountBody();
         data.oid = deserializeCompactID();
         long jcid = deserializeLittleEndianInt();
@@ -775,7 +779,8 @@ class OneNotePtr {
         data.fileData.cb = len;
         offset += len;
         while ((offset & 0x7) > 0) {
-            // Padding is added to the end of the FileData stream to ensure that it ends on an 8-byte boundary.
+            // Padding is added to the end of the FileData stream to ensure that it
+            // ends on an 8-byte boundary.
             ++offset;
         }
         GUID footer = deserializeGUID();
@@ -786,8 +791,10 @@ class OneNotePtr {
         return data;
     }
 
-    private ObjectInfoDependencyOverrideData deserializeObjectInfoDependencyOverrideData() throws IOException {
-        ObjectInfoDependencyOverrideData objectInfoDependencyOverrideData = new ObjectInfoDependencyOverrideData();
+    private ObjectInfoDependencyOverrideData deserializeObjectInfoDependencyOverrideData()
+            throws IOException {
+        ObjectInfoDependencyOverrideData objectInfoDependencyOverrideData =
+                new ObjectInfoDependencyOverrideData();
         long num_8bit_overrides = deserializeLittleEndianInt();
         long num_32bit_overrides = deserializeLittleEndianInt();
         long crc = deserializeLittleEndianInt();
@@ -837,33 +844,42 @@ class OneNotePtr {
      * Depending on stpFormat and cbFormat, will deserialize a FileChunkReference.
      *
      * @param stpFormat An unsigned integer that specifies the size and format of the
-     *                  FileNodeChunkReference.stp field specified by the fnd field if this FileNode structure has a
-     *                  value of the BaseType field equal to 1 or 2. MUST be ignored if the value of the BaseType field
-     *                  of this FileNode structure is equal to 0. The meaning of the StpFormat field is given by the
+     *                  FileNodeChunkReference.stp field specified by the fnd field if this
+     *                  FileNode structure has a
+     *                  value of the BaseType field equal to 1 or 2. MUST be ignored if the
+     *                  value of the BaseType field
+     *                  of this FileNode structure is equal to 0. The meaning of the StpFormat
+     *                  field is given by the
      *                  following table.
      *                  Value Meaning
      *                  0 8 bytes, uncompressed.
      *                  1 4 bytes, uncompressed.
      *                  2 2 bytes, compressed.
      *                  3 4 bytes, compressed.
-     *                  The value of an uncompressed file pointer specifies a location in the file. To uncompress a
+     *                  The value of an uncompressed file pointer specifies a location in the
+     *                  file. To uncompress a
      *                  compressed file pointer, multiply the value by 8.
      * @param cbFormat  An unsigned integer that specifies the size and format of the
-     *                  FileNodeChunkReference.cb field specified by the fnd field if this FileNode structure has a
-     *                  BaseType field value equal to 1 or 2. MUST be 0 and MUST be ignored if BaseType of this
-     *                  FileNode structure is equal to 0. The meaning of CbFormat is given by the following table.
+     *                  FileNodeChunkReference.cb field specified by the fnd field if this
+     *                  FileNode structure has a
+     *                  BaseType field value equal to 1 or 2. MUST be 0 and MUST be ignored if
+     *                  BaseType of this
+     *                  FileNode structure is equal to 0. The meaning of CbFormat is given by
+     *                  the following table.
      *                  Value Meaning
      *                  0 4 bytes, uncompressed.
      *                  1 8 bytes, uncompressed.
      *                  2 1 byte, compressed.
      *                  3 2 bytes, compressed.
-     *                  The value of an uncompressed byte count specifies the size, in bytes, of the data referenced by a
+     *                  The value of an uncompressed byte count specifies the size, in bytes, of
+     *                  the data referenced by a
      *                  FileNodeChunkReference structure. To uncompress a compressed byte count,
      *                  multiply the value by 8.
      * @return
      * @throws IOException
      */
-    FileChunkReference deserializeVarFileChunkReference(long stpFormat, long cbFormat) throws IOException, TikaException {
+    FileChunkReference deserializeVarFileChunkReference(long stpFormat, long cbFormat)
+            throws IOException, TikaException {
         FileChunkReference data = new FileChunkReference(0, 0);
         long local8;
         long local16;
@@ -914,13 +930,14 @@ class OneNotePtr {
         return data;
     }
 
-    FileNodeListHeader deserializeFileNodeListHeader() throws IOException {
+    FileNodeListHeader deserializeFileNodeListHeader() throws TikaException, IOException {
         long positionOfThisHeader = offset;
         long uintMagic = deserializeLittleEndianLong();
         long fileNodeListId = deserializeLittleEndianInt();
         long nFragmentSequence = deserializeLittleEndianInt();
 
-        return new FileNodeListHeader(positionOfThisHeader, uintMagic, fileNodeListId, nFragmentSequence);
+        return new FileNodeListHeader(positionOfThisHeader, uintMagic, fileNodeListId,
+                nFragmentSequence);
     }
 
     /**
@@ -931,16 +948,18 @@ class OneNotePtr {
      * @param curPtr The current pointer.
      * @throws IOException
      */
-    private void postprocessObjectDeclarationContents(FileNode data, FileNodePtr curPtr) throws IOException, TikaException {
+    private void postprocessObjectDeclarationContents(FileNode data, FileNodePtr curPtr)
+            throws IOException, TikaException {
         data.gosid = data.subType.objectDeclarationWithRefCount.body.oid.guid;
         document.guidToObject.put(data.gosid, new FileNodePtr(curPtr));
         if (data.subType.objectDeclarationWithRefCount.body.jcid.isObjectSpaceObjectPropSet()) {
             OneNotePtr objectSpacePropSetPtr = new OneNotePtr(this);
             objectSpacePropSetPtr.reposition(data.ref);
-            data.subType.objectDeclarationWithRefCount.objectRef = objectSpacePropSetPtr.deserializeObjectSpaceObjectPropSet();
+            data.subType.objectDeclarationWithRefCount.objectRef =
+                    objectSpacePropSetPtr.deserializeObjectSpaceObjectPropSet();
             ObjectStreamCounters streamCounters = new ObjectStreamCounters();
             data.propertySet = objectSpacePropSetPtr.deserializePropertySet(streamCounters,
-                data.subType.objectDeclarationWithRefCount.objectRef);
+                    data.subType.objectDeclarationWithRefCount.objectRef);
         } else {
             if (!data.subType.objectDeclarationWithRefCount.body.jcid.isFileData) {
                 throw new TikaException("JCID must be file data when !isObjectSpaceObjectPropSet.");
@@ -957,29 +976,32 @@ class OneNotePtr {
         }
     }
 
-    private PropertySet deserializePropertySet(ObjectStreamCounters counters, ObjectSpaceObjectPropSet streams) throws IOException,
-        TikaException {
+    private PropertySet deserializePropertySet(ObjectStreamCounters counters,
+                                               ObjectSpaceObjectPropSet streams)
+            throws IOException, TikaException {
         PropertySet data = new PropertySet();
         long count = deserializeLittleEndianShort();
-        data.rgPridsData = Stream.generate(PropertyValue::new)
-            .limit((int) count)
-            .collect(Collectors.toList());
+        data.rgPridsData =
+                Stream.generate(PropertyValue::new).limit((int) count).collect(Collectors.toList());
         for (int i = 0; i < count; ++i) {
             data.rgPridsData.get(i).propertyId = deserializePropertyID();
             LOG.debug("{}Property {}", getIndent(), data.rgPridsData.get(i).propertyId);
         }
         LOG.debug("{}{} elements in property set:", getIndent(), count);
         for (int i = 0; i < count; ++i) {
-            data.rgPridsData.set(i, deserializePropertyValueFromPropertyID(
-                data.rgPridsData.get(i).propertyId, streams, counters));
+            data.rgPridsData.set(i,
+                    deserializePropertyValueFromPropertyID(data.rgPridsData.get(i).propertyId,
+                            streams, counters));
         }
         LOG.debug("");
         return data;
 
     }
 
-    private PropertyValue deserializePropertyValueFromPropertyID(OneNotePropertyId propertyID, ObjectSpaceObjectPropSet streams,
-                                                                 ObjectStreamCounters counters) throws IOException, TikaException {
+    private PropertyValue deserializePropertyValueFromPropertyID(OneNotePropertyId propertyID,
+                                                                 ObjectSpaceObjectPropSet streams,
+                                                                 ObjectStreamCounters counters)
+            throws IOException, TikaException {
         PropertyValue data = new PropertyValue();
         data.propertyId = propertyID;
         char val8;
@@ -1022,20 +1044,25 @@ class OneNotePtr {
                     LOG.debug(" PropertyID long({})", data.scalar);
                     break;
                 case 0x7:
-                    // If the value of the PropertyID.type element is "0x7" and the property specifies an array of elements, the value of
+                    // If the value of the PropertyID.type element is "0x7" and the property
+                    // specifies an array of elements, the value of
                     // the
-                    // prtFourBytesOfLengthFollowedByData.cb element MUST be the sum of the sizes, in bytes, of each element in the array.
+                    // prtFourBytesOfLengthFollowedByData.cb element MUST be the sum of the
+                    // sizes, in bytes, of each element in the array.
                     // Exceptions include:
-                    // * The RgOutlineIndentDistance element, where the value of the prtFourBytesOfLengthFollowedByData.cb element
+                    // * The RgOutlineIndentDistance element, where the value of the
+                    // prtFourBytesOfLengthFollowedByData.cb element
                     // MUST be: 4 + (4 × RgOutlineIndentDistance.count).
-                    // * The TableColumnsLocked element, where the value of the prtFourBytesOfLengthFollowedByData.cb
+                    // * The TableColumnsLocked element, where the value of the
+                    // prtFourBytesOfLengthFollowedByData.cb
                     // element MUST be: 1 + (TableColumnsLocked.cColumns + 7) / 8.
-                    // * The TableColumnWidths element, where the value of the prtFourBytesOfLengthFollowedByData.cb
+                    // * The TableColumnWidths element, where the value of the
+                    // prtFourBytesOfLengthFollowedByData.cb
                     // element MUST be: 1 + (4 × TableColumnWidths.cColumns).
 
                     val32 = deserializeLittleEndianInt();
                     LOG.debug(" raw data: ({})[", val32);
-                {
+
                     data.rawData.stp = offset;
                     data.rawData.cb = 0;
                     if (offset + val32 > end) {
@@ -1050,9 +1077,9 @@ class OneNotePtr {
                         content.reposition(data.rawData);
                         content.dumpHex();
                     }
-                }
-                LOG.debug("]");
-                break;
+
+                    LOG.debug("]");
+                    break;
                 case 0x9:
                 case 0xb:
                 case 0xd:
@@ -1061,11 +1088,9 @@ class OneNotePtr {
                 case 0x8:
                 case 0xa:
                 case 0xc:
-                    if (type == 0x8 || type == 0xa
-                        || type == 0xc) {
+                    if (type == 0x8 || type == 0xa || type == 0xc) {
                         val32 = 1;
                     }
-                {
                     List<CompactID> stream = streams.contextIDs.data;
                     String xtype = "contextID";
                     long s_count = counters.context_ids_count;
@@ -1084,30 +1109,29 @@ class OneNotePtr {
                         if (index < stream.size()) {
                             data.compactIDs.add(stream.get(index));
                             LOG.debug(" {}[{}]", xtype,
-                                data.compactIDs.get(data.compactIDs.size() - 1));
+                                    data.compactIDs.get(data.compactIDs.size() - 1));
                         } else {
                             throw new TikaException("SEGV");
                         }
                     }
-                }
-                break;
+                    break;
                 case 0x10:
                     val32 = deserializeLittleEndianInt();
-                {
                     OneNotePropertyId propId = deserializePropertyID();
                     LOG.debug(" UnifiedSubPropertySet {} {}", val32, propId);
-                    data.propertySet.rgPridsData = Stream.generate(PropertyValue::new)
-                        .limit((int) val32)
-                        .collect(Collectors.toList());
+                    data.propertySet.rgPridsData =
+                            Stream.generate(PropertyValue::new).limit((int) val32)
+                                    .collect(Collectors.toList());
                     for (int i = 0; i < val32; ++i) {
                         try {
-                            data.propertySet.rgPridsData.set(i, deserializePropertyValueFromPropertyID(propId, streams, counters));
+                            data.propertySet.rgPridsData.set(i,
+                                    deserializePropertyValueFromPropertyID(propId, streams,
+                                            counters));
                         } catch (IOException e) {
                             return data;
                         }
                     }
-                }
-                break;
+                    break;
                 case 0x11:
                     LOG.debug(" SubPropertySet");
                     data.propertySet = deserializePropertySet(counters, streams);
@@ -1122,12 +1146,13 @@ class OneNotePtr {
         }
     }
 
-    private OneNotePropertyId deserializePropertyID() throws IOException {
+    private OneNotePropertyId deserializePropertyID() throws TikaException, IOException {
         long pid = deserializeLittleEndianInt();
         return new OneNotePropertyId(pid);
     }
 
-    private ObjectSpaceObjectPropSet deserializeObjectSpaceObjectPropSet() throws IOException, TikaException {
+    private ObjectSpaceObjectPropSet deserializeObjectSpaceObjectPropSet()
+            throws IOException, TikaException {
         ObjectSpaceObjectPropSet data = new ObjectSpaceObjectPropSet();
         data.osids.extendedStreamsPresent = 0;
         data.osids.osidsStreamNotPresent = 1;
@@ -1147,19 +1172,18 @@ class OneNotePtr {
         return data;
     }
 
-    private ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs deserializeObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs() throws IOException
-        , TikaException {
-        ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs data = new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
+    private ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs deserializeObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs()
+            throws IOException, TikaException {
+        ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs data =
+                new ObjectSpaceObjectStreamOfOIDsOSIDsOrContextIDs();
         long header = deserializeLittleEndianInt();
         data.count = header & 0xffffff;
         data.osidsStreamNotPresent = ((header >> 31) & 0x1);
         data.extendedStreamsPresent = ((header >> 30) & 0x1);
         if (LOG.isDebugEnabled()) {
-            LOG.debug(
-                "{}Deserialized Stream Header count: {} OsidsNotPresent {} Extended {}",
-                getIndent(), data.count,
-                data.osidsStreamNotPresent,
-                data.extendedStreamsPresent);
+            LOG.debug("{}Deserialized Stream Header count: {} OsidsNotPresent {} Extended {}",
+                    getIndent(), data.count, data.osidsStreamNotPresent,
+                    data.extendedStreamsPresent);
         }
         for (int i = 0; i < data.count; ++i) {
             CompactID cid;
@@ -1175,8 +1199,9 @@ class OneNotePtr {
 
     public void dumpHex() throws TikaMemoryLimitException, IOException {
         if (end - offset > dif.size()) {
-            throw new TikaMemoryLimitException("Exceeded memory limit when trying to dumpHex - " +
-                    "" + (end - offset) + " > " + dif.size());
+            throw new TikaMemoryLimitException(
+                    "Exceeded memory limit when trying to dumpHex - " + "" + (end - offset) +
+                            " > " + dif.size());
         }
         ByteBuffer byteBuffer = ByteBuffer.allocate((int) (end - offset));
         LOG.debug(Hex.encodeHexString(byteBuffer.array()));
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalker.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalker.java
index 5553bf0..2190b1f 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalker.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalker.java
@@ -14,21 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.tika.exception.TikaException;
-import org.apache.tika.exception.TikaMemoryLimitException;
-import org.apache.tika.extractor.EmbeddedDocumentExtractor;
-import org.apache.tika.extractor.EmbeddedDocumentUtil;
-import org.apache.tika.io.TikaInputStream;
-import org.apache.tika.metadata.Metadata;
-import org.apache.tika.parser.ParseContext;
-import org.apache.tika.sax.EmbeddedContentHandler;
-import org.apache.tika.sax.XHTMLContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
+package org.apache.tika.parser.microsoft.onenote;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -47,6 +34,20 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.exception.TikaMemoryLimitException;
+import org.apache.tika.extractor.EmbeddedDocumentExtractor;
+import org.apache.tika.extractor.EmbeddedDocumentUtil;
+import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.EmbeddedContentHandler;
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
 /**
  * Walk the one note tree and create a Map while it goes.
  * Also writes user input text to a print writer as it parses.
@@ -54,39 +55,43 @@ import java.util.regex.Pattern;
 class OneNoteTreeWalker {
 
     private static final String P = "p";
-    private static Pattern HYPERLINK_PATTERN = Pattern.compile("\uFDDFHYPERLINK\\s+\"([^\"]+)\"([^\"]+)$");
-
     /**
      * See spec MS-ONE - 2.3.1 - TIME32 - epoch of jan 1 1980 UTC.
-     * So we create this offset used to calculate number of seconds between this and the Instant.EPOCH.
+     * So we create this offset used to calculate number of seconds between this and the Instant
+     * .EPOCH.
      */
     private static final long TIME32_EPOCH_DIFF_1980;
+    /**
+     * See spec MS-DTYP - 2.3.3 - DATETIME dates are based on epoch of jan 1 1601 UTC.
+     * So we create this offset used to calculate number of seconds between this and the Instant
+     * .EPOCH.
+     */
+    private static final long DATETIME_EPOCH_DIFF_1601;
+    private static final Pattern HYPERLINK_PATTERN =
+            Pattern.compile("\uFDDFHYPERLINK\\s+\"([^\"]+)\"([^\"]+)$");
+
     static {
         LocalDateTime time32Epoch1980 = LocalDateTime.of(1980, Month.JANUARY, 1, 0, 0);
         Instant instant = time32Epoch1980.atZone(ZoneOffset.UTC).toInstant();
         TIME32_EPOCH_DIFF_1980 = (instant.toEpochMilli() - Instant.EPOCH.toEpochMilli()) / 1000;
     }
-    /**
-     * See spec MS-DTYP - 2.3.3 - DATETIME dates are based on epoch of jan 1 1601 UTC.
-     * So we create this offset used to calculate number of seconds between this and the Instant.EPOCH.
-     */
-    private static final long DATETIME_EPOCH_DIFF_1601;
+
     static {
         LocalDateTime time32Epoch1601 = LocalDateTime.of(1601, Month.JANUARY, 1, 0, 0);
         Instant instant = time32Epoch1601.atZone(ZoneOffset.UTC).toInstant();
         DATETIME_EPOCH_DIFF_1601 = (instant.toEpochMilli() - Instant.EPOCH.toEpochMilli()) / 1000;
     }
 
-    private OneNoteTreeWalkerOptions options;
-    private OneNoteDocument oneNoteDocument;
-    private OneNoteDirectFileResource dif;
-    private XHTMLContentHandler xhtml;
-    private Pair<Long, ExtendedGUID> roleAndContext;
     private final Metadata parentMetadata;
     private final EmbeddedDocumentExtractor embeddedDocumentExtractor;
     private final Set<String> authors = new HashSet<>();
     private final Set<String> mostRecentAuthors = new HashSet<>();
     private final Set<String> originalAuthors = new HashSet<>();
+    private final OneNoteTreeWalkerOptions options;
+    private final OneNoteDocument oneNoteDocument;
+    private final OneNoteDirectFileResource dif;
+    private final XHTMLContentHandler xhtml;
+    private final Pair<Long, ExtendedGUID> roleAndContext;
     private Instant lastModifiedTimestamp = Instant.MIN;
     private long creationTimestamp = Long.MAX_VALUE;
     private long lastModified = Long.MIN_VALUE;
@@ -98,21 +103,25 @@ class OneNoteTreeWalker {
      *
      * @param options         The options for how to walk this tree.
      * @param oneNoteDocument The one note document we want to walk.
-     * @param dif             The random file access structure we read and reposition while extracting the content.
+     * @param dif             The rando  file access structure we read and reposition while
+     *                        extracting the content.
      * @param xhtml           The XHTMLContentHandler to populate as you walk the tree.
-     * @param roleAndContext  The role and context value we want to use when crawling. Set this to null if you are
+     * @param roleAndContext  The role  nd context value we want to use when crawling. Set this
+     *                        to null if you are
      *                        crawling all root file nodes, and don't care about revisions.
      */
     public OneNoteTreeWalker(OneNoteTreeWalkerOptions options, OneNoteDocument oneNoteDocument,
                              OneNoteDirectFileResource dif, XHTMLContentHandler xhtml,
-                             Metadata parentMetadata, ParseContext parseContext, Pair<Long, ExtendedGUID> roleAndContext) {
+                             Metadata parentMetadata, ParseContext parseContext,
+                             Pair<Long, ExtendedGUID> roleAndContext) {
         this.options = options;
         this.oneNoteDocument = oneNoteDocument;
         this.dif = dif;
         this.roleAndContext = roleAndContext;
         this.xhtml = xhtml;
         this.parentMetadata = parentMetadata;
-        this.embeddedDocumentExtractor = EmbeddedDocumentUtil.getEmbeddedDocumentExtractor(parseContext);
+        this.embeddedDocumentExtractor =
+                EmbeddedDocumentUtil.getEmbeddedDocumentExtractor(parseContext);
     }
 
     /**
@@ -129,12 +138,14 @@ class OneNoteTreeWalker {
     }
 
     /**
-     * Walk the root file nodes, depending on the options will crawl revisions or the entire revision tree.
+     * Walk the root file nodes, depending on the options will crawl revisions or the entire
+     * revision tree.
      *
      * @return List of the root file nodes.
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
-    public List<Map<String, Object>> walkRootFileNodes() throws IOException, TikaException, SAXException {
+    public List<Map<String, Object>> walkRootFileNodes()
+            throws IOException, TikaException, SAXException {
         List<Map<String, Object>> res = new ArrayList<>();
         if (options.isCrawlAllFileNodesFromRoot()) {
             res.add(walkFileNodeList(oneNoteDocument.root));
@@ -143,7 +154,8 @@ class OneNoteTreeWalker {
                 Map<String, Object> structure = new HashMap<>();
                 structure.put("oneNoteType", "Revision");
                 structure.put("revisionListGuid", revisionListGuid.toString());
-                FileNodePtr fileNodePtr = oneNoteDocument.revisionManifestLists.get(revisionListGuid);
+                FileNodePtr fileNodePtr =
+                        oneNoteDocument.revisionManifestLists.get(revisionListGuid);
                 structure.put("fileNode", walkRevision(fileNodePtr));
                 res.add(structure);
             }
@@ -170,7 +182,8 @@ class OneNoteTreeWalker {
      * @return A map of the parsed data.
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
-    private Map<String, Object> walkRevision(FileNodePtr fileNodePtr) throws IOException, TikaException, SAXException {
+    private Map<String, Object> walkRevision(FileNodePtr fileNodePtr)
+            throws IOException, TikaException, SAXException {
         Map<String, Object> structure = new HashMap<>();
         structure.put("oneNoteType", "FileNodePointer");
         structure.put("offsets", fileNodePtr.nodeListPositions);
@@ -197,22 +210,24 @@ class OneNoteTreeWalker {
         boolean okGroup = false;
         for (FileNode child : revisionFileNode.childFileNodeList.children) {
             if (child.id == FndStructureConstants.RevisionManifestStart4FND ||
-              child.id == FndStructureConstants.RevisionManifestStart6FND ||
-              child.id == FndStructureConstants.RevisionManifestStart7FND) {
+                    child.id == FndStructureConstants.RevisionManifestStart6FND ||
+                    child.id == FndStructureConstants.RevisionManifestStart7FND) {
                 okGroup = validRevisions.contains(child.gosid);
             }
             if (okGroup) {
                 if ((child.id == FndStructureConstants.RootObjectReference2FNDX ||
-                  child.id == FndStructureConstants.RootObjectReference3FND) &&
-                  child.subType.rootObjectReference.rootObjectReferenceBase.rootRole == 1) {
-                    FileNodePtr childFileNodePointer = oneNoteDocument.guidToObject.get(child.gosid);
+                        child.id == FndStructureConstants.RootObjectReference3FND) &&
+                        child.subType.rootObjectReference.rootObjectReferenceBase.rootRole == 1) {
+                    FileNodePtr childFileNodePointer =
+                            oneNoteDocument.guidToObject.get(child.gosid);
                     children.add(walkFileNodePtr(childFileNodePointer));
                 }
             }
         }
         if (!children.isEmpty()) {
             Map<String, Object> childFileNodeListMap = new HashMap<>();
-            childFileNodeListMap.put("fileNodeListHeader", revisionFileNode.childFileNodeList.fileNodeListHeader);
+            childFileNodeListMap.put("fileNodeListHeader",
+                    revisionFileNode.childFileNodeList.fileNodeListHeader);
             childFileNodeListMap.put("children", children);
             structure.put("revisionFileNodeList", childFileNodeListMap);
         }
@@ -226,7 +241,8 @@ class OneNoteTreeWalker {
      * @return Returns a map of the main data.
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
-    public Map<String, Object> walkFileNodePtr(FileNodePtr fileNodePtr) throws IOException, TikaException, SAXException {
+    public Map<String, Object> walkFileNodePtr(FileNodePtr fileNodePtr)
+            throws IOException, TikaException, SAXException {
         if (fileNodePtr != null) {
             FileNode fileNode = fileNodePtr.dereference(oneNoteDocument);
             return walkFileNode(fileNode);
@@ -241,7 +257,8 @@ class OneNoteTreeWalker {
      * @return The result.
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
-    public Map<String, Object> walkFileNodeList(FileNodeList fileNodeList) throws IOException, TikaException, SAXException {
+    public Map<String, Object> walkFileNodeList(FileNodeList fileNodeList)
+            throws IOException, TikaException, SAXException {
         Map<String, Object> structure = new HashMap<>();
         structure.put("oneNoteType", "FileNodeList");
         structure.put("fileNodeListHeader", fileNodeList.fileNodeListHeader);
@@ -262,7 +279,8 @@ class OneNoteTreeWalker {
      * @return Map which is result of the parsed file node.
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
-    public Map<String, Object> walkFileNode(FileNode fileNode) throws IOException, TikaException, SAXException {
+    public Map<String, Object> walkFileNode(FileNode fileNode)
+            throws IOException, TikaException, SAXException {
         Map<String, Object> structure = new HashMap<>();
         structure.put("oneNoteType", "FileNode");
         structure.put("gosid", fileNode.gosid.toString());
@@ -272,7 +290,8 @@ class OneNoteTreeWalker {
         structure.put("fileNodeBaseType", "0x" + Long.toHexString(fileNode.baseType));
         structure.put("isFileData", fileNode.isFileData);
         structure.put("idDesc", fileNode.idDesc);
-        if (fileNode.childFileNodeList != null && fileNode.childFileNodeList.fileNodeListHeader != null) {
+        if (fileNode.childFileNodeList != null &&
+                fileNode.childFileNodeList.fileNodeListHeader != null) {
             structure.put("childFileNodeList", walkFileNodeList(fileNode.childFileNodeList));
         }
         if (fileNode.propertySet != null) {
@@ -281,10 +300,10 @@ class OneNoteTreeWalker {
                 structure.put("propertySet", propSet);
             }
         }
-        if (fileNode.subType.fileDataStoreObjectReference.ref != null &&
-          !FileChunkReference.nil().equals(fileNode.subType.fileDataStoreObjectReference.ref.fileData)) {
-            structure.put("fileDataStoreObjectReference",
-                    walkFileDataStoreObjectReference(fileNode.subType.fileDataStoreObjectReference));
+        if (fileNode.subType.fileDataStoreObjectReference.ref != null && !FileChunkReference.nil()
+                .equals(fileNode.subType.fileDataStoreObjectReference.ref.fileData)) {
+            structure.put("fileDataStoreObjectReference", walkFileDataStoreObjectReference(
+                    fileNode.subType.fileDataStoreObjectReference));
         }
         return structure;
     }
@@ -297,21 +316,22 @@ class OneNoteTreeWalker {
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
     private Map<String, Object> walkFileDataStoreObjectReference(
-            FileDataStoreObjectReference fileDataStoreObjectReference) throws IOException, SAXException, TikaException {
+            FileDataStoreObjectReference fileDataStoreObjectReference)
+            throws IOException, SAXException, TikaException {
         Map<String, Object> structure = new HashMap<>();
         OneNotePtr content = new OneNotePtr(oneNoteDocument, dif);
         content.reposition(fileDataStoreObjectReference.ref.fileData);
         if (fileDataStoreObjectReference.ref.fileData.cb > dif.size()) {
-            throw new TikaMemoryLimitException("File data store cb " +
-                    fileDataStoreObjectReference.ref.fileData.cb +
-              " exceeds document size: " + dif.size());
+            throw new TikaMemoryLimitException(
+                    "File data store cb " + fileDataStoreObjectReference.ref.fileData.cb +
+                            " exceeds document size: " + dif.size());
         }
-        handleEmbedded((int)fileDataStoreObjectReference.ref.fileData.cb);
+        handleEmbedded((int) fileDataStoreObjectReference.ref.fileData.cb);
         structure.put("fileDataStoreObjectMetadata", fileDataStoreObjectReference);
         return structure;
     }
 
-    private void handleEmbedded(int  length) throws TikaException, IOException, SAXException {
+    private void handleEmbedded(int length) throws TikaException, IOException, SAXException {
         TikaInputStream stream = null;
         ByteBuffer buf = null;
         try {
@@ -325,9 +345,7 @@ class OneNoteTreeWalker {
         Metadata embeddedMetadata = new Metadata();
         try {
             stream = TikaInputStream.get(buf.array());
-            embeddedDocumentExtractor.parseEmbedded(
-                    stream,
-                    new EmbeddedContentHandler(xhtml),
+            embeddedDocumentExtractor.parseEmbedded(stream, new EmbeddedContentHandler(xhtml),
                     embeddedMetadata, false);
             AttributesImpl attributes = new AttributesImpl();
             attributes.addAttribute("", "class", "class", "CDATA", "embedded");
@@ -344,8 +362,8 @@ class OneNoteTreeWalker {
      * @return
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
-    private List<Map<String, Object>> processPropertySet(PropertySet propertySet) throws IOException, TikaException,
-      SAXException {
+    private List<Map<String, Object>> processPropertySet(PropertySet propertySet)
+            throws IOException, TikaException, SAXException {
         List<Map<String, Object>> propValues = new ArrayList<>();
         for (PropertyValue propertyValue : propertySet.rgPridsData) {
             propValues.add(processPropertyValue(propertyValue));
@@ -361,21 +379,22 @@ class OneNoteTreeWalker {
      */
     private boolean propertyIsBinary(OneNotePropertyEnum property) {
         return property == OneNotePropertyEnum.RgOutlineIndentDistance ||
-          property == OneNotePropertyEnum.NotebookManagementEntityGuid ||
-          property == OneNotePropertyEnum.RichEditTextUnicode;
+                property == OneNotePropertyEnum.NotebookManagementEntityGuid ||
+                property == OneNotePropertyEnum.RichEditTextUnicode;
     }
 
     /**
      * Process a property value and populate a map containing all the property value data.
      * <p>
-     * Parse out any relevant text and write it to the print writer as well for easy search engine parsing.
+     * Parse out any relevant text and write it to the print writer as well for easy search
+     * engine parsing.
      *
      * @param propertyValue The property value we are parsing.
      * @return The map parsed by this property value.
      * @throws IOException Can throw these when manipulating the seekable byte channel.
      */
-    private Map<String, Object> processPropertyValue(PropertyValue propertyValue) throws IOException, TikaException,
-            SAXException {
+    private Map<String, Object> processPropertyValue(PropertyValue propertyValue)
+            throws IOException, TikaException, SAXException {
         Map<String, Object> propMap = new HashMap<>();
         propMap.put("oneNoteType", "PropertyValue");
         propMap.put("propertyId", propertyValue.propertyId.toString());
@@ -387,13 +406,15 @@ class OneNoteTreeWalker {
                 lastModifiedTimestamp = instant;
             }
         } else if (propertyValue.propertyId.propertyEnum == OneNotePropertyEnum.CreationTimeStamp) {
-            // add the TIME32_EPOCH_DIFF_1980 because OneNote TIME32 epoch time is per 1980, not 1970
+            // add the TIME32_EPOCH_DIFF_1980 because OneNote TIME32 epoch time is per 1980, not
+            // 1970
             long creationTs = propertyValue.scalar + TIME32_EPOCH_DIFF_1980;
             if (creationTs < creationTimestamp) {
                 creationTimestamp = creationTs;
             }
         } else if (propertyValue.propertyId.propertyEnum == OneNotePropertyEnum.LastModifiedTime) {
-            // add the TIME32_EPOCH_DIFF_1980 because OneNote TIME32 epoch time is per 1980, not 1970
+            // add the TIME32_EPOCH_DIFF_1980 because OneNote TIME32 epoch time is per 1980, not
+            // 1970
             long lastMod = propertyValue.scalar + TIME32_EPOCH_DIFF_1980;
             if (lastMod > lastModified) {
                 lastModified = lastMod;
@@ -423,12 +444,12 @@ class OneNoteTreeWalker {
             content.reposition(propertyValue.rawData);
             boolean isBinary = propertyIsBinary(propertyValue.propertyId.propertyEnum);
             propMap.put("isBinary", isBinary);
-            if ((content.size() & 1) == 0
-              && propertyValue.propertyId.propertyEnum != OneNotePropertyEnum.TextExtendedAscii
-              && isBinary == false) {
+            if ((content.size() & 1) == 0 && propertyValue.propertyId.propertyEnum !=
+                    OneNotePropertyEnum.TextExtendedAscii && !isBinary) {
                 if (content.size() > dif.size()) {
-                    throw new TikaMemoryLimitException("File data store cb " + content.size() +
-                      " exceeds document size: " + dif.size());
+                    throw new TikaMemoryLimitException(
+                            "File data store cb " + content.size() + " exceeds document size: " +
+                                    dif.size());
                 }
                 ByteBuffer buf = ByteBuffer.allocate(content.size());
                 dif.read(buf);
@@ -438,21 +459,24 @@ class OneNoteTreeWalker {
                     xhtml.characters((String) propMap.get("dataUnicode16LE"));
                     xhtml.endElement(P);
                 }
-            } else if (propertyValue.propertyId.propertyEnum == OneNotePropertyEnum.TextExtendedAscii) {
+            } else if (propertyValue.propertyId.propertyEnum ==
+                    OneNotePropertyEnum.TextExtendedAscii) {
                 if (content.size() > dif.size()) {
-                    throw new TikaMemoryLimitException("File data store cb " + content.size() +
-                      " exceeds document size: " + dif.size());
+                    throw new TikaMemoryLimitException(
+                            "File data store cb " + content.size() + " exceeds document size: " +
+                                    dif.size());
                 }
                 ByteBuffer buf = ByteBuffer.allocate(content.size());
                 dif.read(buf);
-                propMap.put("dataAscii", new String(buf.array(), StandardCharsets.ISO_8859_1));
+                propMap.put("dataAscii", new String(buf.array(), StandardCharsets.US_ASCII));
                 xhtml.startElement(P);
                 xhtml.characters((String) propMap.get("dataAscii"));
                 xhtml.endElement(P);
-            } else if (isBinary == false) {
+            } else if (!isBinary) {
                 if (content.size() > dif.size()) {
-                    throw new TikaMemoryLimitException("File data store cb " + content.size() +
-                      " exceeds document size: " + dif.size());
+                    throw new TikaMemoryLimitException(
+                            "File data store cb " + content.size() + " exceeds document size: " +
+                                    dif.size());
                 }
                 ByteBuffer buf = ByteBuffer.allocate(content.size());
                 dif.read(buf);
@@ -464,10 +488,12 @@ class OneNoteTreeWalker {
                 }
             } else {
                 if (content.size() > dif.size()) {
-                    throw new TikaMemoryLimitException("File data store cb " + content.size() +
-                      " exceeds document size: " + dif.size());
+                    throw new TikaMemoryLimitException(
+                            "File data store cb " + content.size() + " exceeds document size: " +
+                                    dif.size());
                 }
-                if (propertyValue.propertyId.propertyEnum == OneNotePropertyEnum.RichEditTextUnicode) {
+                if (propertyValue.propertyId.propertyEnum ==
+                        OneNotePropertyEnum.RichEditTextUnicode) {
                     handleRichEditTextUnicode(content.size());
                 } else {
                     //TODO -- these seem to be somewhat broken font files and other
@@ -497,31 +523,35 @@ class OneNoteTreeWalker {
 
     /**
      * returns a UTF-16LE author string.
+     *
      * @param propertyValue The property value of an author.
      * @return Resulting author string in UTF-16LE format.
      */
-    private String getAuthor(PropertyValue propertyValue) throws IOException, TikaMemoryLimitException {
+    private String getAuthor(PropertyValue propertyValue)
+            throws IOException, TikaMemoryLimitException {
         OneNotePtr content = new OneNotePtr(oneNoteDocument, dif);
         content.reposition(propertyValue.rawData);
         if (content.size() > dif.size()) {
-            throw new TikaMemoryLimitException("File data store cb " + content.size() +
-                " exceeds document size: " + dif.size());
+            throw new TikaMemoryLimitException(
+                    "File data store cb " + content.size() + " exceeds document size: " +
+                            dif.size());
         }
         ByteBuffer buf = ByteBuffer.allocate(content.size());
         dif.read(buf);
         return new String(buf.array(), StandardCharsets.UTF_16LE);
     }
 
-    private void handleRichEditTextUnicode(int length) throws SAXException, IOException, TikaException {
+    private void handleRichEditTextUnicode(int length)
+            throws SAXException, IOException, TikaException {
         //this is a null-ended UTF-16LE string
         ByteBuffer buf = ByteBuffer.allocate(length);
         dif.read(buf);
         byte[] arr = buf.array();
         //look for the first null
         int firstNull = 0;
-        for (int i = 0; i < arr.length-1; i += 2) {
-            if (arr[i] == 0 && arr[i+1] == 0) {
-                firstNull =  (i > 0) ? i : 0;
+        for (int i = 0; i < arr.length - 1; i += 2) {
+            if (arr[i] == 0 && arr[i + 1] == 0) {
+                firstNull = (i > 0) ? i : 0;
                 break;
             }
         }
@@ -577,4 +607,4 @@ class OneNoteTreeWalker {
     public void setCreationTimestamp(long creationTimestamp) {
         this.creationTimestamp = creationTimestamp;
     }
-}
\ No newline at end of file
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalkerOptions.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalkerOptions.java
index b25fd05..8226654 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalkerOptions.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/OneNoteTreeWalkerOptions.java
@@ -17,6 +17,7 @@
 package org.apache.tika.parser.microsoft.onenote;
 
 
+import java.io.Serializable;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
@@ -24,13 +25,12 @@ import java.util.Set;
 /**
  * Options when walking the one note tree.
  */
-class OneNoteTreeWalkerOptions {
+public class OneNoteTreeWalkerOptions implements Serializable {
     private boolean crawlAllFileNodesFromRoot = true;
     private boolean onlyLatestRevision = true;
     private Set<OneNotePropertyEnum> utf16PropertiesToPrint = new HashSet<>(
-            Arrays.asList(OneNotePropertyEnum.ImageFilename,
-      OneNotePropertyEnum.Author,
-      OneNotePropertyEnum.CachedTitleString));
+            Arrays.asList(OneNotePropertyEnum.ImageFilename, OneNotePropertyEnum.Author,
+                    OneNotePropertyEnum.CachedTitleString));
 
     /**
      * Do this to ignore revisions and just parse all file nodes from the root recursively.
@@ -45,7 +45,8 @@ class OneNoteTreeWalkerOptions {
      * @param crawlAllFileNodesFromRoot
      * @return
      */
-    public OneNoteTreeWalkerOptions setCrawlAllFileNodesFromRoot(boolean crawlAllFileNodesFromRoot) {
+    public OneNoteTreeWalkerOptions setCrawlAllFileNodesFromRoot(
+            boolean crawlAllFileNodesFromRoot) {
         this.crawlAllFileNodesFromRoot = crawlAllFileNodesFromRoot;
         return this;
     }
@@ -78,10 +79,12 @@ class OneNoteTreeWalkerOptions {
     /**
      * Print file node data in UTF-16 format when they match these props.
      *
-     * @param utf16PropertiesToPrint The set of UTF properties you want to print UTF-16 for. Defaults are usually ok here.
+     * @param utf16PropertiesToPrint The set of UTF properties you want to print UTF-16 for.
+     *                               Defaults are usually ok here.
      * @return Returns this, as per builder pattern.
      */
-    public OneNoteTreeWalkerOptions setUtf16PropertiesToPrint(Set<OneNotePropertyEnum> utf16PropertiesToPrint) {
+    public OneNoteTreeWalkerOptions setUtf16PropertiesToPrint(
+            Set<OneNotePropertyEnum> utf16PropertiesToPrint) {
         this.utf16PropertiesToPrint = utf16PropertiesToPrint;
         return this;
     }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyIDType.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyIDType.java
index 87782e6..d5f79fb 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyIDType.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyIDType.java
@@ -14,13 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 enum PropertyIDType {
-    ObjectID,
-    ArrayOfObjectIDs,
-    ObjectSpaceID,
-    ArrayOfObjectSpaceIDs,
-    ContextID,
-    ArrayofContextIDs;
+    ObjectID, ArrayOfObjectIDs, ObjectSpaceID, ArrayOfObjectSpaceIDs, ContextID, ArrayofContextIDs
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertySet.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertySet.java
index a23d671..d6acb32 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertySet.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertySet.java
@@ -14,38 +14,49 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.tika.exception.TikaMemoryLimitException;
+package org.apache.tika.parser.microsoft.onenote;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
+import org.apache.tika.exception.TikaException;
+
 /**
- * A property set is a collection of properties that specify the attributes of an object (section 2.1.5).
+ * A property set is a collection of properties that specify the attributes of an object (section
+ * 2.1.5).
  * <p>
- * The PropertySet structure specifies the format of a property set and is contained by an ObjectSpaceObjectPropSet structure
- * (section 2.6.1). The meaning of each property in the set is specified in [MS-ONE] section 2.1.12.
+ * The PropertySet structure specifies the format of a property set and is contained by an
+ * ObjectSpaceObjectPropSet structure
+ * (section 2.6.1). The meaning of each property in the set is specified
+ * in [MS-ONE] section 2.1.12.
  * <p>
  * A PropertySet structure can contain references to other objects.
  * <p>
- * The data for a property that is not an object reference is contained in the PropertySet.rgData stream field. The rgData stream is read
- * sequentially beginning with the first property in a PropertySet.rgPrids array until every property has been read.
+ * The data for a property that is not an object reference is contained in the PropertySet
+ * .rgData stream field. The rgData stream is read
+ * sequentially beginning with the first property in a PropertySet.rgPrids array until every
+ * property has been read.
  * <p>
  * The number of bytes read for each property is specified by the PropertyID.type field.
  * <p>
- * The data for a property that is a reference to one or more objects (section 2.1.5) is contained in the streams within an
+ * The data for a property that is a reference to one or more objects (section 2.1.5) is
+ * contained in the streams within an
  * ObjectSpaceObjectPropSet structure (OIDs.body, OSIDs.body, ContextIDs.body).
  * <p>
- * The streams are read sequentially beginning with the first property in a PropertySet.rgPrids array.
+ * The streams are read sequentially beginning with the first property in a PropertySet.rgPrids
+ * array.
  * <p>
- * If the PropertyID.type field specifies a single object (0x8, 0xA, 0xC), a single CompactID (4 bytes) is read from the corresponding
+ * If the PropertyID.type field specifies a single object (0x8, 0xA, 0xC), a single CompactID (4
+ * bytes) is read from the corresponding
  * stream in the ObjectSpaceObjectPropSet structure.
  * <p>
- * If the PropertyID.type field specifies an array of objects (0x9, 0xB, 0xD), an unsigned integer (4 bytes) is read from the
- * PropertySet.rgDatastream and specifies the number of CompactID structures (section 2.2.2) to read from the corresponding stream in the
+ * If the PropertyID.type field specifies an array of objects (0x9, 0xB, 0xD), an unsigned
+ * integer (4 bytes) is read from the
+ * PropertySet.rgDatastream and specifies the number of CompactID structures (section 2.2.2) to
+ * read from the corresponding stream in the
  * ObjectSpaceObjectPropSet structure.
  * <p>
  * The streams for each PropertyID.type field are given by the following table.
@@ -61,7 +72,8 @@ import java.util.Objects;
 class PropertySet {
     List<PropertyValue> rgPridsData = new ArrayList<>();
 
-    public void print(OneNoteDocument document, OneNotePtr pointer, int indentLevel) throws IOException, TikaMemoryLimitException {
+    public void print(OneNoteDocument document, OneNotePtr pointer, int indentLevel)
+            throws IOException, TikaException {
         for (PropertyValue child : rgPridsData) {
             child.print(document, pointer, indentLevel);
         }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyValue.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyValue.java
index 454a3ea..e6b236a 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyValue.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/PropertyValue.java
@@ -14,16 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import org.apache.tika.exception.TikaMemoryLimitException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+package org.apache.tika.parser.microsoft.onenote;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.tika.exception.TikaException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 class PropertyValue {
 
     private static final Logger LOG = LoggerFactory.getLogger(PropertyValue.class);
@@ -37,8 +38,10 @@ class PropertyValue {
     PropertySet propertySet = new PropertySet(); // or used to house a single value
     FileChunkReference rawData = new FileChunkReference(); // FourBytesOfLengthFollowedByData
 
-    public void print(OneNoteDocument document, OneNotePtr pointer, int indentLevel) throws IOException, TikaMemoryLimitException {
-        boolean isRawText = true; //std::string(get_property_id_name(propertyId.id)).find("TextE")!=-1;
+    public void print(OneNoteDocument document, OneNotePtr pointer, int indentLevel)
+            throws IOException, TikaException {
+        boolean isRawText =
+                true; //std::string(get_property_id_name(propertyId.id)).find("TextE")!=-1;
 
         long type = propertyId.type;
 
@@ -57,9 +60,8 @@ class PropertyValue {
                 content.dumpHex();
                 LOG.debug("]");
             }
-        } else if (type == 0x9 || type == 0x8
-          || type == 0xb || type == 0xc
-          || type == 0xa || type == 0xd) {
+        } else if (type == 0x9 || type == 0x8 || type == 0xb || type == 0xc || type == 0xa ||
+                type == 0xd) {
             String xtype = "contextID";
             if (type == 0x8 || type == 0x9) {
                 xtype = "OIDs";
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Revision.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Revision.java
index e6ca0fc..6bc4a48 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Revision.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Revision.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 import java.util.ArrayList;
@@ -27,6 +28,18 @@ class Revision {
     ExtendedGUID gosid = ExtendedGUID.nil();
     ExtendedGUID dependent = ExtendedGUID.nil();
 
+    public Revision() {
+
+    }
+
+    public Revision(Map<Long, GUID> globalId, List<FileNodePtr> manifestList, ExtendedGUID gosid,
+                    ExtendedGUID dependent) {
+        this.globalId = globalId;
+        this.manifestList = manifestList;
+        this.gosid = gosid;
+        this.dependent = dependent;
+    }
+
     public Map<Long, GUID> getGlobalId() {
         return globalId;
     }
@@ -58,15 +71,4 @@ class Revision {
     public void setDependent(ExtendedGUID dependent) {
         this.dependent = dependent;
     }
-
-    public Revision() {
-
-    }
-
-    public Revision(Map<Long, GUID> globalId, List<FileNodePtr> manifestList, ExtendedGUID gosid, ExtendedGUID dependent) {
-        this.globalId = globalId;
-        this.manifestList = manifestList;
-        this.gosid = gosid;
-        this.dependent = dependent;
-    }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RevisionManifest.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RevisionManifest.java
index 4bd18b5..eb27e54 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RevisionManifest.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RevisionManifest.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.tika.parser.microsoft.onenote;
 
 class RevisionManifest {
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RootObjectReference.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RootObjectReference.java
index 94017b9..abe0c72 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RootObjectReference.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/RootObjectReference.java
@@ -33,7 +33,8 @@ class RootObjectReference {
         return rootObjectReferenceBase;
     }
 
-    public RootObjectReference setRootObjectReferenceBase(RootObjectReferenceBase rootObjectReferenceBase) {
+    public RootObjectReference setRootObjectReferenceBase(
+            RootObjectReferenceBase rootObjectReferenceBase) {
         this.rootObjectReferenceBase = rootObjectReferenceBase;
         return this;
     }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/IFSSHTTPBSerializable.java
similarity index 66%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/IFSSHTTPBSerializable.java
index b79ef8a..d141aa1 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/IFSSHTTPBSerializable.java
@@ -14,17 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-class FileNodePtrBackPush {
-    FileNodePtr parent;
+package org.apache.tika.parser.microsoft.onenote.fsshttpb;
 
-    public FileNodePtrBackPush(FileNodePtr parent) {
-        this.parent = parent;
-        this.parent.nodeListPositions.add(0);
-    }
+import java.io.IOException;
+import java.util.List;
 
-    public void dec() {
-        parent.nodeListPositions.remove(parent.nodeListPositions.size() - 1);
-    }
+import org.apache.tika.exception.TikaException;
+
+/**
+ * FSSHTTPB Serialize interface.
+ */
+public interface IFSSHTTPBSerializable {
+
+    /**
+     * Serialize to byte list.
+     *
+     * @return The byte list.
+     */
+    List<Byte> serializeToByteList() throws IOException, TikaException;
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/MSOneStorePackage.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/MSOneStorePackage.java
new file mode 100644
index 0000000..cb6bc23
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/MSOneStorePackage.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb;
+
+import static org.apache.tika.parser.microsoft.onenote.OneNoteParser.ONE_NOTE_PREFIX;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.Property;
+import org.apache.tika.metadata.TikaCoreProperties;
+import org.apache.tika.parser.microsoft.onenote.OneNotePropertyEnum;
+import org.apache.tika.parser.microsoft.onenote.OneNoteTreeWalkerOptions;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.EightBytesOfData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.FourBytesOfData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.IProperty;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.PrtFourBytesOfLengthFollowedByData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.CellManifestDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.PropertySet;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.RevisionManifestDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.RevisionStoreObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.RevisionStoreObjectGroup;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageIndexCellMapping;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageIndexDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageIndexRevisionMapping;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageManifestDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.HeaderCell;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.PropertyID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.PropertyType;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.unsigned.Unsigned;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitConverter;
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.xml.sax.SAXException;
+
+public class MSOneStorePackage {
+    /**
+     * See spec MS-ONE - 2.3.1 - TIME32 - epoch of jan 1 1980 UTC.
+     * So we create this offset used to calculate number of seconds between this and the Instant
+     * .EPOCH.
+     */
+    private static final long TIME32_EPOCH_DIFF_1980;
+    /**
+     * See spec MS-DTYP - 2.3.3 - DATETIME dates are based on epoch of jan 1 1601 UTC.
+     * So we create this offset used to calculate number of seconds between this and the Instant
+     * .EPOCH.
+     */
+    private static final long DATETIME_EPOCH_DIFF_1601;
+    private static final Pattern HYPERLINK_PATTERN =
+            Pattern.compile("\uFDDFHYPERLINK\\s+\"([^\"]+)\"([^\"]+)$");
+    private static final String P = "p";
+
+    static {
+        LocalDateTime time32Epoch1980 = LocalDateTime.of(1980, Month.JANUARY, 1, 0, 0);
+        Instant instant = time32Epoch1980.atZone(ZoneOffset.UTC).toInstant();
+        TIME32_EPOCH_DIFF_1980 = (instant.toEpochMilli() - Instant.EPOCH.toEpochMilli()) / 1000;
+    }
+
+    static {
+        LocalDateTime time32Epoch1601 = LocalDateTime.of(1601, Month.JANUARY, 1, 0, 0);
+        Instant instant = time32Epoch1601.atZone(ZoneOffset.UTC).toInstant();
+        DATETIME_EPOCH_DIFF_1601 = (instant.toEpochMilli() - Instant.EPOCH.toEpochMilli()) / 1000;
+    }
+
+    private final Set<String> authors = new HashSet<>();
+    private final Set<String> mostRecentAuthors = new HashSet<>();
+    private final Set<String> originalAuthors = new HashSet<>();
+    public StorageIndexDataElementData storageIndex;
+    public StorageManifestDataElementData storageManifest;
+    public CellManifestDataElementData headerCellCellManifest;
+    public RevisionManifestDataElementData headerCellRevisionManifest;
+    public List<RevisionManifestDataElementData> revisionManifests;
+    public List<CellManifestDataElementData> cellManifests;
+    public HeaderCell headerCell;
+    public List<RevisionStoreObjectGroup> dataRoot;
+    public List<RevisionStoreObjectGroup> OtherFileNodeList;
+    private boolean mostRecentAuthorProp = false;
+    private boolean originalAuthorProp = false;
+    private Instant lastModifiedTimestamp = Instant.MIN;
+    private long creationTimestamp = Long.MAX_VALUE;
+    private long lastModified = Long.MIN_VALUE;
+
+    public MSOneStorePackage() {
+        this.revisionManifests = new ArrayList<>();
+        this.cellManifests = new ArrayList<>();
+        this.OtherFileNodeList = new ArrayList<>();
+    }
+
+    /**
+     * This method is used to find the Storage Index Cell Mapping matches the Cell ID.
+     *
+     * @param cellID Specify the Cell ID.
+     * @return Return the specific Storage Index Cell Mapping.
+     */
+    public StorageIndexCellMapping findStorageIndexCellMapping(CellID cellID) {
+        StorageIndexCellMapping storageIndexCellMapping = null;
+        if (this.storageIndex != null) {
+            storageIndexCellMapping = this.storageIndex.storageIndexCellMappingList.stream()
+                    .filter(s -> s.cellID.equals(cellID)).findFirst()
+                    .orElse(new StorageIndexCellMapping());
+        }
+        return storageIndexCellMapping;
+    }
+
+    /**
+     * This method is used to find the Storage Index Revision Mapping that matches the Revision Mapping Extended GUID.
+     *
+     * @param revisionExtendedGUID Specify the Revision Mapping Extended GUID.
+     * @return Return the instance of Storage Index Revision Mapping.
+     */
+    public StorageIndexRevisionMapping findStorageIndexRevisionMapping(
+            ExGuid revisionExtendedGUID) {
+        StorageIndexRevisionMapping instance = null;
+        if (this.storageIndex != null) {
+            instance = this.storageIndex.storageIndexRevisionMappingList.stream()
+                    .filter(r -> r.revisionExGuid.equals(revisionExtendedGUID)).findFirst()
+                    .orElse(new StorageIndexRevisionMapping());
+        }
+
+        return instance;
+    }
+
+    /**
+     * Is this property a binary property?
+     *
+     * @param property The property.
+     * @return Is it binary?
+     */
+    private boolean propertyIsBinary(OneNotePropertyEnum property) {
+        return property == OneNotePropertyEnum.RgOutlineIndentDistance ||
+                property == OneNotePropertyEnum.NotebookManagementEntityGuid ||
+                property == OneNotePropertyEnum.RichEditTextUnicode;
+    }
+
+    public void walkTree(OneNoteTreeWalkerOptions options, Metadata metadata,
+                         XHTMLContentHandler xhtml)
+            throws SAXException, TikaException, IOException {
+        for (RevisionStoreObjectGroup revisionStoreObjectGroup : OtherFileNodeList) {
+            for (RevisionStoreObject revisionStoreObject : revisionStoreObjectGroup.objects) {
+                PropertySet propertySet =
+                        revisionStoreObject.propertySet.objectSpaceObjectPropSet.body;
+                for (int i = 0; i < propertySet.rgData.size(); ++i) {
+                    IProperty property = propertySet.rgData.get(i);
+                    PropertyID propertyID = propertySet.rgPrids[i];
+                    PropertyType propertyType = PropertyType.fromIntVal(propertyID.type);
+                    OneNotePropertyEnum oneNotePropertyEnum =
+                            OneNotePropertyEnum.of(Unsigned.uint(propertyID.value).longValue());
+                    if (oneNotePropertyEnum == OneNotePropertyEnum.LastModifiedTimeStamp) {
+                        long fullval = getScalar(property);
+                        Instant instant = Instant.ofEpochSecond(
+                                fullval / 10000000 + DATETIME_EPOCH_DIFF_1601);
+                        if (instant.isAfter(lastModifiedTimestamp)) {
+                            lastModifiedTimestamp = instant;
+                        }
+                        metadata.set(ONE_NOTE_PREFIX + "lastModifiedTimestamp",
+                                String.valueOf(lastModifiedTimestamp.toEpochMilli()));
+                    } else if (oneNotePropertyEnum == OneNotePropertyEnum.CreationTimeStamp) {
+                        // add the TIME32_EPOCH_DIFF_1980 because OneNote TIME32 epoch time is per 1980, not
+                        // 1970
+                        long scalar = getScalar(property);
+                        long creationTs = scalar + TIME32_EPOCH_DIFF_1980;
+                        if (creationTs < creationTimestamp) {
+                            creationTimestamp = creationTs;
+                        }
+                        metadata.set(ONE_NOTE_PREFIX + "creationTimestamp", String.valueOf(creationTimestamp));
+                    } else if (oneNotePropertyEnum == OneNotePropertyEnum.LastModifiedTime) {
+                        // add the TIME32_EPOCH_DIFF_1980 because OneNote TIME32 epoch time is per 1980, not
+                        // 1970
+                        long scalar = getScalar(property);
+                        long lastMod = scalar + TIME32_EPOCH_DIFF_1980;
+                        if (lastMod > lastModified) {
+                            lastModified = lastMod;
+                        }
+                        metadata.set(TikaCoreProperties.MODIFIED, String.valueOf(lastModified));
+                    } else if (oneNotePropertyEnum == OneNotePropertyEnum.Author) {
+                        String author =
+                                new String(((PrtFourBytesOfLengthFollowedByData) property).data,
+                                        StandardCharsets.UTF_8);
+                        if (mostRecentAuthorProp) {
+                            mostRecentAuthors.add(author);
+                        } else if (originalAuthorProp) {
+                            originalAuthors.add(author);
+                        } else {
+                            authors.add(author);
+                        }
+                    } else if (oneNotePropertyEnum == OneNotePropertyEnum.AuthorMostRecent) {
+                        mostRecentAuthorProp = true;
+                    } else if (oneNotePropertyEnum == OneNotePropertyEnum.AuthorOriginal) {
+                        originalAuthorProp = true;
+                    } else if (propertyType == PropertyType.FourBytesOfLengthFollowedByData) {
+                        boolean isBinary = propertyIsBinary(oneNotePropertyEnum);
+                        PrtFourBytesOfLengthFollowedByData dataProperty =
+                                (PrtFourBytesOfLengthFollowedByData) property;
+                        if ((dataProperty.data.length & 1) == 0 &&
+                                oneNotePropertyEnum != OneNotePropertyEnum.TextExtendedAscii &&
+                                !isBinary) {
+                            if (options.getUtf16PropertiesToPrint().contains(oneNotePropertyEnum)) {
+                                xhtml.startElement(P);
+                                xhtml.characters(
+                                        new String(dataProperty.data, StandardCharsets.UTF_16LE));
+                                xhtml.endElement(P);
+                            }
+                        } else if (oneNotePropertyEnum == OneNotePropertyEnum.TextExtendedAscii) {
+                            xhtml.startElement(P);
+                            xhtml.characters(
+                                    new String(dataProperty.data, StandardCharsets.US_ASCII));
+                            xhtml.endElement(P);
+                        } else if (!isBinary) {
+                            if (options.getUtf16PropertiesToPrint().contains(oneNotePropertyEnum)) {
+                                xhtml.startElement(P);
+                                xhtml.characters(
+                                        new String(dataProperty.data, StandardCharsets.UTF_16LE));
+                                xhtml.endElement(P);
+                            }
+                        } else {
+                            if (oneNotePropertyEnum == OneNotePropertyEnum.RichEditTextUnicode) {
+                                handleRichEditTextUnicode(dataProperty.data, xhtml);
+                            } else {
+                                //TODO -- these seem to be somewhat broken font files and other
+                                //odds and ends...what are they and how should we process them?
+                                //handleEmbedded(content.size());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (!authors.isEmpty()) {
+            metadata.set(TikaCoreProperties.CREATOR, authors.toArray(new String[]{}));
+        }
+        if (!mostRecentAuthors.isEmpty()) {
+            metadata.set(Property.externalTextBag(ONE_NOTE_PREFIX + "mostRecentAuthors"),
+                    mostRecentAuthors.toArray(new String[]{}));
+        }
+        if (!originalAuthors.isEmpty()) {
+            metadata.set(Property.externalTextBag(ONE_NOTE_PREFIX + "originalAuthors"),
+                    originalAuthors.toArray(new String[]{}));
+        }
+    }
+
+
+    private void handleRichEditTextUnicode(byte[] arr, XHTMLContentHandler xhtml)
+            throws SAXException, IOException, TikaException {
+        // look for the first null
+        int firstNull = 0;
+        for (int i = 0; i < arr.length - 1; i += 2) {
+            if (arr[i] == 0 && arr[i + 1] == 0) {
+                firstNull = Math.max(i, 0);
+                break;
+            }
+        }
+
+        if (firstNull == 0) {
+            return;
+        }
+        String txt = new String(arr, 0, firstNull, StandardCharsets.UTF_16LE);
+        Matcher m = HYPERLINK_PATTERN.matcher(txt);
+        if (m.find()) {
+            xhtml.startElement("a", "href", m.group(1));
+            xhtml.characters(m.group(2));
+            xhtml.endElement("a");
+        } else {
+            xhtml.startElement(P);
+            xhtml.characters(txt);
+            xhtml.endElement(P);
+        }
+    }
+
+    private long getScalar(IProperty property) throws TikaException, IOException {
+        if (property instanceof FourBytesOfData) {
+            FourBytesOfData fourBytesOfDataProp = (FourBytesOfData) property;
+            return BitConverter.toUInt32(fourBytesOfDataProp.data, 0);
+        } else if (property instanceof EightBytesOfData) {
+            EightBytesOfData fourBytesOfDataProp = (EightBytesOfData) property;
+            return BitConverter.toInt64(fourBytesOfDataProp.data, 0);
+        }
+        throw new TikaException("Could not parse scalar of type " + property.getClass());
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/MSOneStoreParser.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/MSOneStoreParser.java
new file mode 100644
index 0000000..ab1f008
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/MSOneStoreParser.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.CellManifestDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.DataElement;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.DataElementPackage;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.ObjectGroupDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.RevisionManifestDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.RevisionManifestObjectGroupReferences;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.RevisionManifestRootDeclare;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.RevisionStoreObjectGroup;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageIndexCellMapping;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageIndexDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageIndexRevisionMapping;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.StorageManifestDataElementData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.DataElementType;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.HeaderCell;
+
+public class MSOneStoreParser {
+    private final Set<CellID> storageIndexHashTab = new HashSet<>();
+    // The DataElements of Storage Index
+    private List<DataElement> storageIndexDataElements;
+    // The DataElements of Storage Manifest
+    private List<DataElement> storageManifestDataElements;
+    // The DataElements of Cell Manifest
+    private List<DataElement> cellManifestDataElements;
+    // The DataElements of Revision Manifest
+    private List<DataElement> revisionManifestDataElements;
+    // The DataElements of Object Group Data
+    private List<DataElement> objectGroupDataElements;
+    // The DataElements of Object BLOB
+    private List<DataElement> objectBlOBElements;
+
+    public MSOneStorePackage parse(DataElementPackage dataElementPackage) throws IOException {
+        MSOneStorePackage msOneStorePackage = new MSOneStorePackage();
+
+        storageIndexDataElements = dataElementPackage.dataElements.stream()
+                .filter(d -> d.dataElementType == DataElementType.StorageIndexDataElementData)
+                .collect(Collectors.toList());
+        storageManifestDataElements = dataElementPackage.dataElements.stream()
+                .filter(d -> d.dataElementType == DataElementType.StorageManifestDataElementData)
+                .collect(Collectors.toList());
+        cellManifestDataElements = dataElementPackage.dataElements.stream()
+                .filter(d -> d.dataElementType == DataElementType.CellManifestDataElementData)
+                .collect(Collectors.toList());
+        revisionManifestDataElements = dataElementPackage.dataElements.stream()
+                .filter(d -> d.dataElementType == DataElementType.RevisionManifestDataElementData)
+                .collect(Collectors.toList());
+        objectGroupDataElements = dataElementPackage.dataElements.stream()
+                .filter(d -> d.dataElementType == DataElementType.ObjectGroupDataElementData)
+                .collect(Collectors.toList());
+        objectBlOBElements = dataElementPackage.dataElements.stream()
+                .filter(d -> d.dataElementType == DataElementType.ObjectDataBLOBDataElementData)
+                .collect(Collectors.toList());
+
+        msOneStorePackage.storageIndex =
+                (StorageIndexDataElementData) storageIndexDataElements.get(0).data;
+        msOneStorePackage.storageManifest =
+                (StorageManifestDataElementData) storageManifestDataElements.get(0).data;
+
+        // Parse Header Cell
+        CellID headerCellID =
+                msOneStorePackage.storageManifest.storageManifestRootDeclareList.get(0).cellID;
+        StorageIndexCellMapping headerCellStorageIndexCellMapping =
+                msOneStorePackage.findStorageIndexCellMapping(headerCellID);
+        storageIndexHashTab.add(headerCellID);
+
+        if (headerCellStorageIndexCellMapping != null) {
+            msOneStorePackage.headerCellCellManifest =
+                    this.findCellManifest(headerCellStorageIndexCellMapping.cellMappingExGuid);
+            StorageIndexRevisionMapping headerCellRevisionManifestMapping =
+                    msOneStorePackage.findStorageIndexRevisionMapping(
+                            msOneStorePackage.headerCellCellManifest.cellManifestCurrentRevision
+                                    .cellManifestCurrentRevisionExGuid);
+            msOneStorePackage.headerCellRevisionManifest = this.findRevisionManifestDataElement(
+                    headerCellRevisionManifestMapping.revisionMappingExGuid);
+            msOneStorePackage.headerCell =
+                    this.parseHeaderCell(msOneStorePackage.headerCellRevisionManifest);
+
+            // Parse Data root
+            CellID dataRootCellID =
+                    msOneStorePackage.storageManifest.storageManifestRootDeclareList.get(1).cellID;
+            storageIndexHashTab.add(dataRootCellID);
+            msOneStorePackage.dataRoot = this.parseObjectGroup(dataRootCellID, msOneStorePackage);
+            // Parse other data
+            for (StorageIndexCellMapping storageIndexCellMapping : msOneStorePackage.storageIndex
+                    .storageIndexCellMappingList) {
+                if (!storageIndexHashTab.contains(storageIndexCellMapping.cellID)) {
+                    msOneStorePackage.OtherFileNodeList.addAll(
+                            this.parseObjectGroup(storageIndexCellMapping.cellID,
+                                    msOneStorePackage));
+                    storageIndexHashTab.add(storageIndexCellMapping.cellID);
+                }
+            }
+        }
+        return msOneStorePackage;
+    }
+
+    /**
+     * Find the CellManifestDataElementData
+     *
+     * @param cellMappingExtendedGUID The ExGuid of Cell Mapping Extended GUID.
+     * @return The CellManifestDataElementData instance.
+     */
+    private CellManifestDataElementData findCellManifest(ExGuid cellMappingExtendedGUID) {
+        return (CellManifestDataElementData) this.cellManifestDataElements.stream()
+                .filter(d -> d.dataElementExGuid.equals(cellMappingExtendedGUID)).findFirst()
+                .orElse(new DataElement()).data;
+    }
+
+    /**
+     * Find the Revision Manifest from Data Elements.
+     *
+     * @param revisionMappingExtendedGUID The Revision Mapping Extended GUID.
+     * @return Returns the instance of RevisionManifestDataElementData
+     */
+    private RevisionManifestDataElementData findRevisionManifestDataElement(
+            ExGuid revisionMappingExtendedGUID) {
+        return (RevisionManifestDataElementData) this.revisionManifestDataElements.stream()
+                .filter(d -> d.dataElementExGuid.equals(revisionMappingExtendedGUID)).findFirst()
+                .orElse(new DataElement()).data;
+    }
+
+    private HeaderCell parseHeaderCell(RevisionManifestDataElementData headerCellRevisionManifest)
+            throws IOException {
+        ExGuid rootObjectId =
+                headerCellRevisionManifest.revisionManifestObjectGroupReferences.get(
+                        0).objectGroupExtendedGUID;
+
+        DataElement element = this.objectGroupDataElements.stream()
+                .filter(d -> d.dataElementExGuid.equals(rootObjectId)).findFirst()
+                .orElse(new DataElement());
+
+        return HeaderCell.createInstance((ObjectGroupDataElementData) element.data);
+    }
+
+    private List<RevisionStoreObjectGroup> parseObjectGroup(CellID objectGroupCellID,
+                                                            MSOneStorePackage msOneStorePackage)
+            throws IOException {
+        StorageIndexCellMapping storageIndexCellMapping =
+                msOneStorePackage.findStorageIndexCellMapping(objectGroupCellID);
+        CellManifestDataElementData cellManifest =
+                this.findCellManifest(storageIndexCellMapping.cellMappingExGuid);
+        List<RevisionStoreObjectGroup> objectGroups = new ArrayList<>();
+        msOneStorePackage.cellManifests.add(cellManifest);
+        StorageIndexRevisionMapping revisionMapping =
+                msOneStorePackage.findStorageIndexRevisionMapping(
+                        cellManifest.cellManifestCurrentRevision.cellManifestCurrentRevisionExGuid);
+        RevisionManifestDataElementData revisionManifest =
+                findRevisionManifestDataElement(revisionMapping.revisionMappingExGuid);
+        msOneStorePackage.revisionManifests.add(revisionManifest);
+        RevisionManifestRootDeclare encryptionKeyRoot =
+                revisionManifest.revisionManifestRootDeclareList.stream()
+                        .filter(r -> r.rootExGuid.equals(new ExGuid(3,
+                                UUID.fromString("4A3717F8-1C14-49E7-9526-81D942DE1741"))))
+                        .findFirst().orElse(null);
+        boolean isEncryption = encryptionKeyRoot != null;
+        for (RevisionManifestObjectGroupReferences objRef :
+                revisionManifest.revisionManifestObjectGroupReferences) {
+            ObjectGroupDataElementData dataObject =
+                    (ObjectGroupDataElementData) objectGroupDataElements.stream()
+                            .filter(d -> d.dataElementExGuid.equals(objRef.objectGroupExtendedGUID))
+                            .findFirst().get().data;
+
+            RevisionStoreObjectGroup objectGroup =
+                    RevisionStoreObjectGroup.createInstance(objRef.objectGroupExtendedGUID,
+                            dataObject, isEncryption);
+            objectGroups.add(objectGroup);
+        }
+
+        return objectGroups;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/exception/DataElementParseErrorException.java
similarity index 63%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/exception/DataElementParseErrorException.java
index b79ef8a..41cf7b5 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/exception/DataElementParseErrorException.java
@@ -14,17 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-class FileNodePtrBackPush {
-    FileNodePtr parent;
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.exception;
 
-    public FileNodePtrBackPush(FileNodePtr parent) {
-        this.parent = parent;
-        this.parent.nodeListPositions.add(0);
+public class DataElementParseErrorException extends RuntimeException {
+
+    private final int index;
+
+    public DataElementParseErrorException(int index, Exception innerException) {
+        super(innerException);
+        this.index = index;
     }
 
-    public void dec() {
-        parent.nodeListPositions.remove(parent.nodeListPositions.size() - 1);
+    public DataElementParseErrorException(int index, String msg, Exception innerException) {
+        super(msg, innerException);
+        this.index = index;
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/ArrayNumber.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/ArrayNumber.java
new file mode 100644
index 0000000..aac97b1
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/ArrayNumber.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitConverter;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+/**
+ * The class is used to represent the number of the array.
+ */
+public class ArrayNumber implements IProperty {
+    public int number;
+
+    /**
+     * This method is used to deserialize the number of array from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the number of array.
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) throws IOException {
+        this.number = BitConverter.toInt32(byteArray, startIndex);
+        return 4;
+    }
+
+    /**
+     * This method is used to convert the element of the number of array into a byte List.
+     *
+     * @return Return the byte list which store the byte information of the number of array.
+     */
+    public List<Byte> serializeToByteList() {
+        return ByteUtil.toListOfByte(BitConverter.getBytes(this.number));
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/EightBytesOfData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/EightBytesOfData.java
new file mode 100644
index 0000000..26e88d7
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/EightBytesOfData.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+
+/**
+ * This class is used to represent the property contains 8 bytes of data in the PropertySet.rgData stream field.
+ */
+public class EightBytesOfData implements IProperty {
+    public byte[] data;
+
+    /**
+     * This method is used to deserialize the EightBytesOfData from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the EightBytesOfData.
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) {
+        this.data = Arrays.copyOfRange(byteArray, startIndex, startIndex + 8);
+        return 8;
+    }
+
+    /**
+     * This method is used to convert the element of EightBytesOfData into a byte List.
+     *
+     * @return Return the byte list which store the byte information of EightBytesOfData.
+     */
+    public List<Byte> serializeToByteList() {
+        return ByteUtil.toListOfByte(this.data);
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/FourBytesOfData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/FourBytesOfData.java
new file mode 100644
index 0000000..5d6f366
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/FourBytesOfData.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+/**
+ * This class is used to represent the property contains 4 bytes of data in the PropertySet.rgData stream field.
+ */
+public class FourBytesOfData implements IProperty {
+    public byte[] data;
+
+    /**
+     * This method is used to deserialize the FourBytesOfData from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the FourBytesOfData.
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) {
+        this.data = Arrays.copyOfRange(byteArray, startIndex, startIndex + 4);
+        return 4;
+    }
+
+    /**
+     * This method is used to convert the element of FourBytesOfData into a byte List.
+     *
+     * @return Return the byte list which store the byte information of FourBytesOfData.
+     */
+    public List<Byte> serializeToByteList() {
+        return ByteUtil.toListOfByte(this.data);
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/IProperty.java
similarity index 50%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/IProperty.java
index aa01c18..bf402a0 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/IProperty.java
@@ -14,30 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import java.util.ArrayList;
-import java.util.List;
-
-class FileNodeList {
-    FileNodeListHeader fileNodeListHeader;
-    List<FileNode> children = new ArrayList<>();
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
 
-    public FileNodeListHeader getFileNodeListHeader() {
-        return fileNodeListHeader;
-    }
+import java.io.IOException;
+import java.util.List;
 
-    public FileNodeList setFileNodeListHeader(FileNodeListHeader fileNodeListHeader) {
-        this.fileNodeListHeader = fileNodeListHeader;
-        return this;
-    }
 
-    public List<FileNode> getChildren() {
-        return children;
-    }
+/**
+ * The interface of the property in OneNote file.
+ */
+public interface IProperty {
+    /**
+     * This method is used to convert the element of property into a byte List.
+     *
+     * @return Return the byte list which store the byte information of property.
+     */
+    List<Byte> serializeToByteList() throws IOException;
 
-    public FileNodeList setChildren(List<FileNode> children) {
-        this.children = children;
-        return this;
-    }
+    /**
+     * This method is used to deserialize the property from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the property.
+     */
+    int doDeserializeFromByteArray(byte[] byteArray, int startIndex) throws IOException;
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/NoData.java
similarity index 52%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/NoData.java
index aa01c18..4a2c7c2 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/NoData.java
@@ -14,30 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
 
 import java.util.ArrayList;
 import java.util.List;
 
-class FileNodeList {
-    FileNodeListHeader fileNodeListHeader;
-    List<FileNode> children = new ArrayList<>();
-
-    public FileNodeListHeader getFileNodeListHeader() {
-        return fileNodeListHeader;
-    }
-
-    public FileNodeList setFileNodeListHeader(FileNodeListHeader fileNodeListHeader) {
-        this.fileNodeListHeader = fileNodeListHeader;
-        return this;
-    }
-
-    public List<FileNode> getChildren() {
-        return children;
+/**
+ * This class is used to represent the property contains no data.
+ */
+public class NoData implements IProperty {
+    /**
+     * This method is used to deserialize the NoData from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) {
+        return 0;
     }
 
-    public FileNodeList setChildren(List<FileNode> children) {
-        this.children = children;
-        return this;
+    /**
+     * This method is used to convert the element of NoData into a byte List.
+     *
+     * @return Return the byte list which store the byte information of NoData.
+     */
+    public List<Byte> serializeToByteList() {
+        return new ArrayList<>();
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/OneByteOfData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/OneByteOfData.java
new file mode 100644
index 0000000..8c2d64c
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/OneByteOfData.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to represent the property contains 1 byte of data in the PropertySet.rgData stream field.
+ */
+public class OneByteOfData implements IProperty {
+    public byte data;
+
+    /**
+     * This method is used to deserialize the OneByteOfData from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) {
+        this.data = byteArray[startIndex];
+        return 1;
+    }
+
+    /**
+     * This method is used to convert the element of OneByteOfData into a byte List.
+     *
+     * @return Return the byte list which store the byte information of OneByteOfData.
+     */
+    public List<Byte> serializeToByteList() {
+        return new ArrayList<>(this.data);
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/PrtArrayOfPropertyValues.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/PrtArrayOfPropertyValues.java
new file mode 100644
index 0000000..73a01cb
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/PrtArrayOfPropertyValues.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.PropertySet;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.PropertyID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitConverter;
+
+
+/**
+ * The class is used to represent the prtArrayOfPropertyValues .
+ */
+public class PrtArrayOfPropertyValues implements IProperty {
+    public int cProperties;
+    public PropertyID propertyID;
+    public PropertySet[] data;
+
+    /**
+     * This method is used to deserialize the prtArrayOfPropertyValues from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) throws IOException {
+        int index = startIndex;
+        this.cProperties = BitConverter.toInt32(byteArray, index);
+        index += 4;
+        this.propertyID = new PropertyID();
+        int len = this.propertyID.doDeserializeFromByteArray(byteArray, index);
+        index += len;
+        this.data = new PropertySet[this.cProperties];
+        for (int i = 0; i < this.cProperties; i++) {
+            this.data[i] = new PropertySet();
+            int length = this.data[i].doDeserializeFromByteArray(byteArray, index);
+            index += length;
+        }
+
+        return index - startIndex;
+    }
+
+    /**
+     * This method is used to convert the element of the prtArrayOfPropertyValues into a byte List.
+     *
+     * @return Return the byte list which store the byte information of the prtArrayOfPropertyValues.
+     */
+    public List<Byte> serializeToByteList() throws IOException {
+        List<Byte> byteList = new ArrayList<>();
+        for (byte b : BitConverter.getBytes(this.cProperties)) {
+            byteList.add(b);
+        }
+        byteList.addAll(this.propertyID.serializeToByteList());
+        for (PropertySet ps : this.data) {
+            byteList.addAll(ps.serializeToByteList());
+        }
+        return byteList;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/PrtFourBytesOfLengthFollowedByData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/PrtFourBytesOfLengthFollowedByData.java
new file mode 100644
index 0000000..6a8655c
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/PrtFourBytesOfLengthFollowedByData.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitConverter;
+
+/**
+ * This class is used to represent the prtFourBytesOfLengthFollowedByData.
+ */
+public class PrtFourBytesOfLengthFollowedByData implements IProperty {
+    public int cb;
+
+    public byte[] data;
+
+    /**
+     * This method is used to deserialize the prtFourBytesOfLengthFollowedByData from
+     * the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the prtFourBytesOfLengthFollowedByData.
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) throws IOException {
+        int index = startIndex;
+        this.cb = (int) BitConverter.toUInt32(byteArray, startIndex);
+        index += 4;
+        this.data = Arrays.copyOfRange(byteArray, index, index + this.cb);
+        index += this.cb;
+
+        return index - startIndex;
+    }
+
+    /**
+     * This method is used to convert the element of prtFourBytesOfLengthFollowedByData into a byte List.
+     *
+     * @return Return the byte list which store the byte information of prtFourBytesOfLengthFollowedByData.
+     */
+    public List<Byte> serializeToByteList() {
+        List<Byte> byteList = new ArrayList<>();
+        for (byte b : BitConverter.getBytes(this.cb)) {
+            byteList.add(b);
+        }
+        for (byte b : this.data) {
+            byteList.add(b);
+        }
+        return byteList;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/TwoBytesOfData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/TwoBytesOfData.java
new file mode 100644
index 0000000..296e1c4
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/property/TwoBytesOfData.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.property;
+
+import java.util.List;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+/**
+ * This class is used to represent the property contains 2 bytes of data in the PropertySet.rgData stream field.
+ */
+public class TwoBytesOfData implements IProperty {
+    public byte[] data;
+
+
+    /**
+     * This method is used to deserialize the TwoBytesOfData from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the TwoBytesOfData.
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) {
+        this.data = new byte[]{byteArray[startIndex], byteArray[startIndex + 1]};
+
+        return 2;
+    }
+
+    /**
+     * This method is used to convert the element of TwoBytesOfData into a byte List.
+     *
+     * @return Return the byte list which store the byte information of TwoBytesOfData.
+     */
+    public List<Byte> serializeToByteList() {
+        return ByteUtil.toListOfByte(this.data);
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/CellManifestCurrentRevision.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/CellManifestCurrentRevision.java
new file mode 100644
index 0000000..1cc4661
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/CellManifestCurrentRevision.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+public class CellManifestCurrentRevision extends StreamObject {
+    public ExGuid cellManifestCurrentRevisionExGuid;
+
+    /**
+     * Initializes a new instance of the CellManifestCurrentRevision class.
+     */
+    public CellManifestCurrentRevision() {
+        super(StreamObjectTypeHeaderStart.CellManifestCurrentRevision);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.cellManifestCurrentRevisionExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "CellManifestCurrentRevision", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The number of elements actually contained in the list.
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        List<Byte> tmpList = this.cellManifestCurrentRevisionExGuid.serializeToByteList();
+        byteList.addAll(tmpList);
+        return tmpList.size();
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/CellManifestDataElementData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/CellManifestDataElementData.java
new file mode 100644
index 0000000..60428d1
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/CellManifestDataElementData.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+
+/**
+ * Cell manifest data element
+ */
+public class CellManifestDataElementData extends DataElementData {
+    public CellManifestCurrentRevision cellManifestCurrentRevision;
+
+    /**
+     * Initializes a new instance of the CellManifestDataElementData class.
+     */
+    public CellManifestDataElementData() {
+        this.cellManifestCurrentRevision = new CellManifestCurrentRevision();
+    }
+
+    /**
+     * Used to return the length of this element.
+     *
+     * @param byteArray  A Byte array
+     * @param startIndex Start position
+     * @return The element length
+     */
+    @Override
+    public int deserializeDataElementDataFromByteArray(byte[] byteArray, int startIndex)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(startIndex);
+        this.cellManifestCurrentRevision =
+                StreamObject.getCurrent(byteArray, index, CellManifestCurrentRevision.class);
+        return index.get() - startIndex;
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @return The Byte list
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws TikaException, IOException {
+        return this.cellManifestCurrentRevision.serializeToByteList();
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElement.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElement.java
new file mode 100644
index 0000000..058c924
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElement.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.exception.DataElementParseErrorException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.Compact64bitInt;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.DataElementType;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.SerialNumber;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.SequenceNumberGenerator;
+
+public class DataElement extends StreamObject {
+
+    /**
+     * Data Element Data Type Mapping
+     */
+    private static final Map<DataElementType, Class> DATA_ELEMENT_DATA_TYPE_MAPPING;
+
+    /**
+     *  Initializes static members of the DataElement class
+     */
+    static {
+        DATA_ELEMENT_DATA_TYPE_MAPPING = new HashMap<>();
+        for (DataElementType value : DataElementType.values()) {
+            String className = DataElement.class.getPackage().getName() + "." + value.name();
+
+            try {
+                DATA_ELEMENT_DATA_TYPE_MAPPING.put(value, Class.forName(className));
+            } catch (ClassNotFoundException e) {
+                // This is OK, we are not pulling over every single class
+            }
+        }
+    }
+
+    public ExGuid dataElementExGuid;
+    public SerialNumber serialNumber;
+    public DataElementType dataElementType;
+    public DataElementData data;
+
+    /**
+     * Initializes a new instance of the DataElement class.
+     *
+     * @param type data
+     *             element type
+     *             *
+     * @param data Specifies
+     *             the data
+     *             of the
+     *             element .
+     */
+
+
+    public DataElement(DataElementType type, DataElementData data) {
+        super(StreamObjectTypeHeaderStart.DataElement);
+        if (!DATA_ELEMENT_DATA_TYPE_MAPPING.containsKey(type)) {
+            throw new IllegalArgumentException("Invalid argument type value" + type.getIntVal());
+        }
+
+        this.dataElementType = type;
+        this.data = data;
+        this.dataElementExGuid =
+                new ExGuid(SequenceNumberGenerator.GetCurrentSerialNumber(), UUID.randomUUID());
+        this.serialNumber = new SerialNumber(UUID.randomUUID(),
+                SequenceNumberGenerator.GetCurrentSerialNumber());
+    }
+
+    /**
+     * Initializes a new instance of the DataElement class.
+     */
+    public DataElement() {
+        super(StreamObjectTypeHeaderStart.DataElement);
+    }
+
+    /**
+     * Used to get data.
+     *
+     * @return Data of
+     * the element
+     */
+    public <T extends DataElementData> T getData(Class<T> clazz) throws TikaException {
+        if (this.data.getClass().equals(clazz)) {
+            return (T) this.data;
+        } else {
+            throw new TikaException(String.format(Locale.US,
+                    "Unable to cast DataElementData to the type %s, its actual type is %s",
+                    clazz.getName(), this.data.getClass().getName()));
+        }
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A
+     *                      Byte array
+     * @param currentIndex  Start
+     *                      position
+     * @param lengthOfItems The
+     *                      length of
+     *                      the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems) throws TikaException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+
+        try {
+            this.dataElementExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+            this.serialNumber = BasicObject.parse(byteArray, index, SerialNumber.class);
+            this.dataElementType = DataElementType.fromIntVal(
+                    (int) BasicObject.parse(byteArray, index, Compact64bitInt.class)
+                            .getDecodedValue());
+        } catch (Exception e) {
+            throw new DataElementParseErrorException(index.get(), e);
+        }
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new DataElementParseErrorException(currentIndex.get(),
+                    "Failed to check the data element header length, whose value does not cover the " +
+                            "dataElementExGUID, SerialNumber and DataElementType", null);
+        }
+
+        if (DATA_ELEMENT_DATA_TYPE_MAPPING.containsKey(this.dataElementType)) {
+            try {
+                this.data = (DataElementData) DATA_ELEMENT_DATA_TYPE_MAPPING.get(this.dataElementType)
+                        .newInstance();
+            } catch (InstantiationException | IllegalAccessException e) {
+                throw new TikaException("Could not instantiate a " + dataElementType, e);
+            }
+
+            try {
+                index.addAndGet(
+                        this.data.deserializeDataElementDataFromByteArray(byteArray, index.get()));
+            } catch (Exception e) {
+                throw new DataElementParseErrorException(index.get(), e);
+            }
+        } else {
+            throw new DataElementParseErrorException(index.get(),
+                    "Failed to create specific data element instance with the type " +
+                            this.dataElementType, null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The element length
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException, TikaException {
+        int startIndex = byteList.size();
+        byteList.addAll(this.dataElementExGuid.serializeToByteList());
+        byteList.addAll(this.serialNumber.serializeToByteList());
+        byteList.addAll(
+                new Compact64bitInt(this.dataElementType.getIntVal()).serializeToByteList());
+
+        int headerLength = byteList.size() - startIndex;
+        byteList.addAll(this.data.serializeToByteList());
+
+        return headerLength;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementData.java
new file mode 100644
index 0000000..9e8f426
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementData.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.IFSSHTTPBSerializable;
+
+/**
+ * Base class of data element
+ */
+public abstract class DataElementData implements IFSSHTTPBSerializable {
+    /**
+     * De-serialize data element data from byte array.
+     *
+     * @param byteArray  The byte array.
+     * @param startIndex The position where to start.
+     * @return The length of the item.
+     */
+    public abstract int deserializeDataElementDataFromByteArray(byte[] byteArray, int startIndex)
+            throws TikaException, IOException;
+
+    /**
+     * Serialize item to byte list.
+     *
+     * @return The byte list.
+     */
+    public abstract List<Byte> serializeToByteList() throws TikaException, IOException;
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementHash.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementHash.java
new file mode 100644
index 0000000..2657dd0
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementHash.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BinaryItem;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.Compact64bitInt;
+
+/**
+ * Specifies an data element hash stream object
+ */
+public class DataElementHash extends StreamObject {
+    public Compact64bitInt dataElementHashScheme;
+    public BinaryItem dataElementHashData;
+
+    /**
+     * Initializes a new instance of the DataElementHash class.
+     */
+    public DataElementHash() {
+        super(StreamObjectTypeHeaderStart.DataElementHash);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.dataElementHashScheme = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+        this.dataElementHashData = BasicObject.parse(byteArray, index, BinaryItem.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "DataElementHash",
+                    "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List
+     *
+     * @param byteList A Byte list
+     * @return The number of elements actually contained in the list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int startPoint = byteList.size();
+        byteList.addAll(this.dataElementHashScheme.serializeToByteList());
+        byteList.addAll(this.dataElementHashData.serializeToByteList());
+
+        return byteList.size() - startPoint;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementPackage.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementPackage.java
new file mode 100644
index 0000000..784b9ad
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataElementPackage.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+
+public class DataElementPackage extends StreamObject {
+
+    public List<DataElement> dataElements = new ArrayList<>();
+    public byte reserved;
+
+    /**
+     * Initializes a new instance of the DataElementHash class.
+     */
+    public DataElementPackage() {
+        super(StreamObjectTypeHeaderStart.DataElementPackage);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        if (lengthOfItems != 1) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "DataElementPackage",
+                    "Stream object over-parse error", null);
+        }
+
+        reserved = byteArray[currentIndex.getAndIncrement()];
+
+        this.dataElements = new ArrayList<>();
+        AtomicReference<DataElement> dataElement = new AtomicReference<>();
+        while (StreamObject.tryGetCurrent(byteArray, currentIndex, dataElement,
+                DataElement.class)) {
+            this.dataElements.add(dataElement.get());
+        }
+    }
+
+    /**
+     * Used to convert the element into a byte List
+     *
+     * @param byteList A Byte list
+     * @return The number of elements actually contained in the list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws TikaException, IOException {
+        // Add the reserved byte
+        byteList.add((byte) 0);
+        for (DataElement dataElement : dataElements) {
+            byteList.addAll(dataElement.serializeToByteList());
+        }
+        return 1;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataHashObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataHashObject.java
new file mode 100644
index 0000000..a318250
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataHashObject.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BinaryItem;
+
+public class DataHashObject extends StreamObject {
+    /**
+     * Gets or sets a binary item as specified in [MS-FSSHTTPB] section 2.2.1.3 that specifies a
+     * value that is unique to the file data represented by this root node object.
+     * The value of this item depends on the file chunking algorithm used, as specified in section 2.4.
+     */
+    public BinaryItem data;
+
+    /**
+     * Initializes a new instance of the DataHashObject class.
+     */
+    public DataHashObject() {
+        super(StreamObjectTypeHeaderStart.DataHashObject);
+        this.data = new BinaryItem();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DataHashObject that = (DataHashObject) o;
+        return Objects.equals(data, that.data);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(data);
+    }
+
+    @Override
+    public String toString() {
+        return "DataHashObject{" + "Data=" + data + ", streamObjectHeaderEnd=" +
+                streamObjectHeaderEnd + '}';
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+
+        this.data = BasicObject.parse(byteArray, index, BinaryItem.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "Signature",
+                    "Stream Object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The number of elements
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int length = byteList.size();
+        byteList.addAll(this.data.serializeToByteList());
+        return byteList.size() - length;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataSizeObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataSizeObject.java
new file mode 100644
index 0000000..9cad9f8
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/DataSizeObject.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.LittleEndianBitConverter;
+
+/**
+ * Data Size Object
+ */
+public class DataSizeObject extends StreamObject {
+    public long dataSize;
+
+    /**
+     * Initializes a new instance of the DataSizeObject class.
+     */
+    public DataSizeObject() {
+        super(StreamObjectTypeHeaderStart.DataSizeObject);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems) throws IOException {
+        if (lengthOfItems != 8) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "DataSize",
+                    "Stream Object over-parse error", null);
+        }
+
+        this.dataSize = LittleEndianBitConverter.toUInt64(byteArray, currentIndex.get());
+        currentIndex.addAndGet(8);
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return A constant value 8
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) {
+        ByteUtil.appendByteArrayToListOfByte(byteList,
+                LittleEndianBitConverter.getBytes(this.dataSize));
+        return 8;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Error.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/EncryptionObject.java
similarity index 75%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Error.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/EncryptionObject.java
index 1239231..cc52e3c 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/Error.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/EncryptionObject.java
@@ -14,16 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-public enum Error {
-    OK,
-    SEGV,
-    RESERVED_NONZERO,
-    UNKNOWN_ENUM,
-    INVALID_CONSTANT,
-    STRING_TOO_SHORT,
-    HEX_OUT_OF_RANGE,
-    COMPACT_ID_MISSING,
-    UNKNOWN_GUID,
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+
+/**
+ * The class is used to represent the encryption revision store object.
+ */
+class EncryptionObject {
+    public ObjectGroupObjectDeclare objectDeclaration;
+    public byte[] objectData;
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/FileDataObject.java
similarity index 69%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/FileDataObject.java
index b79ef8a..be305f8 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/FileDataObject.java
@@ -14,17 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-class FileNodePtrBackPush {
-    FileNodePtr parent;
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
 
-    public FileNodePtrBackPush(FileNodePtr parent) {
-        this.parent = parent;
-        this.parent.nodeListPositions.add(0);
-    }
-
-    public void dec() {
-        parent.nodeListPositions.remove(parent.nodeListPositions.size() - 1);
-    }
+/**
+ * This class is used to represent the file data.
+ */
+class FileDataObject {
+    public ObjectGroupObjectBLOBDataDeclaration objectDataBLOBDeclaration;
+    public ObjectGroupObjectDataBLOBReference objectDataBLOBReference;
+    public DataElement objectDataBLOBDataElement;
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/IntermediateNodeObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/IntermediateNodeObject.java
new file mode 100644
index 0000000..b693905
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/IntermediateNodeObject.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.chunking.ChunkingFactory;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.SequenceNumberGenerator;
+
+public class IntermediateNodeObject extends NodeObject {
+    /**
+     * Initializes a new instance of the IntermediateNodeObject class.
+     */
+    public IntermediateNodeObject() {
+        super(StreamObjectTypeHeaderStart.IntermediateNodeObject);
+        this.intermediateNodeObjectList = new ArrayList<>();
+    }
+
+    /**
+     * Get all the content which is represented by the root node object.
+     *
+     * @return Return the byte list of root node object content.
+     */
+    @Override
+    public List<Byte> getContent() throws TikaException {
+        List<Byte> content = new ArrayList<>();
+
+        for (LeafNodeObject intermediateNode : this.intermediateNodeObjectList) {
+            content.addAll(intermediateNode.getContent());
+        }
+
+        return content;
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        if (lengthOfItems != 0) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "IntermediateNodeObject",
+                    "Stream Object over-parse error", null);
+        }
+
+        this.signature = StreamObject.getCurrent(byteArray, index, SignatureObject.class);
+        this.dataSize = StreamObject.getCurrent(byteArray, index, DataSizeObject.class);
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The Byte list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws TikaException, IOException {
+        byteList.addAll(this.signature.serializeToByteList());
+        byteList.addAll(this.dataSize.serializeToByteList());
+        return 0;
+    }
+
+    /**
+     * The class is used to build a root node object.
+     */
+    public static class RootNodeObjectBuilder {
+        /**
+         * This method is used to build a root node object from a byte array
+         *
+         * @param fileContent Specify the byte array.
+         * @return Return a root node object build from the byte array.
+         */
+        public IntermediateNodeObject Build(byte[] fileContent) throws TikaException, IOException {
+            IntermediateNodeObject rootNode = new IntermediateNodeObject();
+            rootNode.signature = new SignatureObject();
+            rootNode.dataSize = new DataSizeObject();
+            rootNode.dataSize.dataSize = fileContent.length;
+            rootNode.exGuid =
+                    new ExGuid(SequenceNumberGenerator.GetCurrentSerialNumber(), UUID.randomUUID());
+            rootNode.intermediateNodeObjectList =
+                    ChunkingFactory.createChunkingInstance(fileContent).chunking();
+            return rootNode;
+        }
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/JCIDObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/JCIDObject.java
new file mode 100644
index 0000000..cac0f9b
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/JCIDObject.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.JCID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+
+/**
+ * This class is used to represent the JCID object.
+ */
+public class JCIDObject {
+    public ObjectGroupObjectDeclare objectDeclaration;
+    public JCID jcid;
+
+    /**
+     * Construct the JCIDObject instance.
+     *
+     * @param objectDeclaration The Object Declaration structure.
+     * @param objectData        The Object Data structure.
+     */
+    public JCIDObject(ObjectGroupObjectDeclare objectDeclaration,
+                      ObjectGroupObjectData objectData) throws IOException {
+        this.objectDeclaration = objectDeclaration;
+        this.jcid = new JCID();
+        this.jcid.doDeserializeFromByteArray(ByteUtil.toByteArray(objectData.data.content), 0);
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/LeafNodeObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/LeafNodeObject.java
new file mode 100644
index 0000000..c9dd7d2
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/LeafNodeObject.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BinaryItem;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.DataNodeObjectData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.SequenceNumberGenerator;
+
+public class LeafNodeObject extends NodeObject {
+    public DataNodeObjectData dataNodeObjectData;
+    public DataHashObject dataHash;
+
+    /**
+     * Initializes a new instance of the LeafNodeObjectData class.
+     */
+    public LeafNodeObject() {
+        super(StreamObjectTypeHeaderStart.LeafNodeObject);
+    }
+
+    /**
+     * Get all the content which is represented by the intermediate node object.
+     *
+     * @return Return the byte list of intermediate node object content.
+     */
+    @Override
+    public List<Byte> getContent() throws TikaException {
+        List<Byte> content = new ArrayList<Byte>();
+
+        if (this.dataNodeObjectData != null) {
+            ByteUtil.appendByteArrayToListOfByte(content, this.dataNodeObjectData.objectData);
+        } else if (this.intermediateNodeObjectList != null) {
+            for (LeafNodeObject intermediateNode : this.intermediateNodeObjectList) {
+                content.addAll(intermediateNode.getContent());
+            }
+        } else {
+            throw new TikaException(
+                    "The DataNodeObjectData and IntermediateNodeObjectList properties in " +
+                            "LeafNodeObjectData cannot be null at the same time.");
+        }
+
+        return content;
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        if (lengthOfItems != 0) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "LeafNodeObjectData",
+                    "Stream Object over-parse error", null);
+        }
+
+        this.signature = StreamObject.getCurrent(byteArray, index, SignatureObject.class);
+        this.dataSize = StreamObject.getCurrent(byteArray, index, DataSizeObject.class);
+
+        // Try to read StreamObjectHeaderStart to see there is data hash object or not
+        AtomicReference<StreamObjectHeaderStart> streamObjectHeader = new AtomicReference<>();
+        if ((StreamObjectHeaderStart.tryParse(byteArray, index.get(), streamObjectHeader)) != 0) {
+            if (streamObjectHeader.get().type == StreamObjectTypeHeaderStart.DataHashObject) {
+                this.dataHash = StreamObject.getCurrent(byteArray, index, DataHashObject.class);
+            }
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return A constant value
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws TikaException, IOException {
+        byteList.addAll(this.signature.serializeToByteList());
+        byteList.addAll(this.dataSize.serializeToByteList());
+        return 0;
+    }
+
+    /**
+     * The class is used to build a intermediate node object.
+     */
+    public static class IntermediateNodeObjectBuilder {
+        /**
+         * This method is used to build intermediate node object from an list of object group data element
+         *
+         * @param objectGroupList  Specify the list of object group data elements.
+         * @param dataObj          Specify the object group object.
+         * @param intermediateGuid Specify the intermediate extended GUID.
+         * @return Return the intermediate node object.
+         */
+        public LeafNodeObject Build(List<ObjectGroupDataElementData> objectGroupList,
+                                    ObjectGroupObjectData dataObj,
+                                    ExGuid intermediateGuid) throws TikaException, IOException {
+            AtomicReference<LeafNodeObject> node = new AtomicReference<>();
+            AtomicReference<IntermediateNodeObject> rootNode = new AtomicReference<>();
+
+            AtomicInteger index = new AtomicInteger(0);
+            if (StreamObject.tryGetCurrent(ByteUtil.toByteArray(dataObj.data.content), index, node,
+                    LeafNodeObject.class)) {
+                if (dataObj.objectExGUIDArray == null) {
+                    throw new TikaException(
+                            "Failed to build intermediate node because the object extend GUID array does not exist.");
+                }
+
+                node.get().exGuid = intermediateGuid;
+
+                // Contain a single Data Node Object.
+                if (dataObj.objectExGUIDArray.count.getDecodedValue() == 1) {
+                    AtomicReference<ObjectGroupObjectDeclare> dataNodeDeclare =
+                            new AtomicReference<>();
+                    ObjectGroupObjectData dataNodeData = this.FindByExGuid(objectGroupList,
+                            dataObj.objectExGUIDArray.content.get(0), dataNodeDeclare);
+                    BinaryItem data = dataNodeData.data;
+
+                    node.get().dataNodeObjectData =
+                            new DataNodeObjectData(ByteUtil.toByteArray(data.content), 0,
+                                    (int) data.length.getDecodedValue());
+                    node.get().dataNodeObjectData.exGuid = dataObj.objectExGUIDArray.content.get(0);
+                    node.get().intermediateNodeObjectList = null;
+                } else {
+                    // Contain a list of LeafNodeObjectData
+                    node.get().intermediateNodeObjectList = new ArrayList<LeafNodeObject>();
+                    node.get().dataNodeObjectData = null;
+                    for (ExGuid extGuid : dataObj.objectExGUIDArray.content) {
+                        AtomicReference<ObjectGroupObjectDeclare> intermediateDeclare =
+                                new AtomicReference<>();
+                        ObjectGroupObjectData intermediateData =
+                                this.FindByExGuid(objectGroupList, extGuid, intermediateDeclare);
+                        node.get().intermediateNodeObjectList.add(
+                                new IntermediateNodeObjectBuilder().Build(objectGroupList,
+                                        intermediateData, extGuid));
+                    }
+                }
+            } else if (StreamObject.tryGetCurrent(ByteUtil.toByteArray(dataObj.data.content), index,
+                    rootNode, IntermediateNodeObject.class)) {
+                // In Sub chunking for larger than 1MB zip file, MOSS2010 could return IntermediateNodeObject.
+                // For easy further process, the rootNode will be replaced by intermediate node instead.
+                node.set(new LeafNodeObject());
+                node.get().intermediateNodeObjectList = new ArrayList<LeafNodeObject>();
+                node.get().dataSize = rootNode.get().dataSize;
+                node.get().exGuid = rootNode.get().exGuid;
+                node.get().signature = rootNode.get().signature;
+                node.get().dataNodeObjectData = null;
+                for (ExGuid extGuid : dataObj.objectExGUIDArray.content) {
+                    AtomicReference<ObjectGroupObjectDeclare> intermediateDeclare =
+                            new AtomicReference<>();
+                    ObjectGroupObjectData intermediateData =
+                            this.FindByExGuid(objectGroupList, extGuid, intermediateDeclare);
+                    node.get().intermediateNodeObjectList.add(
+                            new IntermediateNodeObjectBuilder().Build(objectGroupList,
+                                    intermediateData, extGuid));
+                }
+            } else {
+                throw new TikaException(
+                        "In the ObjectGroupDataElement cannot only contain the " +
+                                "IntermediateNodeObject or IntermediateNodeObject.");
+            }
+
+            return node.get();
+        }
+
+        /**
+         * This method is used to build intermediate node object from a byte array with a signature
+         *
+         * @param array     Specify the byte array.
+         * @param signature Specify the signature.
+         * @return Return the intermediate node object.
+         */
+        public LeafNodeObject Build(byte[] array, SignatureObject signature) {
+            LeafNodeObject nodeObject = new LeafNodeObject();
+            nodeObject.dataSize = new DataSizeObject();
+            nodeObject.dataSize.dataSize = array.length;
+
+            nodeObject.signature = signature;
+            nodeObject.exGuid =
+                    new ExGuid(SequenceNumberGenerator.GetCurrentSerialNumber(), UUID.randomUUID());
+
+            nodeObject.dataNodeObjectData = new DataNodeObjectData(array, 0, array.length);
+            nodeObject.intermediateNodeObjectList = null;
+
+            // Now in the current implementation, one intermediate node only contain one single data object node.
+            return nodeObject;
+        }
+
+        /**
+         * This method is used to find the object group data element using the specified extended GUID
+         *
+         * @param objectGroupList Specify the object group data element list.
+         * @param extendedGuid    Specify the extended GUID.
+         * @param declare         Specify the output of ObjectGroupObjectDeclare.
+         * @return Return the object group data element if found.
+         */
+
+        private ObjectGroupObjectData FindByExGuid(List<ObjectGroupDataElementData> objectGroupList,
+                                                   ExGuid extendedGuid,
+                                                   AtomicReference<ObjectGroupObjectDeclare> declare)
+                throws TikaException {
+            for (ObjectGroupDataElementData objectGroup : objectGroupList) {
+
+                int findIndex = -1;
+                for (int i = 0;
+                        i < objectGroup.objectGroupDeclarations.objectDeclarationList.size(); ++i) {
+                    ObjectGroupObjectDeclare objDeclare =
+                            objectGroup.objectGroupDeclarations.objectDeclarationList.get(i);
+                    if (objDeclare.objectExtendedGUID.equals(extendedGuid)) {
+                        findIndex = i;
+                        break;
+                    }
+                }
+
+                if (findIndex < 0) {
+                    continue;
+                }
+
+                declare.set(
+                        objectGroup.objectGroupDeclarations.objectDeclarationList.get(findIndex));
+                return objectGroup.objectGroupData.objectGroupObjectDataList.get(findIndex);
+            }
+
+            throw new TikaException("Cannot find the " + extendedGuid.guid.toString());
+        }
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/NodeObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/NodeObject.java
new file mode 100644
index 0000000..e26cfea
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/NodeObject.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.util.List;
+
+import org.apache.tika.exception.TikaException;
+
+public abstract class NodeObject extends StreamObject {
+    public org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid exGuid;
+    public List<LeafNodeObject> intermediateNodeObjectList;
+    public SignatureObject signature;
+    public DataSizeObject dataSize;
+
+    /**
+     * Initializes a new instance of the NodeObject class.
+     *
+     * @param headerType Specify the node object header type.
+     */
+    protected NodeObject(StreamObjectTypeHeaderStart headerType) {
+        super(headerType);
+    }
+
+    /**
+     * Get all the content which is represented by the node object.
+     *
+     * @return Return the byte list of node object content.
+     */
+    public abstract List<Byte> getContent() throws TikaException;
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupData.java
new file mode 100644
index 0000000..47518e8
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupData.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+
+/**
+ * The ObjectGroupData class.
+ */
+public class ObjectGroupData extends StreamObject {
+    public List<ObjectGroupObjectData> objectGroupObjectDataList;
+    public List<ObjectGroupObjectDataBLOBReference> objectGroupObjectDataBLOBReferenceList;
+
+    /**
+     * Initializes a new instance of the ObjectGroupData class.
+     */
+    public ObjectGroupData() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupData);
+        this.objectGroupObjectDataList = new ArrayList<ObjectGroupObjectData>();
+        this.objectGroupObjectDataBLOBReferenceList =
+                new ArrayList<ObjectGroupObjectDataBLOBReference>();
+    }
+
+    /**
+     * Used to convert the element into a byte List
+     *
+     * @param byteList A Byte list
+     * @return A constant value 0
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws TikaException, IOException {
+        if (this.objectGroupObjectDataList != null) {
+            for (ObjectGroupObjectData objectGroupObjectData : this.objectGroupObjectDataList) {
+                byteList.addAll(objectGroupObjectData.serializeToByteList());
+            }
+        }
+
+        if (this.objectGroupObjectDataBLOBReferenceList != null) {
+            for (ObjectGroupObjectDataBLOBReference objectGroupObjectDataBLOBReference :
+                    this.objectGroupObjectDataBLOBReferenceList) {
+                byteList.addAll(objectGroupObjectDataBLOBReference.serializeToByteList());
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        if (lengthOfItems != 0) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "ObjectGroupDeclarations",
+                    "Stream object over-parse error", null);
+        }
+
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        int headerLength = 0;
+        AtomicReference<StreamObjectHeaderStart> header = new AtomicReference<>();
+
+        this.objectGroupObjectDataList = new ArrayList<>();
+        this.objectGroupObjectDataBLOBReferenceList = new ArrayList<>();
+
+        while ((headerLength = StreamObjectHeaderStart.tryParse(byteArray, index.get(), header)) !=
+                0) {
+            StreamObjectTypeHeaderStart type = header.get().type;
+            if (type == StreamObjectTypeHeaderStart.ObjectGroupObjectData) {
+                index.addAndGet(headerLength);
+                this.objectGroupObjectDataList.add(
+                        (ObjectGroupObjectData) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index));
+            } else if (type == StreamObjectTypeHeaderStart.ObjectGroupObjectDataBLOBReference) {
+                index.addAndGet(headerLength);
+                this.objectGroupObjectDataBLOBReferenceList.add(
+                        (ObjectGroupObjectDataBLOBReference) StreamObject.parseStreamObject(
+                                header.get(), byteArray, index));
+            } else {
+                throw new StreamObjectParseErrorException(index.get(), "ObjectGroupDeclarations",
+                        "Failed to parse ObjectGroupData, expect the inner object type either " +
+                                "ObjectGroupObjectData or ObjectGroupObjectDataBLOBReference, " +
+                                "but actual type value is " + type, null);
+            }
+        }
+
+        currentIndex.set(index.get());
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupDataElementData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupDataElementData.java
new file mode 100644
index 0000000..892ba63
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupDataElementData.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BinaryItem;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellIDArray;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.Compact64bitInt;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.DataElementType;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.DataNodeObjectData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGUIDArray;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+public class ObjectGroupDataElementData extends DataElementData {
+    public DataElementHash
+            dataElementHash;
+    public ObjectGroupDeclarations objectGroupDeclarations;
+    public ObjectGroupMetadataDeclarations objectMetadataDeclaration;
+    public ObjectGroupData objectGroupData;
+
+    /**
+     * Initializes a new instance of the ObjectGroupDataElementData class.
+     */
+    public ObjectGroupDataElementData() {
+        this.objectGroupDeclarations = new ObjectGroupDeclarations();
+
+        // The ObjectMetadataDeclaration is only present for MOSS2013, so leave null for default value.
+        this.objectMetadataDeclaration = null;
+
+        // The DataElementHash is only present for MOSS2013, so leave null for default value.
+        this.dataElementHash = null;
+        this.objectGroupData = new ObjectGroupData();
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @return A Byte list
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws TikaException, IOException {
+        List<Byte> result = new ArrayList<>();
+
+        if (this.dataElementHash != null) {
+            result.addAll(this.dataElementHash.serializeToByteList());
+        }
+
+        result.addAll(this.objectGroupDeclarations.serializeToByteList());
+        if (this.objectMetadataDeclaration != null) {
+            result.addAll(this.objectMetadataDeclaration.serializeToByteList());
+        }
+
+        result.addAll(this.objectGroupData.serializeToByteList());
+        return result;
+    }
+
+    /**
+     * Used to return the length of this element.
+     *
+     * @param byteArray  A Byte array
+     * @param startIndex Start position
+     * @return The length of the element
+     */
+    @Override
+    public int deserializeDataElementDataFromByteArray(byte[] byteArray, int startIndex)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(startIndex);
+
+        AtomicReference<DataElementHash> dataElementHash = new AtomicReference<>();
+        if (StreamObject.tryGetCurrent(byteArray, index, dataElementHash, DataElementHash.class)) {
+            this.dataElementHash = dataElementHash.get();
+        }
+
+        this.objectGroupDeclarations =
+                StreamObject.getCurrent(byteArray, index, ObjectGroupDeclarations.class);
+
+        AtomicReference<ObjectGroupMetadataDeclarations> objectMetadataDeclaration =
+                new AtomicReference<>(new ObjectGroupMetadataDeclarations());
+        if (StreamObject.tryGetCurrent(byteArray, index, objectMetadataDeclaration,
+                ObjectGroupMetadataDeclarations.class)) {
+            this.objectMetadataDeclaration = objectMetadataDeclaration.get();
+        }
+
+        this.objectGroupData = StreamObject.getCurrent(byteArray, index, ObjectGroupData.class);
+
+        return index.get() - startIndex;
+    }
+
+    /**
+     * The internal class for build a list of DataElement from a node object.
+     */
+    public static class Builder {
+        /**
+         * This method is used to build a list of DataElement from a node object
+         *
+         * @param node Specify the node object.
+         * @return Return the list of data elements build from the specified node object.
+         */
+        public List<DataElement> build(NodeObject node) throws TikaException, IOException {
+            List<DataElement> dataElements = new ArrayList<>();
+            this.traverseNodeObject(node, dataElements);
+            return dataElements;
+        }
+
+        /**
+         * This method is used to travel the node tree and build the ObjectGroupDataElementData
+         * and the extra data element list
+         *
+         * @param node         Specify the object node.
+         * @param dataElements Specify the list of data elements.
+         */
+        private void traverseNodeObject(NodeObject node, List<DataElement> dataElements)
+                throws TikaException, IOException {
+            if (node instanceof IntermediateNodeObject) {
+                IntermediateNodeObject intermediateNodeObject = (IntermediateNodeObject) node;
+                ObjectGroupDataElementData data = new ObjectGroupDataElementData();
+                data.objectGroupDeclarations.objectDeclarationList.add(
+                        this.createObjectDeclare(node));
+                data.objectGroupData.objectGroupObjectDataList.add(
+                        this.createObjectData((IntermediateNodeObject) node));
+
+                dataElements.add(new DataElement(DataElementType.ObjectGroupDataElementData, data));
+
+                for (LeafNodeObject child : intermediateNodeObject.intermediateNodeObjectList) {
+                    this.traverseNodeObject(child, dataElements);
+                }
+            } else if (node instanceof LeafNodeObject) {
+                LeafNodeObject intermediateNode = (LeafNodeObject) node;
+
+                ObjectGroupDataElementData data = new ObjectGroupDataElementData();
+                data.objectGroupDeclarations.objectDeclarationList.add(
+                        this.createObjectDeclare(node));
+                data.objectGroupData.objectGroupObjectDataList.add(
+                        this.createObjectData(intermediateNode));
+
+                if (intermediateNode.dataNodeObjectData != null) {
+                    data.objectGroupDeclarations.objectDeclarationList.add(
+                            this.createObjectDeclare(intermediateNode.dataNodeObjectData));
+                    data.objectGroupData.objectGroupObjectDataList.add(
+                            this.createObjectData(intermediateNode.dataNodeObjectData));
+                    dataElements.add(
+                            new DataElement(DataElementType.ObjectGroupDataElementData, data));
+                    return;
+                }
+
+                if (intermediateNode.dataNodeObjectData == null &&
+                        intermediateNode.intermediateNodeObjectList != null) {
+                    dataElements.add(
+                            new DataElement(DataElementType.ObjectGroupDataElementData, data));
+
+                    for (LeafNodeObject child : intermediateNode.intermediateNodeObjectList) {
+                        this.traverseNodeObject(child, dataElements);
+                    }
+
+                    return;
+                }
+
+                throw new TikaException(
+                        "The DataNodeObjectData and IntermediateNodeObjectList properties in " +
+                                "LeafNodeObjectData type cannot be null in the same time.");
+            }
+        }
+
+        /**
+         * This method is used to create ObjectGroupObjectDeclare instance from a node object
+         *
+         * @param node Specify the node object.
+         * @return Return the ObjectGroupObjectDeclare instance.
+         */
+        private ObjectGroupObjectDeclare createObjectDeclare(NodeObject node) throws TikaException {
+            ObjectGroupObjectDeclare objectGroupObjectDeclare = new ObjectGroupObjectDeclare();
+
+            objectGroupObjectDeclare.objectExtendedGUID = node.exGuid;
+            objectGroupObjectDeclare.objectPartitionID = new Compact64bitInt(1);
+            objectGroupObjectDeclare.cellReferencesCount = new Compact64bitInt(0);
+            objectGroupObjectDeclare.objectReferencesCount = new Compact64bitInt(0);
+            objectGroupObjectDeclare.objectDataSize = new Compact64bitInt(node.getContent().size());
+
+            return objectGroupObjectDeclare;
+        }
+
+        /**
+         * This method is used to create ObjectGroupObjectDeclare instance from a data node object
+         *
+         * @param node Specify the node object.
+         * @return Return the ObjectGroupObjectDeclare instance.
+         */
+        private ObjectGroupObjectDeclare createObjectDeclare(DataNodeObjectData node) {
+            ObjectGroupObjectDeclare objectGroupObjectDeclare = new ObjectGroupObjectDeclare();
+
+            objectGroupObjectDeclare.objectExtendedGUID = node.exGuid;
+            objectGroupObjectDeclare.objectPartitionID = new Compact64bitInt(1);
+            objectGroupObjectDeclare.cellReferencesCount = new Compact64bitInt(0);
+            objectGroupObjectDeclare.objectReferencesCount = new Compact64bitInt(1);
+            objectGroupObjectDeclare.objectDataSize = new Compact64bitInt(node.objectData.length);
+
+            return objectGroupObjectDeclare;
+        }
+
+        /**
+         * This method is used to create ObjectGroupObjectData instance from a root node object
+         *
+         * @param node Specify the node object.
+         * @return Return the ObjectGroupObjectData instance.
+         */
+        private ObjectGroupObjectData createObjectData(IntermediateNodeObject node)
+                throws TikaException, IOException {
+            ObjectGroupObjectData objectData = new ObjectGroupObjectData();
+
+            objectData.cellIDArray = new CellIDArray(0, null);
+
+            List<ExGuid> extendedGuidList = new ArrayList<ExGuid>();
+            for (LeafNodeObject child : node.intermediateNodeObjectList) {
+                extendedGuidList.add(child.exGuid);
+            }
+
+            objectData.objectExGUIDArray = new ExGUIDArray(extendedGuidList);
+            objectData.data = new BinaryItem(node.serializeToByteList());
+
+            return objectData;
+        }
+
+        /**
+         * This method is used to create ObjectGroupObjectData instance from a intermediate node object
+         *
+         * @param node Specify the node object.
+         * @return Return the ObjectGroupObjectData instance.
+         */
+        private ObjectGroupObjectData createObjectData(LeafNodeObject node)
+                throws TikaException, IOException {
+            ObjectGroupObjectData objectData = new ObjectGroupObjectData();
+
+            objectData.cellIDArray = new CellIDArray(0, null);
+            List<ExGuid> extendedGuidList = new ArrayList<ExGuid>();
+
+            if (node.dataNodeObjectData != null) {
+                extendedGuidList.add(node.dataNodeObjectData.exGuid);
+            } else if (node.intermediateNodeObjectList != null) {
+                for (LeafNodeObject child : node.intermediateNodeObjectList) {
+                    extendedGuidList.add(child.exGuid);
+                }
+            }
+
+            objectData.objectExGUIDArray = new ExGUIDArray(extendedGuidList);
+            objectData.data = new BinaryItem(node.serializeToByteList());
+
+            return objectData;
+        }
+
+        /**
+         * This method is used to create ObjectGroupObjectData instance from a data node object
+         *
+         * @param node Specify the node object.
+         * @return Return the ObjectGroupObjectData instance.
+         */
+        private ObjectGroupObjectData createObjectData(DataNodeObjectData node) {
+            ObjectGroupObjectData objectData = new ObjectGroupObjectData();
+            objectData.cellIDArray = new CellIDArray(0, null);
+            objectData.objectExGUIDArray = new ExGUIDArray(new ArrayList<>());
+            objectData.data = new BinaryItem(ByteUtil.toListOfByte(node.objectData));
+            return objectData;
+        }
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupDeclarations.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupDeclarations.java
new file mode 100644
index 0000000..b4a521b
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupDeclarations.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+
+/**
+ * Object Group Declarations
+ */
+public class ObjectGroupDeclarations extends StreamObject {
+    public List<ObjectGroupObjectDeclare> objectDeclarationList;
+    public List<ObjectGroupObjectBLOBDataDeclaration> objectGroupObjectBLOBDataDeclarationList;
+
+    /**
+     * Initializes a new instance of the ObjectGroupDeclarations class.
+     */
+    public ObjectGroupDeclarations() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupDeclarations);
+        this.objectDeclarationList = new ArrayList<>();
+        this.objectGroupObjectBLOBDataDeclarationList = new ArrayList<>();
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        if (lengthOfItems != 0) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "ObjectGroupDeclarations",
+                    "Stream object over-parse error", null);
+        }
+
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        int headerLength = 0;
+        AtomicReference<StreamObjectHeaderStart> header = new AtomicReference<>();
+        this.objectDeclarationList = new ArrayList<>();
+        this.objectGroupObjectBLOBDataDeclarationList = new ArrayList<>();
+        while ((headerLength = StreamObjectHeaderStart.tryParse(byteArray, index.get(), header)) !=
+                0) {
+            if (header.get().type == StreamObjectTypeHeaderStart.ObjectGroupObjectDeclare) {
+                index.addAndGet(headerLength);
+                this.objectDeclarationList.add(
+                        (ObjectGroupObjectDeclare) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index));
+            } else if (header.get().type ==
+                    StreamObjectTypeHeaderStart.ObjectGroupObjectBLOBDataDeclaration) {
+                index.addAndGet(headerLength);
+                this.objectGroupObjectBLOBDataDeclarationList.add(
+                        (ObjectGroupObjectBLOBDataDeclaration) StreamObject.parseStreamObject(
+                                header.get(), byteArray, index));
+            } else {
+                throw new StreamObjectParseErrorException(index.get(), "ObjectGroupDeclarations",
+                        "Failed to parse ObjectGroupDeclarations, expect the inner object type either " +
+                                "ObjectGroupObjectDeclare or ObjectGroupObjectBLOBDataDeclaration, " +
+                                "but actual type value is " + header.get().type, null);
+            }
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List
+     *
+     * @param byteList The Byte list
+     * @return A constant value 0
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws TikaException, IOException {
+        if (this.objectDeclarationList != null) {
+            for (ObjectGroupObjectDeclare objectGroupObjectDeclare : this.objectDeclarationList) {
+                byteList.addAll(objectGroupObjectDeclare.serializeToByteList());
+            }
+        }
+
+        if (this.objectGroupObjectBLOBDataDeclarationList != null) {
+            for (ObjectGroupObjectBLOBDataDeclaration objectGroupObjectBLOBDataDeclaration :
+                    this.objectGroupObjectBLOBDataDeclarationList) {
+                byteList.addAll(objectGroupObjectBLOBDataDeclaration.serializeToByteList());
+            }
+        }
+
+        return 0;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupMetadata.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupMetadata.java
new file mode 100644
index 0000000..61981b2
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupMetadata.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.Compact64bitInt;
+
+/**
+ * Specifies an object group metadata
+ */
+public class ObjectGroupMetadata extends StreamObject {
+    /**
+     * Gets or sets a compact unsigned 64-bit integer that specifies the expected change frequency of the object.
+     * This value MUST be:
+     * 0, if the change frequency is not known.
+     * 1, if the object is known to change frequently.
+     * 2, if the object is known to change infrequently.
+     * 3, if the object is known to change independently of any other objects.
+     */
+    public Compact64bitInt ObjectChangeFrequency;
+
+    /**
+     * Initializes a new instance of the ObjectGroupMetadata class.
+     */
+    public ObjectGroupMetadata() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupMetadata);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.ObjectChangeFrequency = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "ObjectGroupMetadata",
+                    "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List
+     *
+     * @param byteList A Byte list
+     * @return The number of elements actually contained in the list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        List<Byte> tmpList = this.ObjectChangeFrequency.serializeToByteList();
+        byteList.addAll(tmpList);
+        return tmpList.size();
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupMetadataDeclarations.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupMetadataDeclarations.java
new file mode 100644
index 0000000..b268779
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupMetadataDeclarations.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+
+/**
+ * Object Metadata Declaration
+ */
+public class ObjectGroupMetadataDeclarations extends StreamObject {
+    public List<ObjectGroupMetadata> objectGroupMetadataList;
+
+    /**
+     * Initializes a new instance of the ObjectGroupMetadataDeclarations class.
+     */
+    public ObjectGroupMetadataDeclarations() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupMetadataDeclarations);
+        this.objectGroupMetadataList = new ArrayList<>();
+    }
+
+    /**
+     * Used to convert the element into a byte List
+     *
+     * @param byteList A Byte list
+     * @return A constant value 0
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws TikaException, IOException {
+        if (this.objectGroupMetadataList != null) {
+            for (ObjectGroupMetadata objectGroupMetadata : this.objectGroupMetadataList) {
+                byteList.addAll(objectGroupMetadata.serializeToByteList());
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        if (lengthOfItems != 0) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "ObjectGroupMetadataDeclarations", "Stream object over-parse error", null);
+        }
+
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        int headerLength;
+        AtomicReference<StreamObjectHeaderStart> header = new AtomicReference<>();
+        this.objectGroupMetadataList = new ArrayList<>();
+
+        while ((headerLength = StreamObjectHeaderStart.tryParse(byteArray, index.get(), header)) !=
+                0) {
+            index.addAndGet(headerLength);
+            if (header.get().type == StreamObjectTypeHeaderStart.ObjectGroupMetadata) {
+                this.objectGroupMetadataList.add(
+                        (ObjectGroupMetadata) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index));
+            } else {
+                throw new StreamObjectParseErrorException(index.get(), "ObjectGroupDeclarations",
+                        "Failed to parse ObjectGroupMetadataDeclarations, expect the inner object type " +
+                                "ObjectGroupMetadata, but actual type value is " +
+                                header.get().type, null);
+            }
+        }
+
+        currentIndex.set(index.get());
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectBLOBDataDeclaration.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectBLOBDataDeclaration.java
new file mode 100644
index 0000000..a86d256
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectBLOBDataDeclaration.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.Compact64bitInt;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+/**
+ * object data BLOB declaration
+ */
+public class ObjectGroupObjectBLOBDataDeclaration extends StreamObject {
+    public ExGuid objectExGUID;
+    public ExGuid objectDataBLOBExGUID;
+    public Compact64bitInt objectPartitionID;
+    /**
+     * Gets or sets a compact unsigned 64-bit integer that specifies the size in bytes of the
+     * object.opaque binary data  for the declared object.
+     * This MUST match the size of the binary item in the corresponding object data BLOB
+     * referenced by the Object Data BLOB reference for this object.
+     */
+    public Compact64bitInt objectDataSize;
+    public Compact64bitInt objectReferencesCount;
+    public Compact64bitInt cellReferencesCount;
+
+    /**
+     * Initializes a new instance of the ObjectGroupObjectBLOBDataDeclaration class.
+     */
+    public ObjectGroupObjectBLOBDataDeclaration() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupObjectBLOBDataDeclaration);
+        this.objectExGUID = new ExGuid();
+        this.objectDataBLOBExGUID = new ExGuid();
+        this.objectPartitionID = new Compact64bitInt();
+        this.objectDataSize = new Compact64bitInt();
+        this.objectReferencesCount = new Compact64bitInt();
+        this.cellReferencesCount = new Compact64bitInt();
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+
+        this.objectExGUID = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.objectDataBLOBExGUID = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.objectPartitionID = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+        this.objectReferencesCount = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+        this.cellReferencesCount = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "ObjectGroupObjectBLOBDataDeclaration", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The number of the element
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.objectExGUID.serializeToByteList());
+        byteList.addAll(this.objectDataBLOBExGUID.serializeToByteList());
+        byteList.addAll(this.objectPartitionID.serializeToByteList());
+        byteList.addAll(this.objectDataSize.serializeToByteList());
+        byteList.addAll(this.objectReferencesCount.serializeToByteList());
+        byteList.addAll(this.cellReferencesCount.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectData.java
new file mode 100644
index 0000000..6d2f3f7
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectData.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BinaryItem;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellIDArray;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGUIDArray;
+
+public class ObjectGroupObjectData extends StreamObject {
+    public ExGUIDArray objectExGUIDArray;
+    public CellIDArray cellIDArray;
+    public BinaryItem data;
+
+    /**
+     * Initializes a new instance of the ObjectGroupObjectData class.
+     */
+    public ObjectGroupObjectData() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupObjectData);
+        this.objectExGUIDArray = new ExGUIDArray();
+        this.cellIDArray = new CellIDArray();
+        this.data = new BinaryItem();
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.objectExGUIDArray = BasicObject.parse(byteArray, index, ExGUIDArray.class);
+        this.cellIDArray = BasicObject.parse(byteArray, index, CellIDArray.class);
+        this.data = BasicObject.parse(byteArray, index, BinaryItem.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "ObjectGroupObjectData",
+                    "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List
+     *
+     * @param byteList A Byte list
+     * @return The number of the element
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.objectExGUIDArray.serializeToByteList());
+        byteList.addAll(this.cellIDArray.serializeToByteList());
+        byteList.addAll(this.data.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectDataBLOBReference.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectDataBLOBReference.java
new file mode 100644
index 0000000..45e577a
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectDataBLOBReference.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellIDArray;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGUIDArray;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+/**
+ * object data BLOB reference
+ */
+public class ObjectGroupObjectDataBLOBReference extends StreamObject {
+    public ExGUIDArray objectExtendedGUIDArray;
+    public CellIDArray cellIDArray;
+    public ExGuid blobExtendedGUID;
+
+    /**
+     * Initializes a new instance of the ObjectGroupObjectDataBLOBReference class.
+     */
+    public ObjectGroupObjectDataBLOBReference() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupObjectDataBLOBReference);
+        this.objectExtendedGUIDArray = new ExGUIDArray();
+        this.cellIDArray = new CellIDArray();
+        this.blobExtendedGUID = new ExGuid();
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.objectExtendedGUIDArray = BasicObject.parse(byteArray, index, ExGUIDArray.class);
+        this.cellIDArray = BasicObject.parse(byteArray, index, CellIDArray.class);
+        this.blobExtendedGUID = BasicObject.parse(byteArray, index, ExGuid.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "ObjectGroupObjectDataBLOBReference", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The number of the elements
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.objectExtendedGUIDArray.serializeToByteList());
+        byteList.addAll(cellIDArray.serializeToByteList());
+        byteList.addAll(this.blobExtendedGUID.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectDeclare.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectDeclare.java
new file mode 100644
index 0000000..3eae87b
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/ObjectGroupObjectDeclare.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+/**
+ * object declaration
+ */
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.Compact64bitInt;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+public class ObjectGroupObjectDeclare extends StreamObject {
+    public ExGuid objectExtendedGUID;
+    public Compact64bitInt objectPartitionID;
+    /**
+     * Gets or sets a compact unsigned 64-bit integer that specifies the size in bytes of the object.binary data opaque
+     * to this protocol for the declared object.
+     * This MUST match the size of the binary item in the corresponding object data for this object.
+     */
+    public Compact64bitInt objectDataSize;
+    public Compact64bitInt objectReferencesCount;
+    public Compact64bitInt cellReferencesCount;
+
+    /**
+     * Initializes a new instance of the ObjectGroupObjectDeclare class.
+     */
+    public ObjectGroupObjectDeclare() {
+        super(StreamObjectTypeHeaderStart.ObjectGroupObjectDeclare);
+        this.objectExtendedGUID = new ExGuid();
+        this.objectPartitionID = new Compact64bitInt();
+        this.objectDataSize = new Compact64bitInt();
+        this.objectReferencesCount = new Compact64bitInt();
+        this.cellReferencesCount = new Compact64bitInt();
+
+        this.objectPartitionID.setDecodedValue(1);
+        this.objectReferencesCount.setDecodedValue(1);
+        this.cellReferencesCount.setDecodedValue(0);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+
+        this.objectExtendedGUID = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.objectPartitionID = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+        this.objectDataSize = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+        this.objectReferencesCount = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+        this.cellReferencesCount = BasicObject.parse(byteArray, index, Compact64bitInt.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "ObjectGroupObjectDeclare", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The number of the element
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.objectExtendedGUID.serializeToByteList());
+        byteList.addAll(this.objectPartitionID.serializeToByteList());
+        byteList.addAll(this.objectDataSize.serializeToByteList());
+        byteList.addAll(this.objectReferencesCount.serializeToByteList());
+        byteList.addAll(this.cellReferencesCount.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/PropertySet.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/PropertySet.java
new file mode 100644
index 0000000..7236354
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/PropertySet.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.ArrayNumber;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.EightBytesOfData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.FourBytesOfData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.IProperty;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.NoData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.OneByteOfData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.PrtArrayOfPropertyValues;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.PrtFourBytesOfLengthFollowedByData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.property.TwoBytesOfData;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.PropertyID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.PropertyType;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitConverter;
+
+/**
+ * This class is used to represent a PropertySet.
+ */
+public class PropertySet implements IProperty {
+    public int cProperties;
+
+    public PropertyID[] rgPrids;
+    public List<IProperty> rgData;
+
+    /**
+     * This method is used to convert the element of PropertySet into a byte List.
+     *
+     * @return Return the byte list which store the byte information of PropertySet.
+     */
+    public List<Byte> serializeToByteList() throws IOException {
+        List<Byte> byteList = new ArrayList<>();
+        for (byte b : BitConverter.getBytes(this.cProperties)) {
+            byteList.add(b);
+        }
+
+        for (PropertyID propertyId : this.rgPrids) {
+            byteList.addAll(propertyId.serializeToByteList());
+        }
+
+        for (IProperty property : this.rgData) {
+            byteList.addAll(property.serializeToByteList());
+        }
+
+        return byteList;
+    }
+
+    /**
+     * This method is used to deserialize the PropertySet from the specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the PropertySet.
+     */
+    public int doDeserializeFromByteArray(byte[] byteArray, int startIndex) throws IOException {
+        int index = startIndex;
+
+        this.cProperties = BitConverter.toInt16(byteArray, startIndex);
+        index += 2;
+        this.rgPrids = new PropertyID[this.cProperties];
+        for (int i = 0; i < this.cProperties; i++) {
+            PropertyID propertyID = new PropertyID();
+            propertyID.doDeserializeFromByteArray(byteArray, index);
+            this.rgPrids[i] = propertyID;
+            index += 4;
+        }
+        this.rgData = new ArrayList<>();
+        for (PropertyID propertyID : this.rgPrids) {
+            IProperty property = null;
+            switch (PropertyType.fromIntVal(propertyID.type)) {
+                case NoData:
+                case Bool:
+                case ObjectID:
+                case ContextID:
+                case ObjectSpaceID:
+                    property = new NoData();
+                    break;
+                case ArrayOfObjectIDs:
+                case ArrayOfObjectSpaceIDs:
+                case ArrayOfContextIDs:
+                    property = new ArrayNumber();
+                    break;
+                case OneByteOfData:
+                    property = new OneByteOfData();
+                    break;
+                case TwoBytesOfData:
+                    property = new TwoBytesOfData();
+                    break;
+                case FourBytesOfData:
+                    property = new FourBytesOfData();
+                    break;
+                case EightBytesOfData:
+                    property = new EightBytesOfData();
+                    break;
+                case FourBytesOfLengthFollowedByData:
+                    property = new PrtFourBytesOfLengthFollowedByData();
+                    break;
+                case ArrayOfPropertyValues:
+                    property = new PrtArrayOfPropertyValues();
+                    break;
+                case PropertySet:
+                    property = new PropertySet();
+                    break;
+                default:
+                    break;
+            }
+            if (property != null) {
+                int len = property.doDeserializeFromByteArray(byteArray, index);
+                this.rgData.add(property);
+                index += len;
+            }
+        }
+
+        return index - startIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/PropertySetObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/PropertySetObject.java
new file mode 100644
index 0000000..46dda50
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/PropertySetObject.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.space.ObjectSpaceObjectPropSet;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+
+/**
+ * This class is used to represent the property set.
+ */
+public class PropertySetObject {
+    public ObjectGroupObjectDeclare objectDeclaration;
+    public ObjectSpaceObjectPropSet objectSpaceObjectPropSet;
+
+    /**
+     * Construct the PropertySetObject instance.
+     *
+     * @param objectDeclaration The Object Declaration structure.
+     * @param objectData        The Object Data structure.
+     */
+    public PropertySetObject(ObjectGroupObjectDeclare objectDeclaration,
+                             ObjectGroupObjectData objectData) throws IOException {
+        this.objectDeclaration = objectDeclaration;
+        this.objectSpaceObjectPropSet = new ObjectSpaceObjectPropSet();
+        this.objectSpaceObjectPropSet.doDeserializeFromByteArray(
+                ByteUtil.toByteArray(objectData.data.content), 0);
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifest.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifest.java
new file mode 100644
index 0000000..001a75a
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+public class RevisionManifest extends StreamObject {
+    public ExGuid revisionID;
+    public ExGuid baseRevisionID;
+
+    /**
+     * Initializes a new instance of the RevisionManifest class.
+     */
+    public RevisionManifest() {
+        super(StreamObjectTypeHeaderStart.RevisionManifest);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.revisionID = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.baseRevisionID = BasicObject.parse(byteArray, index, ExGuid.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "RevisionManifest",
+                    "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The length of list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.revisionID.serializeToByteList());
+        byteList.addAll(this.baseRevisionID.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestDataElementData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestDataElementData.java
new file mode 100644
index 0000000..74695d1
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestDataElementData.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.exception.DataElementParseErrorException;
+
+public class RevisionManifestDataElementData extends DataElementData {
+    public RevisionManifest revisionManifest;
+    public List<RevisionManifestRootDeclare> revisionManifestRootDeclareList;
+    public List<RevisionManifestObjectGroupReferences> revisionManifestObjectGroupReferences;
+
+    /**
+     * Initializes a new instance of the RevisionManifestDataElementData class.
+     */
+    public RevisionManifestDataElementData() {
+        this.revisionManifest = new RevisionManifest();
+        this.revisionManifestRootDeclareList = new ArrayList<>();
+        this.revisionManifestObjectGroupReferences = new ArrayList<>();
+    }
+
+    /**
+     * Used to return the length of this element.
+     *
+     * @param byteArray  A Byte list
+     * @param startIndex Start position
+     * @return The length of the element
+     */
+    @Override
+    public int deserializeDataElementDataFromByteArray(byte[] byteArray, int startIndex)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(startIndex);
+        this.revisionManifest = StreamObject.getCurrent(byteArray, index, RevisionManifest.class);
+
+        this.revisionManifestRootDeclareList = new ArrayList<>();
+        this.revisionManifestObjectGroupReferences = new ArrayList<>();
+        AtomicReference<StreamObjectHeaderStart> header = new AtomicReference<>();
+        int headerLength = 0;
+        while ((headerLength = StreamObjectHeaderStart.tryParse(byteArray, index.get(), header)) !=
+                0) {
+            if (header.get().type == StreamObjectTypeHeaderStart.RevisionManifestRootDeclare) {
+                index.addAndGet(headerLength);
+                this.revisionManifestRootDeclareList.add(
+                        (RevisionManifestRootDeclare) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index));
+            } else if (header.get().type ==
+                    StreamObjectTypeHeaderStart.RevisionManifestObjectGroupReferences) {
+                index.addAndGet(headerLength);
+                this.revisionManifestObjectGroupReferences.add(
+                        (RevisionManifestObjectGroupReferences) StreamObject.parseStreamObject(
+                                header.get(), byteArray, index));
+            } else {
+                throw new DataElementParseErrorException(index.get(),
+                        "Failed to parse RevisionManifestDataElement, expect the inner object type " +
+                                "RevisionManifestRootDeclare or RevisionManifestObjectGroupReferences, " +
+                                "but actual type value is " + header.get().type, null);
+            }
+        }
+
+        return index.get() - startIndex;
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @return A Byte list
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws TikaException, IOException {
+        List<Byte> byteList = new ArrayList<>();
+        byteList.addAll(this.revisionManifest.serializeToByteList());
+
+        if (this.revisionManifestRootDeclareList != null) {
+            for (RevisionManifestRootDeclare revisionManifestRootDeclare : this.revisionManifestRootDeclareList) {
+                byteList.addAll(revisionManifestRootDeclare.serializeToByteList());
+            }
+        }
+
+        if (this.revisionManifestObjectGroupReferences != null) {
+            for (RevisionManifestObjectGroupReferences revisionManifestObjectGroupReferences :
+                    this.revisionManifestObjectGroupReferences) {
+                byteList.addAll(revisionManifestObjectGroupReferences.serializeToByteList());
+            }
+        }
+
+        return byteList;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestObjectGroupReferences.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestObjectGroupReferences.java
new file mode 100644
index 0000000..ceb92bc
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestObjectGroupReferences.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+/**
+ * Specifies a revision manifest object group references, each followed by object group extended GUIDs
+ */
+public class RevisionManifestObjectGroupReferences extends StreamObject {
+    public ExGuid objectGroupExtendedGUID;
+
+    /**
+     * Initializes a new instance of the RevisionManifestObjectGroupReferences class.
+     */
+    public RevisionManifestObjectGroupReferences() {
+        super(StreamObjectTypeHeaderStart.RevisionManifestObjectGroupReferences);
+    }
+
+    /**
+     * Initializes a new instance of the RevisionManifestObjectGroupReferences class.
+     *
+     * @param objectGroupExtendedGUID Extended GUID
+     */
+    public RevisionManifestObjectGroupReferences(ExGuid objectGroupExtendedGUID) {
+        super(StreamObjectTypeHeaderStart.RevisionManifestObjectGroupReferences);
+        this.objectGroupExtendedGUID = objectGroupExtendedGUID;
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.objectGroupExtendedGUID = BasicObject.parse(byteArray, index, ExGuid.class);
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "RevisionManifestObjectGroupReferences", "Stream object over-parse error",
+                    null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The number of elements actually contained in the list.
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        List<Byte> tmpList = this.objectGroupExtendedGUID.serializeToByteList();
+        byteList.addAll(tmpList);
+        return tmpList.size();
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestRootDeclare.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestRootDeclare.java
new file mode 100644
index 0000000..6861940
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionManifestRootDeclare.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+/**
+ * Specifies a revision manifest root declare, each followed by root and object extended GUIDs
+ */
+public class RevisionManifestRootDeclare extends StreamObject {
+    public ExGuid rootExGuid;
+    public ExGuid objectExGuid;
+
+    /**
+     * Initializes a new instance of the RevisionManifestRootDeclare class.
+     */
+    public RevisionManifestRootDeclare() {
+        super(StreamObjectTypeHeaderStart.RevisionManifestRootDeclare);
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte list
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.rootExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.objectExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "RevisionManifestRootDeclare", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The length of list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.rootExGuid.serializeToByteList());
+        byteList.addAll(this.objectExGuid.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionStoreObject.java
similarity index 51%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionStoreObject.java
index aa01c18..5885262 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodeList.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionStoreObject.java
@@ -14,30 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-import java.util.ArrayList;
-import java.util.List;
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
 
-class FileNodeList {
-    FileNodeListHeader fileNodeListHeader;
-    List<FileNode> children = new ArrayList<>();
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellIDArray;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGUIDArray;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
 
-    public FileNodeListHeader getFileNodeListHeader() {
-        return fileNodeListHeader;
-    }
 
-    public FileNodeList setFileNodeListHeader(FileNodeListHeader fileNodeListHeader) {
-        this.fileNodeListHeader = fileNodeListHeader;
-        return this;
-    }
+/**
+ * The class is used to represent the revision store object.
+ */
+public class RevisionStoreObject {
 
-    public List<FileNode> getChildren() {
-        return children;
-    }
+    public ExGuid objectID;
+    public ExGuid objectGroupID;
+    public JCIDObject jcid;
+    public PropertySetObject propertySet;
+    public FileDataObject
+            fileDataObject;
+    public ExGUIDArray referencedObjectID;
+    public CellIDArray referencedObjectSpacesID;
+
+    /**
+     * Initialize the class.
+     */
+    public RevisionStoreObject() {
 
-    public FileNodeList setChildren(List<FileNode> children) {
-        this.children = children;
-        return this;
     }
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionStoreObjectGroup.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionStoreObjectGroup.java
new file mode 100644
index 0000000..48b7daa
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/RevisionStoreObjectGroup.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+public class RevisionStoreObjectGroup {
+    public ExGuid objectGroupID;
+    public List<RevisionStoreObject> objects;
+    public List<EncryptionObject> encryptionObjects;
+
+    public RevisionStoreObjectGroup(ExGuid objectGroupId) {
+        this.objects = new ArrayList<>();
+        this.encryptionObjects = new ArrayList<>();
+        this.objectGroupID = objectGroupId;
+    }
+
+    public static RevisionStoreObjectGroup createInstance(ExGuid objectGroupId,
+                                                          ObjectGroupDataElementData dataObject,
+                                                          boolean isEncryption) throws IOException {
+        RevisionStoreObjectGroup objectGroup = new RevisionStoreObjectGroup(objectGroupId);
+        Map<ExGuid, RevisionStoreObject> objectDict = new HashMap<>();
+        if (!isEncryption) {
+            RevisionStoreObject revisionObject = null;
+            for (int i = 0; i < dataObject.objectGroupDeclarations.objectDeclarationList.size();
+                    i++) {
+                ObjectGroupObjectDeclare objectDeclaration =
+                        dataObject.objectGroupDeclarations.objectDeclarationList.get(i);
+                ObjectGroupObjectData objectData =
+                        dataObject.objectGroupData.objectGroupObjectDataList.get(i);
+
+                if (!objectDict.containsKey(objectDeclaration.objectExtendedGUID)) {
+                    revisionObject = new RevisionStoreObject();
+                    revisionObject.objectGroupID = objectGroupId;
+                    revisionObject.objectID = objectDeclaration.objectExtendedGUID;
+                    objectDict.put(objectDeclaration.objectExtendedGUID, revisionObject);
+                } else {
+                    revisionObject = objectDict.get(objectDeclaration.objectExtendedGUID);
+                }
+                if (objectDeclaration.objectPartitionID.getDecodedValue() == 4) {
+                    revisionObject.jcid = new JCIDObject(objectDeclaration, objectData);
+                } else if (objectDeclaration.objectPartitionID.getDecodedValue() == 1) {
+                    revisionObject.propertySet =
+                            new PropertySetObject(objectDeclaration, objectData);
+                    if (revisionObject.jcid.jcid.isFileData != 0) {
+                        revisionObject.referencedObjectID = objectData.objectExGUIDArray;
+                        revisionObject.referencedObjectSpacesID = objectData.cellIDArray;
+                    }
+                }
+            }
+
+            for (int i = 0; i <
+                    dataObject.objectGroupDeclarations.objectGroupObjectBLOBDataDeclarationList.size();
+                    i++) {
+                ObjectGroupObjectBLOBDataDeclaration objectGroupObjectBLOBDataDeclaration =
+                        dataObject.objectGroupDeclarations.objectGroupObjectBLOBDataDeclarationList.get(
+                                i);
+                ObjectGroupObjectDataBLOBReference objectGroupObjectDataBLOBReference =
+                        dataObject.objectGroupData.objectGroupObjectDataBLOBReferenceList.get(i);
+                if (!objectDict.containsKey(objectGroupObjectBLOBDataDeclaration.objectExGUID)) {
+                    revisionObject = new RevisionStoreObject();
+                    objectDict.put(objectGroupObjectBLOBDataDeclaration.objectExGUID,
+                            revisionObject);
+                } else {
+                    revisionObject =
+                            objectDict.get(objectGroupObjectBLOBDataDeclaration.objectExGUID);
+                }
+                if (objectGroupObjectBLOBDataDeclaration.objectPartitionID.getDecodedValue() == 2) {
+                    revisionObject.fileDataObject = new FileDataObject();
+                    revisionObject.fileDataObject.objectDataBLOBDeclaration =
+                            objectGroupObjectBLOBDataDeclaration;
+                    revisionObject.fileDataObject.objectDataBLOBReference =
+                            objectGroupObjectDataBLOBReference;
+                }
+            }
+            objectGroup.objects.addAll(objectDict.values());
+        } else {
+            for (int i = 0; i < dataObject.objectGroupDeclarations.objectDeclarationList.size();
+                    i++) {
+                ObjectGroupObjectDeclare objectDeclaration =
+                        dataObject.objectGroupDeclarations.objectDeclarationList.get(i);
+                ObjectGroupObjectData objectData =
+                        dataObject.objectGroupData.objectGroupObjectDataList.get(i);
+
+                if (objectDeclaration.objectPartitionID.getDecodedValue() == 1) {
+                    EncryptionObject encrypObject = new EncryptionObject();
+                    encrypObject.objectDeclaration = objectDeclaration;
+                    encrypObject.objectData = ByteUtil.toByteArray(objectData.data.content);
+                    objectGroup.encryptionObjects.add(encrypObject);
+                }
+            }
+        }
+
+        return objectGroup;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/SignatureObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/SignatureObject.java
new file mode 100644
index 0000000..535ba30
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/SignatureObject.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BinaryItem;
+
+/**
+ * Signature Object
+ */
+public class SignatureObject extends StreamObject {
+    /**
+     * Gets or sets a binary item as specified in [MS-FSSHTTPB] section 2.2.1.3 that specifies a
+     * value that is unique to the file data represented by this root node object.
+     * The value of this item depends on the file chunking algorithm used, as specified in section 2.4.
+     */
+    public BinaryItem signatureData;
+
+    /**
+     * Initializes a new instance of the SignatureObject class.
+     */
+    public SignatureObject() {
+        super(StreamObjectTypeHeaderStart.SignatureObject);
+        this.signatureData = new BinaryItem();
+    }
+
+    /**
+     * Used to de-serialize the element.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+
+        this.signatureData = BasicObject.parse(byteArray, index, BinaryItem.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "Signature",
+                    "Stream Object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The number of elements
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int length = byteList.size();
+        byteList.addAll(this.signatureData.serializeToByteList());
+        return byteList.size() - length;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexCellMapping.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexCellMapping.java
new file mode 100644
index 0000000..101e4f7
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexCellMapping.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.SerialNumber;
+
+/**
+ * Specifies the storage index cell mappings (with cell identifier, cell mapping extended GUID,
+ * and cell mapping serial number)
+ */
+public class StorageIndexCellMapping extends StreamObject {
+    public CellID cellID;
+    public ExGuid cellMappingExGuid;
+    public SerialNumber cellMappingSerialNumber;
+
+    /**
+     * Initializes a new instance of the StorageIndexCellMapping class.
+     */
+    public StorageIndexCellMapping() {
+        super(StreamObjectTypeHeaderStart.StorageIndexCellMapping);
+    }
+
+    /**
+     * Used to de-serialize the items.
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.cellID = BasicObject.parse(byteArray, index, CellID.class);
+        this.cellMappingExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.cellMappingSerialNumber = BasicObject.parse(byteArray, index, SerialNumber.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(), "StorageIndexCellMapping",
+                    "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The length of list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.cellID.serializeToByteList());
+        byteList.addAll(this.cellMappingExGuid.serializeToByteList());
+        byteList.addAll(this.cellMappingSerialNumber.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexDataElementData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexDataElementData.java
new file mode 100644
index 0000000..2e84ab9
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexDataElementData.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.exception.DataElementParseErrorException;
+
+public class StorageIndexDataElementData extends DataElementData {
+    public StorageIndexManifestMapping storageIndexManifestMapping;
+    public List<StorageIndexCellMapping> storageIndexCellMappingList;
+    public List<StorageIndexRevisionMapping> storageIndexRevisionMappingList;
+
+    /**
+     * Initializes a new instance of the StorageIndexDataElementData class.
+     */
+    public StorageIndexDataElementData() {
+        this.storageIndexManifestMapping = new StorageIndexManifestMapping();
+        this.storageIndexCellMappingList = new ArrayList<>();
+        this.storageIndexRevisionMappingList = new ArrayList<>();
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @return A Byte list
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws TikaException, IOException {
+        List<Byte> byteList = new ArrayList<>();
+
+        if (this.storageIndexManifestMapping != null) {
+            byteList.addAll(this.storageIndexManifestMapping.serializeToByteList());
+        }
+
+        if (this.storageIndexCellMappingList != null) {
+            for (StorageIndexCellMapping cellMapping : this.storageIndexCellMappingList) {
+                byteList.addAll(cellMapping.serializeToByteList());
+            }
+        }
+
+        // Storage Index Revision Mapping
+        if (this.storageIndexRevisionMappingList != null) {
+            for (StorageIndexRevisionMapping revisionMapping : this.storageIndexRevisionMappingList) {
+                byteList.addAll(revisionMapping.serializeToByteList());
+            }
+        }
+
+        return byteList;
+    }
+
+    /**
+     * Used to de-serialize the data element.
+     *
+     * @param byteArray  Byte array
+     * @param startIndex Start position
+     * @return The length of the element
+     */
+    @Override
+    public int deserializeDataElementDataFromByteArray(byte[] byteArray, int startIndex)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(startIndex);
+        int headerLength = 0;
+        AtomicReference<StreamObjectHeaderStart> header = new AtomicReference<>();
+        boolean isStorageIndexManifestMappingExist = false;
+        while ((headerLength = StreamObjectHeaderStart.tryParse(byteArray, index.get(), header)) !=
+                0) {
+            index.addAndGet(headerLength);
+            if (header.get().type == StreamObjectTypeHeaderStart.StorageIndexManifestMapping) {
+                if (isStorageIndexManifestMappingExist) {
+                    throw new DataElementParseErrorException(index.get() - headerLength,
+                            "Failed to parse StorageIndexDataElement, only can contain zero or one " +
+                                    "StorageIndexManifestMapping", null);
+                }
+
+                this.storageIndexManifestMapping =
+                        (StorageIndexManifestMapping) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index);
+                isStorageIndexManifestMappingExist = true;
+            } else if (header.get().type == StreamObjectTypeHeaderStart.StorageIndexCellMapping) {
+                this.storageIndexCellMappingList.add(
+                        (StorageIndexCellMapping) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index));
+            } else if (header.get().type ==
+                    StreamObjectTypeHeaderStart.StorageIndexRevisionMapping) {
+                this.storageIndexRevisionMappingList.add(
+                        (StorageIndexRevisionMapping) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index));
+            } else {
+                throw new DataElementParseErrorException(index.get() - headerLength,
+                        "Failed to parse StorageIndexDataElement, expect the inner object type " +
+                                "StorageIndexCellMapping or StorageIndexRevisionMapping, but actual type value is " +
+                                header.get().type, null);
+            }
+        }
+
+        return index.get() - startIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexManifestMapping.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexManifestMapping.java
new file mode 100644
index 0000000..50e7b0c
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexManifestMapping.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.SerialNumber;
+
+public class StorageIndexManifestMapping extends StreamObject {
+    public ExGuid manifestMappingExGuid;
+    public SerialNumber manifestMappingSerialNumber;
+
+    /**
+     * Initializes a new instance of the StorageIndexManifestMapping class.
+     */
+    public StorageIndexManifestMapping() {
+        super(StreamObjectTypeHeaderStart.StorageIndexManifestMapping);
+    }
+
+    /**
+     * Used to Deserialize the items.
+     *
+     * @param byteArray     Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.manifestMappingExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.manifestMappingSerialNumber = BasicObject.parse(byteArray, index, SerialNumber.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "StorageIndexManifestMapping", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The length of list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.manifestMappingExGuid.serializeToByteList());
+        byteList.addAll(this.manifestMappingSerialNumber.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexRevisionMapping.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexRevisionMapping.java
new file mode 100644
index 0000000..2805750
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageIndexRevisionMapping.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.SerialNumber;
+
+/**
+ * Specifies the storage index revision mappings (with revision and revision mapping
+ * extended GUIDs, and revision mapping serial number)
+ */
+public class StorageIndexRevisionMapping extends StreamObject {
+    public ExGuid revisionExGuid;
+    public ExGuid revisionMappingExGuid;
+    public SerialNumber revisionMappingSerialNumber;
+
+    /**
+     * Initializes a new instance of the StorageIndexRevisionMapping class.
+     */
+    public StorageIndexRevisionMapping() {
+        super(StreamObjectTypeHeaderStart.StorageIndexRevisionMapping);
+    }
+
+    /**
+     * Used to de-serialize the items
+     *
+     * @param byteArray     A Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.revisionExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.revisionMappingExGuid = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.revisionMappingSerialNumber = BasicObject.parse(byteArray, index, SerialNumber.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "StorageIndexRevisionMapping", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The length of list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.revisionExGuid.serializeToByteList());
+        byteList.addAll(this.revisionMappingExGuid.serializeToByteList());
+        byteList.addAll(this.revisionMappingSerialNumber.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestDataElementData.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestDataElementData.java
new file mode 100644
index 0000000..d3162e1
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestDataElementData.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.exception.DataElementParseErrorException;
+
+public class StorageManifestDataElementData extends DataElementData {
+    public StorageManifestSchemaGUID storageManifestSchemaGUID;
+    public List<StorageManifestRootDeclare> storageManifestRootDeclareList;
+
+    /**
+     * Initializes a new instance of the StorageManifestDataElementData class.
+     */
+    public StorageManifestDataElementData() {
+        // Storage Manifest
+        this.storageManifestSchemaGUID = new StorageManifestSchemaGUID();
+        this.storageManifestRootDeclareList = new ArrayList<>();
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @return A Byte list
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws TikaException, IOException {
+        List<Byte> byteList = new ArrayList<Byte>();
+        byteList.addAll(this.storageManifestSchemaGUID.serializeToByteList());
+
+        if (this.storageManifestRootDeclareList != null) {
+            for (StorageManifestRootDeclare storageManifestRootDeclare : this.storageManifestRootDeclareList) {
+                byteList.addAll(storageManifestRootDeclare.serializeToByteList());
+            }
+        }
+
+        return byteList;
+    }
+
+    /**
+     * Used to de-serialize data element.
+     *
+     * @param byteArray  Byte array
+     * @param startIndex Start position
+     * @return The length of the array
+     */
+    @Override
+    public int deserializeDataElementDataFromByteArray(byte[] byteArray, int startIndex)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(startIndex);
+
+        this.storageManifestSchemaGUID =
+                StreamObject.getCurrent(byteArray, index, StorageManifestSchemaGUID.class);
+        this.storageManifestRootDeclareList = new ArrayList<>();
+
+        AtomicReference<StreamObjectHeaderStart> header = new AtomicReference<>();
+        int headerLength = 0;
+        while ((headerLength = StreamObjectHeaderStart.tryParse(byteArray, index.get(), header)) !=
+                0) {
+            if (header.get().type == StreamObjectTypeHeaderStart.StorageManifestRootDeclare) {
+                index.addAndGet(headerLength);
+                this.storageManifestRootDeclareList.add(
+                        (StorageManifestRootDeclare) StreamObject.parseStreamObject(header.get(),
+                                byteArray, index));
+            } else {
+                throw new DataElementParseErrorException(index.get(),
+                        "Failed to parse StorageManifestDataElement, expect the inner object type " +
+                                "StorageManifestRootDeclare, but actual type value is " +
+                                header.get().type, null);
+            }
+        }
+
+        return index.get() - startIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestRootDeclare.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestRootDeclare.java
new file mode 100644
index 0000000..1a5c54a
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestRootDeclare.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.CellID;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.ExGuid;
+
+/**
+ * Specifies one or more storage manifest root declare.
+ */
+public class StorageManifestRootDeclare extends StreamObject {
+    public ExGuid rootExGUID;
+    public CellID cellID;
+
+    /**
+     * Initializes a new instance of the StorageManifestRootDeclare class.
+     */
+    public StorageManifestRootDeclare() {
+        super(StreamObjectTypeHeaderStart.StorageManifestRootDeclare);
+    }
+
+    /**
+     * Used to de-serialize the items.
+     *
+     * @param byteArray     Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems)
+            throws TikaException, IOException {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        this.rootExGUID = BasicObject.parse(byteArray, index, ExGuid.class);
+        this.cellID = BasicObject.parse(byteArray, index, CellID.class);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "StorageManifestRootDeclare", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return The length of list
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) throws IOException {
+        int itemsIndex = byteList.size();
+        byteList.addAll(this.rootExGUID.serializeToByteList());
+        byteList.addAll(this.cellID.serializeToByteList());
+        return byteList.size() - itemsIndex;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestSchemaGUID.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestSchemaGUID.java
new file mode 100644
index 0000000..70f7990
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StorageManifestSchemaGUID.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+
+/**
+ * Specifies a storage manifest schema GUID
+ */
+public class StorageManifestSchemaGUID extends StreamObject {
+    public UUID guid;
+
+    /**
+     * Initializes a new instance of the StorageManifestSchemaGUID class.
+     */
+    public StorageManifestSchemaGUID() {
+        super(StreamObjectTypeHeaderStart.StorageManifestSchemaGUID);
+        // this.GUID = DataElementExGuids.StorageManifestGUID;
+    }
+
+    /**
+     * Used to de-serialize the items.
+     *
+     * @param byteArray     Byte array
+     * @param currentIndex  Start position
+     * @param lengthOfItems The length of the items
+     */
+    @Override
+    protected void deserializeItemsFromByteArray(byte[] byteArray, AtomicInteger currentIndex,
+                                                 int lengthOfItems) {
+        AtomicInteger index = new AtomicInteger(currentIndex.get());
+        byte[] temp = Arrays.copyOf(byteArray, 16);
+        this.guid = UUID.nameUUIDFromBytes(temp);
+        index.addAndGet(16);
+
+        if (index.get() - currentIndex.get() != lengthOfItems) {
+            throw new StreamObjectParseErrorException(currentIndex.get(),
+                    "StorageManifestSchemaGUID", "Stream object over-parse error", null);
+        }
+
+        currentIndex.set(index.get());
+    }
+
+    /**
+     * Used to convert the element into a byte List.
+     *
+     * @param byteList A Byte list
+     * @return A constant value 16
+     */
+    @Override
+    protected int serializeItemsToByteList(List<Byte> byteList) {
+        byteList.addAll(
+                ByteUtil.toListOfByte(this.guid.toString().getBytes(StandardCharsets.UTF_8)));
+        return 16;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObject.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObject.java
new file mode 100644
index 0000000..81192b2
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObject.java
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.IFSSHTTPBSerializable;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitReader;
+
+public abstract class StreamObject implements IFSSHTTPBSerializable {
+
+    /**
+     * Hash set contains the StreamObjectTypeHeaderStart type.
+     */
+    private static final Set<StreamObjectTypeHeaderStart> compoundTypes = new HashSet<>(
+            Arrays.asList(StreamObjectTypeHeaderStart.DataElement,
+                    StreamObjectTypeHeaderStart.Knowledge,
+                    StreamObjectTypeHeaderStart.CellKnowledge,
+                    StreamObjectTypeHeaderStart.DataElementPackage,
+                    StreamObjectTypeHeaderStart.ObjectGroupDeclarations,
+                    StreamObjectTypeHeaderStart.ObjectGroupData,
+                    StreamObjectTypeHeaderStart.WaterlineKnowledge,
+                    StreamObjectTypeHeaderStart.ContentTagKnowledge,
+                    StreamObjectTypeHeaderStart.Request,
+                    StreamObjectTypeHeaderStart.FsshttpbSubResponse,
+                    StreamObjectTypeHeaderStart.SubRequest,
+                    StreamObjectTypeHeaderStart.ReadAccessResponse,
+                    StreamObjectTypeHeaderStart.SpecializedKnowledge,
+                    StreamObjectTypeHeaderStart.WriteAccessResponse,
+                    StreamObjectTypeHeaderStart.QueryChangesFilter,
+                    StreamObjectTypeHeaderStart.ResponseError,
+                    StreamObjectTypeHeaderStart.UserAgent,
+                    StreamObjectTypeHeaderStart.FragmentKnowledge,
+                    StreamObjectTypeHeaderStart.ObjectGroupMetadataDeclarations,
+                    StreamObjectTypeHeaderStart.LeafNodeObject,
+                    StreamObjectTypeHeaderStart.IntermediateNodeObject,
+                    StreamObjectTypeHeaderStart.TargetPartitionId));
+
+    /**
+     * The dictionary of StreamObjectTypeHeaderStart and type.
+     */
+    private static final Map<StreamObjectTypeHeaderStart, Class> streamObjectTypeMapping;
+
+    static {
+        streamObjectTypeMapping = new HashMap<>();
+        for (StreamObjectTypeHeaderStart value : StreamObjectTypeHeaderStart.values()) {
+            String className = StreamObject.class.getPackage().getName() + "." + value.name();
+            try {
+                streamObjectTypeMapping.put(value, Class.forName(className));
+            } catch (ClassNotFoundException e) {
+                // This is OK, we are not pulling over every single class
+            }
+        }
+    }
+
+    StreamObjectHeaderEnd streamObjectHeaderEnd;
+    /**
+     * Gets the StreamObjectTypeHeaderStart.
+     */
+    private StreamObjectTypeHeaderStart streamObjectType;
+    /**
+     * Gets the length of items.
+     */
+    private int lengthOfItems;
+    private StreamObjectHeaderStart streamObjectHeaderStart;
+
+    /**
+     * Initializes a new instance of the StreamObject class.
+     *
+     * @param streamObjectType The instance of StreamObjectTypeHeaderStart.
+     */
+    protected StreamObject(StreamObjectTypeHeaderStart streamObjectType) {
+        this.streamObjectType = streamObjectType;
+    }
+
+    /**
+     * Gets the StreamObjectTypeHeaderStart
+     */
+    public static Set<StreamObjectTypeHeaderStart> getCompoundTypes() {
+        return compoundTypes;
+    }
+
+    /**
+     * Gets the StreamObjectTypeMapping
+     */
+    public static Map<StreamObjectTypeHeaderStart, Class> getStreamObjectTypeMapping() {
+        return streamObjectTypeMapping;
+    }
+
+    /**
+     * Get current stream object.
+     *
+     * @param byteArray The byte array which contains message.
+     * @param index     The position where to start.
+     * @return The current object instance.
+     */
+    public static <T extends StreamObject> T getCurrent(byte[] byteArray, AtomicInteger index,
+                                                        Class<T> clazz)
+            throws TikaException, IOException {
+        AtomicInteger tmpIndex = new AtomicInteger(index.get());
+        int length;
+        AtomicReference<StreamObjectHeaderStart> streamObjectHeader = new AtomicReference<>();
+        if ((length =
+                StreamObjectHeaderStart.tryParse(byteArray, tmpIndex.get(), streamObjectHeader)) ==
+                0) {
+            throw new StreamObjectParseErrorException(tmpIndex.get(), clazz.getName(),
+                    "Failed to extract either 16bit or 32bit stream object header in the current index.",
+                    null);
+        }
+
+        tmpIndex.addAndGet(length);
+
+        StreamObject streamObject =
+                parseStreamObject(streamObjectHeader.get(), byteArray, tmpIndex);
+
+        if (!streamObject.getClass().equals(clazz)) {
+            String destClassName = "(null)";
+            if (streamObjectTypeMapping.containsKey(streamObjectHeader.get().type)) {
+                destClassName =
+                        streamObjectTypeMapping.get(streamObjectHeader.get().type).getName();
+            }
+            throw new StreamObjectParseErrorException(tmpIndex.get(), clazz.getName(),
+                    String.format(Locale.US,
+                            "Failed to get stream object as expect type %s, actual type is %s",
+                            clazz.getName(), destClassName), null);
+        }
+
+        // Store the current index to the ref parameter index.
+        index.set(tmpIndex.get());
+        return (T) streamObject;
+    }
+
+    /**
+     * Parse stream object from byte array.
+     *
+     * @param header    The instance of StreamObjectHeaderStart.
+     * @param byteArray The byte array.
+     * @param index     The position where to start.
+     * @return The instance of StreamObject.
+     */
+    public static StreamObject parseStreamObject(StreamObjectHeaderStart header, byte[] byteArray,
+                                                 AtomicInteger index) throws IOException, TikaException {
+        if (streamObjectTypeMapping.containsKey(header.type)) {
+            Class headerTypeClass = streamObjectTypeMapping.get(header.type);
+            StreamObject streamObject;
+            try {
+                streamObject = (StreamObject) headerTypeClass.newInstance();
+            } catch (InstantiationException | IllegalAccessException e) {
+                throw new TikaException("Could not instantiate class " + headerTypeClass, e);
+            }
+
+            int res = streamObject.deserializeFromByteArray(header, byteArray, index.get());
+            index.addAndGet(res);
+
+            return streamObject;
+        }
+
+        int tmpIndex = index.get();
+        tmpIndex -=
+                header.headerType == StreamObjectHeaderStart.STREAM_OBJECT_HEADER_START_16_BIT ? 2 : 4;
+        throw new StreamObjectParseErrorException(tmpIndex, "Unknown", String.format(Locale.US,
+                "Failed to create the specified stream object instance, the type %s of stream object " +
+                        "header in the current index is not defined", header.type.getIntVal()),
+                null);
+    }
+
+    /**
+     * Try to get current object, true will returned if success.
+     *
+     * @param byteArray    The byte array.
+     * @param index        The position where to start.
+     * @param streamObject The instance that want to get.
+     * @return The result of whether get success.
+     */
+
+    public static <T extends StreamObject> boolean tryGetCurrent(byte[] byteArray,
+                                                                 AtomicInteger index,
+                                                                 AtomicReference<T> streamObject,
+                                                                 Class<T> clazz)
+            throws TikaException, IOException {
+        AtomicInteger tmpIndex = new AtomicInteger(index.get());
+
+        int length = 0;
+        AtomicReference<StreamObjectHeaderStart> streamObjectHeader = new AtomicReference<>();
+        if ((length =
+                StreamObjectHeaderStart.tryParse(byteArray, tmpIndex.get(), streamObjectHeader)) ==
+                0) {
+            return false;
+        }
+
+        tmpIndex.addAndGet(length);
+        if (streamObjectTypeMapping.containsKey(streamObjectHeader.get().type) &&
+                streamObjectTypeMapping.get(streamObjectHeader.get().type).equals(clazz)) {
+            streamObject.set((T) parseStreamObject(streamObjectHeader.get(), byteArray, tmpIndex));
+        } else {
+            return false;
+        }
+
+        index.set(tmpIndex.get());
+        return true;
+    }
+
+    /**
+     * Serialize item to byte list.
+     *
+     * @return The byte list.
+     */
+    public List<Byte> serializeToByteList() throws IOException, TikaException {
+        List<Byte> byteList = new ArrayList<>();
+
+        int lengthOfItems = this.serializeItemsToByteList(byteList);
+
+        AtomicReference<StreamObjectHeaderStart> header = new AtomicReference<>();
+        if (this.streamObjectType.getIntVal() <= 0x3F && lengthOfItems <= 127) {
+            header.set(new StreamObjectHeaderStart16bit(this.streamObjectType, lengthOfItems));
+        } else {
+            header.set(new StreamObjectHeaderStart32bit(this.streamObjectType, lengthOfItems));
+        }
+
+        byteList.addAll(0, header.get().serializeToByteList());
+
+        if (compoundTypes.contains(this.streamObjectType)) {
+            if (this.streamObjectType.getIntVal() <= 0x3F) {
+                byteList.addAll(new StreamObjectHeaderEnd8bit(
+                        this.streamObjectType.getIntVal()).serializeToByteList());
+            } else {
+                byteList.addAll(new StreamObjectHeaderEnd16bit(
+                        this.streamObjectType.getIntVal()).serializeToByteList());
+            }
+        }
+
+        return byteList;
+    }
+
+    /**
+     * Used to return the length of this element.
+     *
+     * @param header     Then instance of StreamObjectHeaderStart.
+     * @param byteArray  The byte list
+     * @param startIndex The position where to start.
+     * @return The element length
+     */
+    public int deserializeFromByteArray(StreamObjectHeaderStart header, byte[] byteArray,
+                                        int startIndex) throws IOException, TikaException {
+        this.streamObjectType = header.type;
+        this.lengthOfItems = header.length;
+
+        if (header instanceof StreamObjectHeaderStart32bit) {
+            if (header.length == 32767) {
+                this.lengthOfItems =
+                        (int) ((StreamObjectHeaderStart32bit) header).largeLength.getDecodedValue();
+            }
+        }
+
+        AtomicInteger index = new AtomicInteger(startIndex);
+        this.streamObjectHeaderStart = header;
+        this.deserializeItemsFromByteArray(byteArray, index, this.lengthOfItems);
+
+        if (compoundTypes.contains(this.streamObjectType)) {
+            StreamObjectHeaderEnd end = null;
+            BitReader bitReader = new BitReader(byteArray, index.get());
+            int aField = bitReader.readInt32(2);
+            if (aField == 0x1) {
+                end = BasicObject.parse(byteArray, index, StreamObjectHeaderEnd8bit.class);
+            }
+            if (aField == 0x3) {
+                end = BasicObject.parse(byteArray, index, StreamObjectHeaderEnd16bit.class);
+            }
+
+            if (end.type.getIntVal() != this.streamObjectType.getIntVal()) {
+                throw new StreamObjectParseErrorException(index.get(), null,
+                        "Unexpected the stream header end value " +
+                                this.streamObjectType.getIntVal(), null);
+            }
+
+            this.streamObjectHeaderEnd = end;
+        }
+
+        return index.get() - startIndex;
+    }
+
+    /**
+     * Serialize items to byte list.
+     *
+     * @param byteList The byte list need to serialized.
+     * @return The length in bytes for additional data if the current stream object has, otherwise return 0.
+     */
+    protected abstract int serializeItemsToByteList(List<Byte> byteList)
+            throws IOException, TikaException;
+
+    /**
+     * De-serialize items from byte array.
+     *
+     * @param byteArray     The byte array which contains response message.
+     * @param currentIndex  The index special where to start.
+     * @param lengthOfItems The length of items.
+     */
+    protected abstract void deserializeItemsFromByteArray(byte[] byteArray,
+                                                          AtomicInteger currentIndex,
+                                                          int lengthOfItems)
+            throws TikaException, IOException;
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd.java
similarity index 65%
copy from tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
copy to tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd.java
index b79ef8a..11e5bb5 100644
--- a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/FileNodePtrBackPush.java
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd.java
@@ -14,17 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.tika.parser.microsoft.onenote;
 
-class FileNodePtrBackPush {
-    FileNodePtr parent;
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
 
-    public FileNodePtrBackPush(FileNodePtr parent) {
-        this.parent = parent;
-        this.parent.nodeListPositions.add(0);
-    }
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
 
-    public void dec() {
-        parent.nodeListPositions.remove(parent.nodeListPositions.size() - 1);
-    }
+public abstract class StreamObjectHeaderEnd extends BasicObject {
+    /**
+     * Gets or sets the type of the stream object.
+     * value 1 for 8-bit stream object header start,
+     * value 3 for 16-bit stream object header start.
+     */
+    StreamObjectTypeHeaderEnd type;
 }
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd16bit.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd16bit.java
new file mode 100644
index 0000000..4822fce
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd16bit.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitReader;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitWriter;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.LittleEndianBitConverter;
+
+/**
+ * An 16-bit header for a compound object would indicate the end of a stream object
+ */
+public class StreamObjectHeaderEnd16bit extends StreamObjectHeaderEnd {
+    /**
+     * Initializes a new instance of the StreamObjectHeaderEnd16bit class with the specified type value.
+     *
+     * @param type Specify the integer value of the type.
+     */
+    public StreamObjectHeaderEnd16bit(int type) throws TikaException {
+        this.type = StreamObjectTypeHeaderEnd.fromIntVal(type);
+        if (this.type == null) {
+            throw new TikaException(String.format(Locale.US,
+                    "The type value RuntimeException is not defined for the stream object end 16-bit header",
+                    type));
+        }
+
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderEnd16bit class with the specified type value.
+     *
+     * @param headerType Specify the value of the type.
+     */
+    public StreamObjectHeaderEnd16bit(StreamObjectTypeHeaderEnd headerType) {
+        this.type = headerType;
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderEnd16bit class, this is the default constructor.
+     */
+    public StreamObjectHeaderEnd16bit() {
+    }
+
+    /**
+     * This method is used to convert the element of StreamObjectHeaderEnd16bit basic object into a byte List.
+     *
+     * @return Return the byte list which store the byte information of StreamObjectHeaderEnd16bit.
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws IOException {
+        BitWriter bitFieldWriter = new BitWriter(2);
+        bitFieldWriter.appendInit32(0x3, 2);
+        bitFieldWriter.appendUInit32(this.type.getIntVal(), 14);
+        return bitFieldWriter.getByteList();
+    }
+
+    /**
+     * This method is used to get the byte value of the 16-bit stream object header End.
+     *
+     * @return Return StreamObjectHeaderEnd8bit value represented by unsigned short integer.
+     */
+    public short toUint16() throws IOException {
+        List<Byte> bytes = this.serializeToByteList();
+        return LittleEndianBitConverter.ToUInt16(ByteUtil.toByteArray(bytes), 0);
+    }
+
+    /**
+     * This method is used to deserialize the StreamObjectHeaderEnd16bit basic object from the
+     * specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the StreamObjectHeaderEnd16bit basic object.
+     */
+    @Override
+    protected int doDeserializeFromByteArray(byte[] byteArray, int startIndex)
+            throws IOException, TikaException {
+        BitReader reader = new BitReader(byteArray, startIndex);
+        int headerType = reader.readInt32(2);
+
+        if (headerType != 0x3) {
+            throw new TikaException(String.format(Locale.US,
+                    "Failed to get the StreamObjectHeaderEnd16bit header type value, expect value %d, " +
+                            "but actual value is %s", 0x3, headerType));
+        }
+
+        int typeValue = reader.readUInt32(14);
+        this.type = StreamObjectTypeHeaderEnd.fromIntVal(typeValue);
+        if (this.type == null) {
+            throw new TikaException(String.format(Locale.US,
+                    "Failed to get the StreamObjectHeaderEnd16bit type value, the value %d is not defined",
+                    typeValue));
+        }
+
+        return 2;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd8bit.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd8bit.java
new file mode 100644
index 0000000..157e0e1
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderEnd8bit.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitReader;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitWriter;
+
+/**
+ * An 8-bit header for a compound object would indicate the end of a stream object
+ */
+public class StreamObjectHeaderEnd8bit extends StreamObjectHeaderEnd {
+    /**
+     * Initializes a new instance of the StreamObjectHeaderEnd8bit class with the specified type value.
+     *
+     * @param type Specify the integer value of the type.
+     */
+    public StreamObjectHeaderEnd8bit(int type) throws TikaException {
+
+        this.type = StreamObjectTypeHeaderEnd.fromIntVal(type);
+        if (this.type == null) {
+            throw new TikaException(String.format(Locale.US,
+                    "The type value %s is not defined for the stream object end 8 bit header",
+                    type));
+        }
+
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderEnd8bit class, this is the default constructor.
+     */
+    public StreamObjectHeaderEnd8bit() {
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderEnd8bit class with the specified type value.
+     *
+     * @param type Specify the value of the type.
+     */
+    public StreamObjectHeaderEnd8bit(StreamObjectTypeHeaderEnd type) throws TikaException {
+        this(type.getIntVal());
+    }
+
+    /**
+     * This method is used to convert the element of StreamObjectHeaderEnd8bit basic object into a byte List.
+     *
+     * @return Return the byte list which store the byte information of StreamObjectHeaderEnd8bit.
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws IOException {
+        BitWriter bitFieldWriter = new BitWriter(1);
+        bitFieldWriter.appendInit32(0x1, 2);
+        bitFieldWriter.appendUInit32(this.type.getIntVal(), 6);
+        return bitFieldWriter.getByteList();
+    }
+
+    /**
+     * This method is used to get the byte value of the 8bit stream object header End.
+     *
+     * @return Return StreamObjectHeaderEnd8bit value represented by byte.
+     */
+    public byte toByte() throws IOException {
+        List<Byte> bytes = this.serializeToByteList();
+
+        if (bytes.size() != 1) {
+            throw new IOException("The unexpected StreamObjectHeaderEnd8bit length");
+        }
+
+        return bytes.get(0);
+    }
+
+    /**
+     * This method is used to deserialize the StreamObjectHeaderEnd8bit basic object from the
+     * specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the StreamObjectHeaderEnd8bit basic object.
+     */
+    @Override
+    protected int doDeserializeFromByteArray(byte[] byteArray, int startIndex)
+            throws IOException, TikaException {
+        BitReader reader = new BitReader(byteArray, startIndex);
+        int headerType = reader.readInt32(2);
+
+        if (headerType != 0x1) {
+            throw new TikaException(String.format(Locale.US,
+                    "Failed to get the StreamObjectHeaderEnd8bit header type value, " +
+                            "expect value %s, but actual value is %s", 0x1, headerType));
+        }
+
+        int typeValue = reader.readUInt32(6);
+        this.type = StreamObjectTypeHeaderEnd.fromIntVal(typeValue);
+        if (this.type == null) {
+            throw new TikaException(String.format(Locale.US,
+                    "Failed to get the StreamObjectHeaderEnd8bit type value, the value %s is not defined",
+                    typeValue));
+        }
+
+        return 1;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart.java
new file mode 100644
index 0000000..a16255c
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+
+/**
+ * This class specifies the base class for 16-bit or 32-bit stream object header start
+ */
+public abstract class StreamObjectHeaderStart extends BasicObject {
+    /**
+     * Specify for 16-bit stream object header start.
+     */
+    public static final int STREAM_OBJECT_HEADER_START_16_BIT = 0x0;
+
+    /**
+     * Specify for 32-bit stream object header start.
+     */
+    public static final int STREAM_OBJECT_HEADER_START_32_BIT = 0x02;
+    public StreamObjectTypeHeaderStart type;
+    /**
+     * Gets or sets the type of the stream object.
+     * value 0 for 16-bit stream object header start,
+     * value 2 for 32-bit stream object header start.
+     */
+    protected int headerType;
+    /**
+     * Gets or sets a value that specifies if set a compound parse type is needed and
+     * MUST be ended with either an 8-bit stream object header end or a 16-bit stream object header end.
+     * If the bit is zero, it specifies a single object. Otherwise it specifies a compound object.
+     */
+    protected int compound;
+    protected int length;
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart class.
+     */
+    protected StreamObjectHeaderStart() {
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart class with specified header type.
+     *
+     * @param streamObjectTypeHeaderStart Specify the value of the StreamObjectHeaderStart Type.
+     */
+    protected StreamObjectHeaderStart(StreamObjectTypeHeaderStart streamObjectTypeHeaderStart) {
+        this.type = streamObjectTypeHeaderStart;
+    }
+
+    /**
+     * This method is used to parse the actual 16bit or 32bit stream header.
+     *
+     * @param byteArray          Specify the Byte array.
+     * @param startIndex         Specify the start position.
+     * @param streamObjectHeader Specify the out value for the parse result.
+     * @return Return true if success, otherwise returns false.
+     */
+    public static int tryParse(byte[] byteArray, int startIndex,
+                               AtomicReference<StreamObjectHeaderStart> streamObjectHeader) {
+        int headerType = byteArray[startIndex] & 0x03;
+        if (headerType == StreamObjectHeaderStart.STREAM_OBJECT_HEADER_START_16_BIT) {
+            streamObjectHeader.set(new StreamObjectHeaderStart16bit());
+        } else {
+            if (headerType == StreamObjectHeaderStart.STREAM_OBJECT_HEADER_START_32_BIT) {
+                streamObjectHeader.set(new StreamObjectHeaderStart32bit());
+            } else {
+                return 0;
+            }
+        }
+
+        try {
+            return streamObjectHeader.get().deserializeFromByteArray(byteArray, startIndex);
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart16bit.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart16bit.java
new file mode 100644
index 0000000..4000bc2
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart16bit.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitReader;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitWriter;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.ByteUtil;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.LittleEndianBitConverter;
+
+/**
+ * An 16-bit header for a compound object would indicate the start of a stream object
+ */
+public class StreamObjectHeaderStart16bit extends StreamObjectHeaderStart {
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart16bit class with specified type and length.
+     *
+     * @param type   Specify the type of the StreamObjectHeaderStart16bit.
+     * @param length Specify the length of the StreamObjectHeaderStart16bit.
+     */
+    public StreamObjectHeaderStart16bit(StreamObjectTypeHeaderStart type, int length)
+            throws TikaException {
+        if (this.length > 127) {
+            throw new TikaException(
+                    "Field Length - 16-bit Stream Object Header Start, Length (7-bits): A 7-bit " +
+                            "unsigned integer that specifies the length in bytes for additional data " +
+                            "(if any). If the length is more than 127 bytes, a 32-bit stream object header " +
+                            "start MUST be used.");
+        }
+
+        this.headerType = 0x0;
+        this.type = type;
+        this.compound = StreamObject.getCompoundTypes().contains(this.type) ? 1 : 0;
+        this.length = length;
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart16bit class with specified type.
+     *
+     * @param type Specify the type of the StreamObjectHeaderStart16bit.
+     */
+    public StreamObjectHeaderStart16bit(StreamObjectTypeHeaderStart type) throws TikaException {
+        this(type, 0);
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart16bit class, this is the default constructor.
+     */
+    public StreamObjectHeaderStart16bit() {
+    }
+
+    /**
+     * This method is used to convert the element of StreamObjectHeaderStart16bit basic object into a byte List.
+     *
+     * @return Return the byte list which store the byte information of StreamObjectHeaderStart16bit.
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws IOException {
+        BitWriter bitField = new BitWriter(2);
+        bitField.appendInit32(this.headerType, 2);
+        bitField.appendInit32(this.compound, 1);
+        bitField.appendUInit32(this.type.getIntVal(), 6);
+        bitField.appendInit32(this.length, 7);
+        List<Byte> result = new ArrayList<>();
+        ByteUtil.appendByteArrayToListOfByte(result, bitField.getBytes());
+        return result;
+    }
+
+    /**
+     * This method is used to get the Uint16 value of the 16bit stream object header.
+     *
+     * @return Return the ushort value.
+     */
+    public short ToUint16() throws IOException {
+        return LittleEndianBitConverter.ToUInt16(ByteUtil.toByteArray(this.serializeToByteList()),
+                0);
+    }
+
+    /**
+     * This method is used to deserialize the StreamObjectHeaderStart16bit basic object from the
+     * specified byte array and start index.
+     *
+     * @param byteArray  Specify the byte array.
+     * @param startIndex Specify the start index from the byte array.
+     * @return Return the length in byte of the StreamObjectHeaderStart16bit basic object.
+     */
+    @Override
+    protected int doDeserializeFromByteArray(byte[] byteArray, int startIndex)
+            throws IOException, TikaException {
+        BitReader bitReader = new BitReader(byteArray, startIndex);
+        this.headerType = bitReader.readInt32(2);
+        if (this.headerType != StreamObjectHeaderStart.STREAM_OBJECT_HEADER_START_16_BIT) {
+            throw new TikaException(String.format(Locale.US,
+                    "Failed to get the StreamObjectHeaderStart16bit header type value, expect value %s, " +
+                            "but actual value is %s", STREAM_OBJECT_HEADER_START_16_BIT,
+                    this.headerType));
+        }
+
+        this.compound = bitReader.readInt32(1);
+        int typeValue = bitReader.readInt32(6);
+        this.type = StreamObjectTypeHeaderStart.fromIntVal(typeValue);
+        if (type == null) {
+            throw new TikaException(String.format(Locale.US,
+                    "Failed to get the StreamObjectHeaderStart16bit type value, the value %s is not defined",
+                    typeValue));
+        }
+
+        if (StreamObject.getCompoundTypes().contains(type) && this.compound != 1) {
+            throw new TikaException(String.format(Locale.US,
+                    "Failed to parse the StreamObjectHeaderStart16bit header. If the type value is %s then " +
+                            "the compound value should 1, but actual value is 0", typeValue));
+        }
+
+        this.length = bitReader.readInt32(7);
+        if (this.length > 127) {
+            throw new TikaException(
+                    "16-bit Stream Object Header Start, Length (7-bits): A 7-bit unsigned integer that " +
+                            "specifies the length in bytes for additional data (if any). If the length is more than " +
+                            "127 bytes, a 32-bit stream object header start MUST be used.");
+        }
+
+        return 2;
+    }
+}
diff --git a/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart32bit.java b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart32bit.java
new file mode 100644
index 0000000..07a2bce
--- /dev/null
+++ b/tika-parsers/src/main/java/org/apache/tika/parser/microsoft/onenote/fsshttpb/streamobj/StreamObjectHeaderStart32bit.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.BasicObject;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.streamobj.basic.Compact64bitInt;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitReader;
+import org.apache.tika.parser.microsoft.onenote.fsshttpb.util.BitWriter;
+
+/**
+ * An 32-bit header for a compound object would indicate the start of a stream object
+ */
+public class StreamObjectHeaderStart32bit extends StreamObjectHeaderStart {
+    /**
+     * Gets or sets an optional compact uint64 that specifies the length in bytes for additional data (if any).
+     * This field MUST be specified if the Length field contains 32767 and MUST NOT be specified if the Length field
+     * contains any other value than 32767.
+     */
+    public Compact64bitInt largeLength;
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart32bit class with specified type and length.
+     *
+     * @param type   Specify the type of the StreamObjectHeaderStart32bit.
+     * @param length Specify the length of the StreamObjectHeaderStart32bit.
+     */
+    public StreamObjectHeaderStart32bit(StreamObjectTypeHeaderStart type, int length) {
+        this.headerType = StreamObjectHeaderStart.STREAM_OBJECT_HEADER_START_32_BIT;
+        this.type = type;
+        this.compound = StreamObject.getCompoundTypes().contains(this.type) ? 1 : 0;
+
+        if (length >= 32767) {
+            this.length = 32767;
+            this.largeLength = new Compact64bitInt(length);
+        } else {
+            this.length = length;
+            this.largeLength = null;
+        }
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart32bit class, this is the default constructor.
+     */
+    public StreamObjectHeaderStart32bit() {
+    }
+
+    /**
+     * Initializes a new instance of the StreamObjectHeaderStart32bit class with specified type.
+     *
+     * @param streamObjectTypeHeaderStart Specify the type of the StreamObjectHeaderStart32bit.
+     */
+    public StreamObjectHeaderStart32bit(StreamObjectTypeHeaderStart streamObjectTypeHeaderStart) {
+        this.type = streamObjectTypeHeaderStart;
+    }
+
+    /**
+     * This method is used to convert the element of StreamObjectHeaderStart32bit basic object into a byte List.
+     *
+     * @return Return the byte list which store the byte information of StreamObjectHeaderStart32bit.
+     */
+    @Override
+    public List<Byte> serializeToByteList() throws IOException {
... 7296 lines suppressed ...