1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.notes;
12
13 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
14 import static org.eclipse.jgit.lib.Constants.encodeASCII;
15 import static org.eclipse.jgit.lib.FileMode.TREE;
16 import static org.eclipse.jgit.util.RawParseUtils.parseHexInt4;
17
18 import java.io.IOException;
19
20 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
21 import org.eclipse.jgit.lib.AbbreviatedObjectId;
22 import org.eclipse.jgit.lib.FileMode;
23 import org.eclipse.jgit.lib.MutableObjectId;
24 import org.eclipse.jgit.lib.ObjectId;
25 import org.eclipse.jgit.lib.ObjectReader;
26 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
27
28
29 final class NoteParser extends CanonicalTreeParser {
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 static InMemoryNoteBucket parse(AbbreviatedObjectId prefix,
56 final ObjectId treeId, final ObjectReader reader)
57 throws IOException {
58 return new NoteParser(prefix, reader, treeId).parse();
59 }
60
61 private final int prefixLen;
62
63 private final int pathPadding;
64
65 private NonNoteEntry firstNonNote;
66
67 private NonNoteEntry lastNonNote;
68
69 private NoteParser(AbbreviatedObjectId prefix, ObjectReader r, ObjectId t)
70 throws IncorrectObjectTypeException, IOException {
71 super(encodeASCII(prefix.name()), r, t);
72 prefixLen = prefix.length();
73
74
75
76 pathPadding = 0 < prefixLen ? 1 : 0;
77 if (0 < pathPadding)
78 System.arraycopy(path, 0, path, pathPadding, prefixLen);
79 }
80
81 private InMemoryNoteBucket parse() {
82 InMemoryNoteBucket r = parseTree();
83 r.nonNotes = firstNonNote;
84 return r;
85 }
86
87 private InMemoryNoteBucket parseTree() {
88 for (; !eof(); next(1)) {
89 if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH && isHex())
90 return parseLeafTree();
91
92 else if (getNameLength() == 2 && isHex() && isTree())
93 return parseFanoutTree();
94
95 else
96 storeNonNote();
97 }
98
99
100 return new LeafBucket(prefixLen);
101 }
102
103 private LeafBucket parseLeafTree() {
104 final LeafBucket leaf = new LeafBucket(prefixLen);
105 final MutableObjectId idBuf = new MutableObjectId();
106
107 for (; !eof(); next(1)) {
108 if (parseObjectId(idBuf))
109 leaf.parseOneEntry(idBuf, getEntryObjectId());
110 else
111 storeNonNote();
112 }
113
114 return leaf;
115 }
116
117 private boolean parseObjectId(MutableObjectId id) {
118 if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH) {
119 try {
120 id.fromString(path, pathPadding);
121 return true;
122 } catch (ArrayIndexOutOfBoundsException notHex) {
123 return false;
124 }
125 }
126 return false;
127 }
128
129 private FanoutBucket parseFanoutTree() {
130 final FanoutBucket fanout = new FanoutBucket(prefixLen);
131
132 for (; !eof(); next(1)) {
133 final int cell = parseFanoutCell();
134 if (0 <= cell)
135 fanout.setBucket(cell, getEntryObjectId());
136 else
137 storeNonNote();
138 }
139
140 return fanout;
141 }
142
143 private int parseFanoutCell() {
144 if (getNameLength() == 2 && isTree()) {
145 try {
146 return (parseHexInt4(path[pathOffset + 0]) << 4)
147 | parseHexInt4(path[pathOffset + 1]);
148 } catch (ArrayIndexOutOfBoundsException notHex) {
149 return -1;
150 }
151 }
152 return -1;
153 }
154
155 private void storeNonNote() {
156 ObjectId id = getEntryObjectId();
157 FileMode fileMode = getEntryFileMode();
158
159 byte[] name = new byte[getNameLength()];
160 getName(name, 0);
161
162 NonNoteEntry ent = new NonNoteEntry(name, fileMode, id);
163 if (firstNonNote == null)
164 firstNonNote = ent;
165 if (lastNonNote != null)
166 lastNonNote.next = ent;
167 lastNonNote = ent;
168 }
169
170 private boolean isTree() {
171 return TREE.equals(mode);
172 }
173
174 private boolean isHex() {
175 try {
176 for (int i = pathOffset; i < pathLen; i++)
177 parseHexInt4(path[i]);
178 return true;
179 } catch (ArrayIndexOutOfBoundsException fail) {
180 return false;
181 }
182 }
183 }