NoteParser.java
- /*
- * Copyright (C) 2010, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- package org.eclipse.jgit.notes;
- import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
- import static org.eclipse.jgit.lib.Constants.encodeASCII;
- import static org.eclipse.jgit.lib.FileMode.TREE;
- import static org.eclipse.jgit.util.RawParseUtils.parseHexInt4;
- import java.io.IOException;
- import org.eclipse.jgit.errors.IncorrectObjectTypeException;
- import org.eclipse.jgit.lib.AbbreviatedObjectId;
- import org.eclipse.jgit.lib.FileMode;
- import org.eclipse.jgit.lib.MutableObjectId;
- import org.eclipse.jgit.lib.ObjectId;
- import org.eclipse.jgit.lib.ObjectReader;
- import org.eclipse.jgit.treewalk.CanonicalTreeParser;
- /** Custom tree parser to select note bucket type and load it. */
- final class NoteParser extends CanonicalTreeParser {
- /**
- * Parse a tree object into a {@link NoteBucket} instance.
- *
- * The type of note tree is automatically detected by examining the items
- * within the tree, and allocating the proper storage type based on the
- * first note-like entry encountered. Since the method parses by guessing
- * the type on the first element, malformed note trees can be read as the
- * wrong type of tree.
- *
- * This method is not recursive, it parses the one tree given to it and
- * returns the bucket. If there are subtrees for note storage, they are
- * setup as lazy pointers that will be resolved at a later time.
- *
- * @param prefix
- * common hex digits that all notes within this tree share. The
- * root tree has {@code prefix.length() == 0}, the first-level
- * subtrees should be {@code prefix.length()==2}, etc.
- * @param treeId
- * the tree to read from the repository.
- * @param reader
- * reader to access the tree object.
- * @return bucket to holding the notes of the specified tree.
- * @throws IOException
- * {@code treeId} cannot be accessed.
- */
- static InMemoryNoteBucket parse(AbbreviatedObjectId prefix,
- final ObjectId treeId, final ObjectReader reader)
- throws IOException {
- return new NoteParser(prefix, reader, treeId).parse();
- }
- private final int prefixLen;
- private final int pathPadding;
- private NonNoteEntry firstNonNote;
- private NonNoteEntry lastNonNote;
- private NoteParser(AbbreviatedObjectId prefix, ObjectReader r, ObjectId t)
- throws IncorrectObjectTypeException, IOException {
- super(encodeASCII(prefix.name()), r, t);
- prefixLen = prefix.length();
- // Our path buffer has a '/' that we don't want after the prefix.
- // Drop it by shifting the path down one position.
- pathPadding = 0 < prefixLen ? 1 : 0;
- if (0 < pathPadding)
- System.arraycopy(path, 0, path, pathPadding, prefixLen);
- }
- private InMemoryNoteBucket parse() {
- InMemoryNoteBucket r = parseTree();
- r.nonNotes = firstNonNote;
- return r;
- }
- private InMemoryNoteBucket parseTree() {
- for (; !eof(); next(1)) {
- if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH && isHex())
- return parseLeafTree();
- else if (getNameLength() == 2 && isHex() && isTree())
- return parseFanoutTree();
- else
- storeNonNote();
- }
- // If we cannot determine the style used, assume its a leaf.
- return new LeafBucket(prefixLen);
- }
- private LeafBucket parseLeafTree() {
- final LeafBucket leaf = new LeafBucket(prefixLen);
- final MutableObjectId idBuf = new MutableObjectId();
- for (; !eof(); next(1)) {
- if (parseObjectId(idBuf))
- leaf.parseOneEntry(idBuf, getEntryObjectId());
- else
- storeNonNote();
- }
- return leaf;
- }
- private boolean parseObjectId(MutableObjectId id) {
- if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH) {
- try {
- id.fromString(path, pathPadding);
- return true;
- } catch (ArrayIndexOutOfBoundsException notHex) {
- return false;
- }
- }
- return false;
- }
- private FanoutBucket parseFanoutTree() {
- final FanoutBucket fanout = new FanoutBucket(prefixLen);
- for (; !eof(); next(1)) {
- final int cell = parseFanoutCell();
- if (0 <= cell)
- fanout.setBucket(cell, getEntryObjectId());
- else
- storeNonNote();
- }
- return fanout;
- }
- private int parseFanoutCell() {
- if (getNameLength() == 2 && isTree()) {
- try {
- return (parseHexInt4(path[pathOffset + 0]) << 4)
- | parseHexInt4(path[pathOffset + 1]);
- } catch (ArrayIndexOutOfBoundsException notHex) {
- return -1;
- }
- }
- return -1;
- }
- private void storeNonNote() {
- ObjectId id = getEntryObjectId();
- FileMode fileMode = getEntryFileMode();
- byte[] name = new byte[getNameLength()];
- getName(name, 0);
- NonNoteEntry ent = new NonNoteEntry(name, fileMode, id);
- if (firstNonNote == null)
- firstNonNote = ent;
- if (lastNonNote != null)
- lastNonNote.next = ent;
- lastNonNote = ent;
- }
- private boolean isTree() {
- return TREE.equals(mode);
- }
- private boolean isHex() {
- try {
- for (int i = pathOffset; i < pathLen; i++)
- parseHexInt4(path[i]);
- return true;
- } catch (ArrayIndexOutOfBoundsException fail) {
- return false;
- }
- }
- }