Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 44c59260e84df23b73445c5b46d9806e43046d5b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
/*
 * Copyright (C) 2010, Google Inc.
 * and other copyright owners as documented in the project's IP log.
 *
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Distribution License v1.0 which
 * accompanies this distribution, is reproduced below, and is
 * available at http://www.eclipse.org/org/documents/edl-v10.php
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 * - Neither the name of the Eclipse Foundation, Inc. nor the
 *   names of its contributors may be used to endorse or promote
 *   products derived from this software without specific prior
 *   written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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 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 Constants#R_NOTES}
	 * prefix if it exists.
	 *
	 * @param noteRefName
	 * @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 IOException
	 *             the repository cannot be accessed through the reader.
	 * @throws CorruptObjectException
	 *             a tree object is corrupt and cannot be read.
	 * @throws IncorrectObjectTypeException
	 *             a tree object wasn't actually a tree.
	 * @throws 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 IOException
	 *             the repository cannot be accessed through the reader.
	 * @throws CorruptObjectException
	 *             a tree object is corrupt and cannot be read.
	 * @throws IncorrectObjectTypeException
	 *             a tree object wasn't actually a tree.
	 * @throws 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 IOException
	 *             the repository cannot be accessed through the reader.
	 * @throws CorruptObjectException
	 *             a tree object is corrupt and cannot be read.
	 * @throws IncorrectObjectTypeException
	 *             a tree object wasn't actually a tree.
	 * @throws 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;
	}

	/**
	 * @return an iterator that iterates over notes of this NoteMap. Non note
	 *         entries are ignored by this iterator.
	 */
	@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 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 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 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 LargeObjectException
	 *             the note data is larger than {@code sizeLimit}.
	 * @throws MissingObjectException
	 *             the note's blob does not exist in the repository.
	 * @throws 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);
		else
			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 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 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 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 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);
	}
}

Back to the top