| /* |
| * 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 java.io.IOException; |
| import java.util.Iterator; |
| |
| import org.eclipse.jgit.errors.CorruptObjectException; |
| import org.eclipse.jgit.errors.IncorrectObjectTypeException; |
| import org.eclipse.jgit.errors.LargeObjectException; |
| import org.eclipse.jgit.errors.MissingObjectException; |
| import org.eclipse.jgit.lib.AbbreviatedObjectId; |
| import org.eclipse.jgit.lib.AnyObjectId; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.MutableObjectId; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.ObjectInserter; |
| import org.eclipse.jgit.lib.ObjectReader; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevTree; |
| |
| /** |
| * Index of notes from a note branch. |
| * |
| * This class is not thread-safe, and relies on an |
| * {@link org.eclipse.jgit.lib.ObjectReader} that it borrows/shares with the |
| * caller. The reader can be used during any call, and is not released by this |
| * class. The caller should arrange for releasing the shared |
| * {@code ObjectReader} at the proper times. |
| */ |
| public class NoteMap implements Iterable<Note> { |
| /** |
| * Construct a new empty note map. |
| * |
| * @return an empty note map. |
| */ |
| public static NoteMap newEmptyMap() { |
| NoteMap r = new NoteMap(null /* no reader */); |
| r.root = new LeafBucket(0); |
| return r; |
| } |
| |
| /** |
| * Shorten the note ref name by trimming off the |
| * {@link org.eclipse.jgit.lib.Constants#R_NOTES} prefix if it exists. |
| * |
| * @param noteRefName |
| * a {@link java.lang.String} object. |
| * @return a more user friendly note name |
| */ |
| public static String shortenRefName(String noteRefName) { |
| if (noteRefName.startsWith(Constants.R_NOTES)) |
| return noteRefName.substring(Constants.R_NOTES.length()); |
| return noteRefName; |
| } |
| |
| /** |
| * Load a collection of notes from a branch. |
| * |
| * @param reader |
| * reader to scan the note branch with. This reader may be |
| * retained by the NoteMap for the life of the map in order to |
| * support lazy loading of entries. |
| * @param commit |
| * the revision of the note branch to read. |
| * @return the note map read from the commit. |
| * @throws java.io.IOException |
| * the repository cannot be accessed through the reader. |
| * @throws org.eclipse.jgit.errors.CorruptObjectException |
| * a tree object is corrupt and cannot be read. |
| * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException |
| * a tree object wasn't actually a tree. |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * a reference tree object doesn't exist. |
| */ |
| public static NoteMap read(ObjectReader reader, RevCommit commit) |
| throws MissingObjectException, IncorrectObjectTypeException, |
| CorruptObjectException, IOException { |
| return read(reader, commit.getTree()); |
| } |
| |
| /** |
| * Load a collection of notes from a tree. |
| * |
| * @param reader |
| * reader to scan the note branch with. This reader may be |
| * retained by the NoteMap for the life of the map in order to |
| * support lazy loading of entries. |
| * @param tree |
| * the note tree to read. |
| * @return the note map read from the tree. |
| * @throws java.io.IOException |
| * the repository cannot be accessed through the reader. |
| * @throws org.eclipse.jgit.errors.CorruptObjectException |
| * a tree object is corrupt and cannot be read. |
| * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException |
| * a tree object wasn't actually a tree. |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * a reference tree object doesn't exist. |
| */ |
| public static NoteMap read(ObjectReader reader, RevTree tree) |
| throws MissingObjectException, IncorrectObjectTypeException, |
| CorruptObjectException, IOException { |
| return readTree(reader, tree); |
| } |
| |
| /** |
| * Load a collection of notes from a tree. |
| * |
| * @param reader |
| * reader to scan the note branch with. This reader may be |
| * retained by the NoteMap for the life of the map in order to |
| * support lazy loading of entries. |
| * @param treeId |
| * the note tree to read. |
| * @return the note map read from the tree. |
| * @throws java.io.IOException |
| * the repository cannot be accessed through the reader. |
| * @throws org.eclipse.jgit.errors.CorruptObjectException |
| * a tree object is corrupt and cannot be read. |
| * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException |
| * a tree object wasn't actually a tree. |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * a reference tree object doesn't exist. |
| */ |
| public static NoteMap readTree(ObjectReader reader, ObjectId treeId) |
| throws MissingObjectException, IncorrectObjectTypeException, |
| CorruptObjectException, IOException { |
| NoteMap map = new NoteMap(reader); |
| map.load(treeId); |
| return map; |
| } |
| |
| /** |
| * Construct a new note map from an existing note bucket. |
| * |
| * @param root |
| * the root bucket of this note map |
| * @param reader |
| * reader to scan the note branch with. This reader may be |
| * retained by the NoteMap for the life of the map in order to |
| * support lazy loading of entries. |
| * @return the note map built from the note bucket |
| */ |
| static NoteMap newMap(InMemoryNoteBucket root, ObjectReader reader) { |
| NoteMap map = new NoteMap(reader); |
| map.root = root; |
| return map; |
| } |
| |
| /** Borrowed reader to access the repository. */ |
| private final ObjectReader reader; |
| |
| /** All of the notes that have been loaded. */ |
| private InMemoryNoteBucket root; |
| |
| private NoteMap(ObjectReader reader) { |
| this.reader = reader; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public Iterator<Note> iterator() { |
| try { |
| return root.iterator(new MutableObjectId(), reader); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * Lookup a note for a specific ObjectId. |
| * |
| * @param id |
| * the object to look for. |
| * @return the note's blob ObjectId, or null if no note exists. |
| * @throws java.io.IOException |
| * a portion of the note space is not accessible. |
| */ |
| public ObjectId get(AnyObjectId id) throws IOException { |
| Note n = root.getNote(id, reader); |
| return n == null ? null : n.getData(); |
| } |
| |
| /** |
| * Lookup a note for a specific ObjectId. |
| * |
| * @param id |
| * the object to look for. |
| * @return the note for the given object id, or null if no note exists. |
| * @throws java.io.IOException |
| * a portion of the note space is not accessible. |
| */ |
| public Note getNote(AnyObjectId id) throws IOException { |
| return root.getNote(id, reader); |
| } |
| |
| /** |
| * Determine if a note exists for the specified ObjectId. |
| * |
| * @param id |
| * the object to look for. |
| * @return true if a note exists; false if there is no note. |
| * @throws java.io.IOException |
| * a portion of the note space is not accessible. |
| */ |
| public boolean contains(AnyObjectId id) throws IOException { |
| return get(id) != null; |
| } |
| |
| /** |
| * Open and return the content of an object's note. |
| * |
| * This method assumes the note is fairly small and can be accessed |
| * efficiently. Larger notes should be accessed by streaming: |
| * |
| * <pre> |
| * ObjectId dataId = thisMap.get(id); |
| * if (dataId != null) |
| * reader.open(dataId).openStream(); |
| * </pre> |
| * |
| * @param id |
| * object to lookup the note of. |
| * @param sizeLimit |
| * maximum number of bytes to return. If the note data size is |
| * larger than this limit, LargeObjectException will be thrown. |
| * @return if a note is defined for {@code id}, the note content. If no note |
| * is defined, null. |
| * @throws org.eclipse.jgit.errors.LargeObjectException |
| * the note data is larger than {@code sizeLimit}. |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * the note's blob does not exist in the repository. |
| * @throws java.io.IOException |
| * the note's blob cannot be read from the repository |
| */ |
| public byte[] getCachedBytes(AnyObjectId id, int sizeLimit) |
| throws LargeObjectException, MissingObjectException, IOException { |
| ObjectId dataId = get(id); |
| if (dataId != null) { |
| return reader.open(dataId).getCachedBytes(sizeLimit); |
| } |
| return null; |
| } |
| |
| /** |
| * Attach (or remove) a note on an object. |
| * |
| * If no note exists, a new note is stored. If a note already exists for the |
| * given object, it is replaced (or removed). |
| * |
| * This method only updates the map in memory. |
| * |
| * If the caller wants to attach a UTF-8 encoded string message to an |
| * object, {@link #set(AnyObjectId, String, ObjectInserter)} is a convenient |
| * way to encode and update a note in one step. |
| * |
| * @param noteOn |
| * the object to attach the note to. This same ObjectId can later |
| * be used as an argument to {@link #get(AnyObjectId)} or |
| * {@link #getCachedBytes(AnyObjectId, int)} to read back the |
| * {@code noteData}. |
| * @param noteData |
| * data to associate with the note. This must be the ObjectId of |
| * a blob that already exists in the repository. If null the note |
| * will be deleted, if present. |
| * @throws java.io.IOException |
| * a portion of the note space is not accessible. |
| */ |
| public void set(AnyObjectId noteOn, ObjectId noteData) throws IOException { |
| InMemoryNoteBucket newRoot = root.set(noteOn, noteData, reader); |
| if (newRoot == null) { |
| newRoot = new LeafBucket(0); |
| newRoot.nonNotes = root.nonNotes; |
| } |
| root = newRoot; |
| } |
| |
| /** |
| * Attach a note to an object. |
| * |
| * If no note exists, a new note is stored. If a note already exists for the |
| * given object, it is replaced (or removed). |
| * |
| * @param noteOn |
| * the object to attach the note to. This same ObjectId can later |
| * be used as an argument to {@link #get(AnyObjectId)} or |
| * {@link #getCachedBytes(AnyObjectId, int)} to read back the |
| * {@code noteData}. |
| * @param noteData |
| * text to store in the note. The text will be UTF-8 encoded when |
| * stored in the repository. If null the note will be deleted, if |
| * the empty string a note with the empty string will be stored. |
| * @param ins |
| * inserter to write the encoded {@code noteData} out as a blob. |
| * The caller must ensure the inserter is flushed before the |
| * updated note map is made available for reading. |
| * @throws java.io.IOException |
| * the note data could not be stored in the repository. |
| */ |
| public void set(AnyObjectId noteOn, String noteData, ObjectInserter ins) |
| throws IOException { |
| ObjectId dataId; |
| if (noteData != null) { |
| byte[] dataUTF8 = Constants.encode(noteData); |
| dataId = ins.insert(Constants.OBJ_BLOB, dataUTF8); |
| } else { |
| dataId = null; |
| } |
| set(noteOn, dataId); |
| } |
| |
| /** |
| * Remove a note from an object. |
| * |
| * If no note exists, no action is performed. |
| * |
| * This method only updates the map in memory. |
| * |
| * @param noteOn |
| * the object to remove the note from. |
| * @throws java.io.IOException |
| * a portion of the note space is not accessible. |
| */ |
| public void remove(AnyObjectId noteOn) throws IOException { |
| set(noteOn, null); |
| } |
| |
| /** |
| * Write this note map as a tree. |
| * |
| * @param inserter |
| * inserter to use when writing trees to the object database. |
| * Caller is responsible for flushing the inserter before trying |
| * to read the objects, or exposing them through a reference. |
| * @return the top level tree. |
| * @throws java.io.IOException |
| * a tree could not be written. |
| */ |
| public ObjectId writeTree(ObjectInserter inserter) throws IOException { |
| return root.writeTree(inserter); |
| } |
| |
| /** @return the root note bucket */ |
| InMemoryNoteBucket getRoot() { |
| return root; |
| } |
| |
| private void load(ObjectId rootTree) throws MissingObjectException, |
| IncorrectObjectTypeException, CorruptObjectException, IOException { |
| AbbreviatedObjectId none = AbbreviatedObjectId.fromString(""); //$NON-NLS-1$ |
| root = NoteParser.parse(none, rootTree, reader); |
| } |
| } |