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 package org.eclipse.jgit.attributes;
43
44 import java.io.IOException;
45 import java.util.HashMap;
46 import java.util.List;
47 import java.util.ListIterator;
48 import java.util.Map;
49
50 import org.eclipse.jgit.annotations.Nullable;
51 import org.eclipse.jgit.attributes.Attribute.State;
52 import org.eclipse.jgit.dircache.DirCacheIterator;
53 import org.eclipse.jgit.lib.FileMode;
54 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
55 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
56 import org.eclipse.jgit.treewalk.TreeWalk;
57 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
58 import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
59
60
61
62
63
64
65
66
67
68
69
70
71 public class AttributesHandler {
72 private static final String MACRO_PREFIX = "[attr]";
73
74 private static final String BINARY_RULE_KEY = "binary";
75
76
77
78
79
80 private static final List<Attribute> BINARY_RULE_ATTRIBUTES = new AttributesRule(
81 MACRO_PREFIX + BINARY_RULE_KEY, "-diff -merge -text")
82 .getAttributes();
83
84 private final TreeWalk treeWalk;
85
86 private final AttributesNode globalNode;
87
88 private final AttributesNode infoNode;
89
90 private final Map<String, List<Attribute>> expansions = new HashMap<>();
91
92
93
94
95
96
97
98
99 public AttributesHandler(TreeWalk treeWalk) throws IOException {
100 this.treeWalk = treeWalk;
101 AttributesNodeProvider attributesNodeProvider =treeWalk.getAttributesNodeProvider();
102 this.globalNode = attributesNodeProvider != null
103 ? attributesNodeProvider.getGlobalAttributesNode() : null;
104 this.infoNode = attributesNodeProvider != null
105 ? attributesNodeProvider.getInfoAttributesNode() : null;
106
107 AttributesNode rootNode = attributesNode(treeWalk,
108 rootOf(
109 treeWalk.getTree(WorkingTreeIterator.class)),
110 rootOf(
111 treeWalk.getTree(DirCacheIterator.class)),
112 rootOf(treeWalk
113 .getTree(CanonicalTreeParser.class)));
114
115 expansions.put(BINARY_RULE_KEY, BINARY_RULE_ATTRIBUTES);
116 for (AttributesNode node : new AttributesNode[] { globalNode, rootNode,
117 infoNode }) {
118 if (node == null) {
119 continue;
120 }
121 for (AttributesRule rule : node.getRules()) {
122 if (rule.getPattern().startsWith(MACRO_PREFIX)) {
123 expansions.put(rule.getPattern()
124 .substring(MACRO_PREFIX.length()).trim(),
125 rule.getAttributes());
126 }
127 }
128 }
129 }
130
131
132
133
134
135
136
137
138 public Attributes getAttributes() throws IOException {
139 String entryPath = treeWalk.getPathString();
140 boolean isDirectory = (treeWalk.getFileMode() == FileMode.TREE);
141 Attributes attributes = new Attributes();
142
143
144 mergeInfoAttributes(entryPath, isDirectory, attributes);
145
146
147 mergePerDirectoryEntryAttributes(entryPath, isDirectory,
148 treeWalk.getTree(WorkingTreeIterator.class),
149 treeWalk.getTree(DirCacheIterator.class),
150 treeWalk.getTree(CanonicalTreeParser.class),
151 attributes);
152
153
154 mergeGlobalAttributes(entryPath, isDirectory, attributes);
155
156
157
158 for (Attribute a : attributes.getAll()) {
159 if (a.getState() == State.UNSPECIFIED)
160 attributes.remove(a.getKey());
161 }
162
163 return attributes;
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 private void mergeGlobalAttributes(String entryPath, boolean isDirectory,
180 Attributes result) {
181 mergeAttributes(globalNode, entryPath, isDirectory, result);
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 private void mergeInfoAttributes(String entryPath, boolean isDirectory,
198 Attributes result) {
199 mergeAttributes(infoNode, entryPath, isDirectory, result);
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 private void mergePerDirectoryEntryAttributes(String entryPath,
220 boolean isDirectory,
221 @Nullable WorkingTreeIterator workingTreeIterator,
222 @Nullable DirCacheIterator dirCacheIterator,
223 @Nullable CanonicalTreeParser otherTree, Attributes result)
224 throws IOException {
225
226 if (workingTreeIterator != null || dirCacheIterator != null
227 || otherTree != null) {
228 AttributesNode attributesNode = attributesNode(
229 treeWalk, workingTreeIterator, dirCacheIterator, otherTree);
230 if (attributesNode != null) {
231 mergeAttributes(attributesNode, entryPath, isDirectory, result);
232 }
233 mergePerDirectoryEntryAttributes(entryPath, isDirectory,
234 parentOf(workingTreeIterator), parentOf(dirCacheIterator),
235 parentOf(otherTree), result);
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 protected void mergeAttributes(@Nullable AttributesNode node,
255 String entryPath,
256 boolean isDirectory, Attributes result) {
257 if (node == null)
258 return;
259 List<AttributesRule> rules = node.getRules();
260
261
262 ListIterator<AttributesRule> ruleIterator = rules
263 .listIterator(rules.size());
264 while (ruleIterator.hasPrevious()) {
265 AttributesRule rule = ruleIterator.previous();
266 if (rule.isMatch(entryPath, isDirectory)) {
267 ListIterator<Attribute> attributeIte = rule.getAttributes()
268 .listIterator(rule.getAttributes().size());
269
270
271 while (attributeIte.hasPrevious()) {
272 expandMacro(attributeIte.previous(), result);
273 }
274 }
275 }
276 }
277
278
279
280
281
282
283
284 protected void expandMacro(Attribute attr, Attributes result) {
285
286 if (result.containsKey(attr.getKey()))
287 return;
288
289
290 result.put(attr);
291
292 List<Attribute> expansion = expansions.get(attr.getKey());
293 if (expansion == null) {
294 return;
295 }
296 switch (attr.getState()) {
297 case UNSET: {
298 for (Attribute e : expansion) {
299 switch (e.getState()) {
300 case SET:
301 expandMacro(new Attribute(e.getKey(), State.UNSET), result);
302 break;
303 case UNSET:
304 expandMacro(new Attribute(e.getKey(), State.SET), result);
305 break;
306 case UNSPECIFIED:
307 expandMacro(new Attribute(e.getKey(), State.UNSPECIFIED),
308 result);
309 break;
310 case CUSTOM:
311 default:
312 expandMacro(e, result);
313 }
314 }
315 break;
316 }
317 case CUSTOM: {
318 for (Attribute e : expansion) {
319 switch (e.getState()) {
320 case SET:
321 case UNSET:
322 case UNSPECIFIED:
323 expandMacro(e, result);
324 break;
325 case CUSTOM:
326 default:
327 expandMacro(new Attribute(e.getKey(), attr.getValue()),
328 result);
329 }
330 }
331 break;
332 }
333 case UNSPECIFIED: {
334 for (Attribute e : expansion) {
335 expandMacro(new Attribute(e.getKey(), State.UNSPECIFIED),
336 result);
337 }
338 break;
339 }
340 case SET:
341 default:
342 for (Attribute e : expansion) {
343 expandMacro(e, result);
344 }
345 break;
346 }
347 }
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366 private static AttributesNode attributesNode(TreeWalk treeWalk,
367 @Nullable WorkingTreeIterator workingTreeIterator,
368 @Nullable DirCacheIterator dirCacheIterator,
369 @Nullable CanonicalTreeParser otherTree) throws IOException {
370 AttributesNode attributesNode = null;
371 switch (treeWalk.getOperationType()) {
372 case CHECKIN_OP:
373 if (workingTreeIterator != null) {
374 attributesNode = workingTreeIterator.getEntryAttributesNode();
375 }
376 if (attributesNode == null && dirCacheIterator != null) {
377 attributesNode = dirCacheIterator
378 .getEntryAttributesNode(treeWalk.getObjectReader());
379 }
380 if (attributesNode == null && otherTree != null) {
381 attributesNode = otherTree
382 .getEntryAttributesNode(treeWalk.getObjectReader());
383 }
384 break;
385 case CHECKOUT_OP:
386 if (otherTree != null) {
387 attributesNode = otherTree
388 .getEntryAttributesNode(treeWalk.getObjectReader());
389 }
390 if (attributesNode == null && dirCacheIterator != null) {
391 attributesNode = dirCacheIterator
392 .getEntryAttributesNode(treeWalk.getObjectReader());
393 }
394 if (attributesNode == null && workingTreeIterator != null) {
395 attributesNode = workingTreeIterator.getEntryAttributesNode();
396 }
397 break;
398 default:
399 throw new IllegalStateException(
400 "The only supported operation types are:"
401 + OperationType.CHECKIN_OP + ","
402 + OperationType.CHECKOUT_OP);
403 }
404
405 return attributesNode;
406 }
407
408 private static <T extends AbstractTreeIterator> T parentOf(@Nullable T node) {
409 if(node==null) return null;
410 @SuppressWarnings("unchecked")
411 Class<T> type = (Class<T>) node.getClass();
412 AbstractTreeIterator parent = node.parent;
413 if (type.isInstance(parent)) {
414 return type.cast(parent);
415 }
416 return null;
417 }
418
419 private static <T extends AbstractTreeIterator> T rootOf(
420 @Nullable T node) {
421 if(node==null) return null;
422 AbstractTreeIterator t=node;
423 while (t!= null && t.parent != null) {
424 t= t.parent;
425 }
426 @SuppressWarnings("unchecked")
427 Class<T> type = (Class<T>) node.getClass();
428 if (type.isInstance(t)) {
429 return type.cast(t);
430 }
431 return null;
432 }
433
434 }