Expose beginning of iterator indication from AbstractTreeIterator
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / treewalk / CanonicalTreeParser.java
blobdcc53cd9da64cdeb8dcf07d2fc9229c7f751bdac
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
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
21 * written permission.
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.treewalk;
40 import java.io.IOException;
42 import org.spearce.jgit.errors.IncorrectObjectTypeException;
43 import org.spearce.jgit.errors.MissingObjectException;
44 import org.spearce.jgit.lib.Constants;
45 import org.spearce.jgit.lib.FileMode;
46 import org.spearce.jgit.lib.ObjectId;
47 import org.spearce.jgit.lib.ObjectLoader;
48 import org.spearce.jgit.lib.Repository;
50 /** Parses raw Git trees from the canonical semi-text/semi-binary format. */
51 public class CanonicalTreeParser extends AbstractTreeIterator {
52 private byte[] raw;
54 /** First offset within {@link #raw} of the current entry's data. */
55 private int currPtr;
57 /** Offset one past the current entry (first byte of next entry. */
58 private int nextPtr;
60 /** Create a new parser. */
61 public CanonicalTreeParser() {
62 // Nothing necessary.
65 private CanonicalTreeParser(final CanonicalTreeParser p) {
66 super(p);
69 /**
70 * Reset this parser to walk through the given tree data.
72 * @param treeData
73 * the raw tree content.
75 public void reset(final byte[] treeData) {
76 raw = treeData;
77 currPtr = 0;
78 if (!eof())
79 parseEntry();
82 /**
83 * Reset this parser to walk through the given tree.
85 * @param repo
86 * repository to load the tree data from.
87 * @param id
88 * identity of the tree being parsed; used only in exception
89 * messages if data corruption is found.
90 * @throws MissingObjectException
91 * the object supplied is not available from the repository.
92 * @throws IncorrectObjectTypeException
93 * the object supplied as an argument is not actually a tree and
94 * cannot be parsed as though it were a tree.
95 * @throws IOException
96 * a loose object or pack file could not be read.
98 public void reset(final Repository repo, final ObjectId id)
99 throws IncorrectObjectTypeException, IOException {
100 final ObjectLoader ldr = repo.openObject(id);
101 if (ldr == null)
102 throw new MissingObjectException(id, Constants.TYPE_TREE);
103 final byte[] subtreeData = ldr.getCachedBytes();
104 if (ldr.getType() != Constants.OBJ_TREE)
105 throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE);
106 reset(subtreeData);
109 public CanonicalTreeParser createSubtreeIterator(final Repository repo)
110 throws IncorrectObjectTypeException, IOException {
111 final ObjectId id = getEntryObjectId();
112 if (!FileMode.TREE.equals(mode))
113 throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE);
114 final CanonicalTreeParser p = new CanonicalTreeParser(this);
115 p.reset(repo, id);
116 return p;
119 @Override
120 public byte[] idBuffer() {
121 return raw;
124 @Override
125 public int idOffset() {
126 return nextPtr - Constants.OBJECT_ID_LENGTH;
129 @Override
130 public boolean first() {
131 return currPtr == 0;
134 public boolean eof() {
135 return currPtr == raw.length;
138 @Override
139 public void next(int delta) {
140 if (delta == 1) {
141 // Moving forward one is the most common case.
143 currPtr = nextPtr;
144 if (!eof())
145 parseEntry();
146 return;
149 // Fast skip over records, then parse the last one.
151 final int end = raw.length;
152 int ptr = nextPtr;
153 while (--delta > 0 && ptr != end) {
154 while (raw[ptr] != 0)
155 ptr++;
156 ptr += Constants.OBJECT_ID_LENGTH + 1;
158 if (delta != 0)
159 throw new ArrayIndexOutOfBoundsException(delta);
160 currPtr = ptr;
161 if (!eof())
162 parseEntry();
165 @Override
166 public void back(int delta) {
167 int ptr = currPtr;
168 while (--delta >= 0) {
169 if (ptr == 0)
170 throw new ArrayIndexOutOfBoundsException(delta);
172 // Rewind back beyond the id and the null byte. Find the
173 // last space, this _might_ be the split between the mode
174 // and the path. Most paths in most trees do not contain a
175 // space so this prunes our search more quickly.
177 ptr -= Constants.OBJECT_ID_LENGTH;
178 while (raw[--ptr] != ' ')
179 /* nothing */;
180 if (--ptr < Constants.OBJECT_ID_LENGTH) {
181 if (delta != 0)
182 throw new ArrayIndexOutOfBoundsException(delta);
183 ptr = 0;
184 break;
187 // Locate a position that matches "\0.{20}[0-7]" such that
188 // the ptr will rest on the [0-7]. This must be the first
189 // byte of the mode. This search works because the path in
190 // the prior record must have a non-zero length and must not
191 // contain a null byte.
193 for (int n;; ptr = n) {
194 n = ptr - 1;
195 final byte b = raw[n];
196 if ('0' <= b && b <= '7')
197 continue;
198 if (raw[n - Constants.OBJECT_ID_LENGTH] != 0)
199 continue;
200 break;
203 currPtr = ptr;
204 parseEntry();
207 private void parseEntry() {
208 int ptr = currPtr;
209 byte c = raw[ptr++];
210 int tmp = c - '0';
211 for (;;) {
212 c = raw[ptr++];
213 if (' ' == c)
214 break;
215 tmp <<= 3;
216 tmp += c - '0';
218 mode = tmp;
220 tmp = pathOffset;
221 for (;; tmp++) {
222 c = raw[ptr++];
223 if (c == 0)
224 break;
225 try {
226 path[tmp] = c;
227 } catch (ArrayIndexOutOfBoundsException e) {
228 growPath(tmp);
229 path[tmp] = c;
232 pathLen = tmp;
233 nextPtr = ptr + Constants.OBJECT_ID_LENGTH;