Make all AbstractTreeIterator implementations bi-directional
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / treewalk / CanonicalTreeParser.java
blob111d03b05ae3d16d59b9e89353e245f731d23ade
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 public boolean eof() {
130 return currPtr == raw.length;
133 @Override
134 public void next(int delta) {
135 if (delta == 1) {
136 // Moving forward one is the most common case.
138 currPtr = nextPtr;
139 if (!eof())
140 parseEntry();
141 return;
144 // Fast skip over records, then parse the last one.
146 final int end = raw.length;
147 int ptr = nextPtr;
148 while (--delta > 0 && ptr != end) {
149 while (raw[ptr] != 0)
150 ptr++;
151 ptr += Constants.OBJECT_ID_LENGTH + 1;
153 if (delta != 0)
154 throw new ArrayIndexOutOfBoundsException(delta);
155 currPtr = ptr;
156 if (!eof())
157 parseEntry();
160 @Override
161 public void back(int delta) {
162 int ptr = currPtr;
163 while (--delta >= 0) {
164 if (ptr == 0)
165 throw new ArrayIndexOutOfBoundsException(delta);
167 // Rewind back beyond the id and the null byte. Find the
168 // last space, this _might_ be the split between the mode
169 // and the path. Most paths in most trees do not contain a
170 // space so this prunes our search more quickly.
172 ptr -= Constants.OBJECT_ID_LENGTH;
173 while (raw[--ptr] != ' ')
174 /* nothing */;
175 if (--ptr < Constants.OBJECT_ID_LENGTH) {
176 if (delta != 0)
177 throw new ArrayIndexOutOfBoundsException(delta);
178 ptr = 0;
179 break;
182 // Locate a position that matches "\0.{20}[0-7]" such that
183 // the ptr will rest on the [0-7]. This must be the first
184 // byte of the mode. This search works because the path in
185 // the prior record must have a non-zero length and must not
186 // contain a null byte.
188 for (int n;; ptr = n) {
189 n = ptr - 1;
190 final byte b = raw[n];
191 if ('0' <= b && b <= '7')
192 continue;
193 if (raw[n - Constants.OBJECT_ID_LENGTH] != 0)
194 continue;
195 break;
198 currPtr = ptr;
199 parseEntry();
202 private void parseEntry() {
203 int ptr = currPtr;
204 byte c = raw[ptr++];
205 int tmp = c - '0';
206 for (;;) {
207 c = raw[ptr++];
208 if (' ' == c)
209 break;
210 tmp <<= 3;
211 tmp += c - '0';
213 mode = tmp;
215 tmp = pathOffset;
216 for (;; tmp++) {
217 c = raw[ptr++];
218 if (c == 0)
219 break;
220 try {
221 path[tmp] = c;
222 } catch (ArrayIndexOutOfBoundsException e) {
223 growPath(tmp);
224 path[tmp] = c;
227 pathLen = tmp;
228 nextPtr = ptr + Constants.OBJECT_ID_LENGTH;