2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org
.spearce
.jgit
.dircache
;
40 import java
.io
.IOException
;
41 import java
.util
.ArrayList
;
42 import java
.util
.Collections
;
43 import java
.util
.Comparator
;
44 import java
.util
.List
;
46 import org
.spearce
.jgit
.lib
.Constants
;
49 * Updates a {@link DirCache} by supplying discrete edit commands.
51 * An editor updates a DirCache by taking a list of {@link PathEdit} commands
52 * and executing them against the entries of the destination cache to produce a
53 * new cache. This edit style allows applications to insert a few commands and
54 * then have the editor compute the proper entry indexes necessary to perform an
55 * efficient in-order update of the index records. This can be easier to use
56 * than {@link DirCacheBuilder}.
59 * @see DirCacheBuilder
61 public class DirCacheEditor
extends BaseDirCacheEditor
{
62 private static final Comparator
<PathEdit
> EDIT_CMP
= new Comparator
<PathEdit
>() {
63 public int compare(final PathEdit o1
, final PathEdit o2
) {
64 final byte[] a
= o1
.path
;
65 final byte[] b
= o2
.path
;
66 return DirCache
.cmp(a
, a
.length
, b
, b
.length
);
70 private final List
<PathEdit
> edits
;
73 * Construct a new editor.
76 * the cache this editor will eventually update.
78 * estimated number of entries the editor will have upon
79 * completion. This sizes the initial entry table.
81 protected DirCacheEditor(final DirCache dc
, final int ecnt
) {
83 edits
= new ArrayList
<PathEdit
>();
87 * Append one edit command to the list of commands to be applied.
89 * Edit commands may be added in any order chosen by the application. They
90 * are automatically rearranged by the builder to provide the most efficient
94 * another edit command.
96 public void add(final PathEdit edit
) {
101 public boolean commit() throws IOException
{
102 if (edits
.isEmpty()) {
103 // No changes? Don't rewrite the index.
108 return super.commit();
111 public void finish() {
112 if (!edits
.isEmpty()) {
118 private void applyEdits() {
119 Collections
.sort(edits
, EDIT_CMP
);
121 final int maxIdx
= cache
.getEntryCount();
123 for (final PathEdit e
: edits
) {
124 int eIdx
= cache
.findEntry(e
.path
, e
.path
.length
);
125 final boolean missing
= eIdx
< 0;
128 final int cnt
= Math
.min(eIdx
, maxIdx
) - lastIdx
;
130 fastKeep(lastIdx
, cnt
);
131 lastIdx
= missing ? eIdx
: cache
.nextEntry(eIdx
);
133 if (e
instanceof DeletePath
)
135 if (e
instanceof DeleteTree
) {
136 lastIdx
= cache
.nextEntry(e
.path
, e
.path
.length
, eIdx
);
140 final DirCacheEntry ent
;
142 ent
= new DirCacheEntry(e
.path
);
144 ent
= cache
.getEntry(eIdx
);
149 final int cnt
= maxIdx
- lastIdx
;
151 fastKeep(lastIdx
, cnt
);
155 * Any index record update.
157 * Applications should subclass and provide their own implementation for the
158 * {@link #apply(DirCacheEntry)} method. The editor will invoke apply once
159 * for each record in the index which matches the path name. If there are
160 * multiple records (for example in stages 1, 2 and 3), the edit instance
161 * will be called multiple times, once for each stage.
163 public abstract static class PathEdit
{
167 * Create a new update command by path name.
170 * path of the file within the repository.
172 public PathEdit(final String entryPath
) {
173 path
= Constants
.encode(entryPath
);
177 * Create a new update command for an existing entry instance.
180 * entry instance to match path of. Only the path of this
181 * entry is actually considered during command evaluation.
183 public PathEdit(final DirCacheEntry ent
) {
188 * Apply the update to a single cache entry matching the path.
190 * After apply is invoked the entry is added to the output table, and
191 * will be included in the new index.
194 * the entry being processed. All fields are zeroed out if
195 * the path is a new path in the index.
197 public abstract void apply(DirCacheEntry ent
);
201 * Deletes a single file entry from the index.
203 * This deletion command removes only a single file at the given location,
204 * but removes multiple stages (if present) for that path. To remove a
205 * complete subtree use {@link DeleteTree} instead.
209 public static final class DeletePath
extends PathEdit
{
211 * Create a new deletion command by path name.
214 * path of the file within the repository.
216 public DeletePath(final String entryPath
) {
221 * Create a new deletion command for an existing entry instance.
224 * entry instance to remove. Only the path of this entry is
225 * actually considered during command evaluation.
227 public DeletePath(final DirCacheEntry ent
) {
231 public void apply(final DirCacheEntry ent
) {
232 throw new UnsupportedOperationException("No apply in delete");
237 * Recursively deletes all paths under a subtree.
239 * This deletion command is more generic than {@link DeletePath} as it can
240 * remove all records which appear recursively under the same subtree.
241 * Multiple stages are removed (if present) for any deleted entry.
243 * This command will not remove a single file entry. To remove a single file
244 * use {@link DeletePath}.
248 public static final class DeleteTree
extends PathEdit
{
250 * Create a new tree deletion command by path name.
253 * path of the subtree within the repository. If the path
254 * does not end with "/" a "/" is implicitly added to ensure
255 * only the subtree's contents are matched by the command.
257 public DeleteTree(final String entryPath
) {
258 super(entryPath
.endsWith("/") ? entryPath
: entryPath
+ "/");
261 public void apply(final DirCacheEntry ent
) {
262 throw new UnsupportedOperationException("No apply in delete");