View Javadoc
1   /*
2    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.treewalk.filter;
12  
13  import java.io.IOException;
14  
15  import org.eclipse.jgit.dircache.DirCacheIterator;
16  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
17  import org.eclipse.jgit.errors.MissingObjectException;
18  import org.eclipse.jgit.treewalk.TreeWalk;
19  import org.eclipse.jgit.treewalk.WorkingTreeIterator;
20  
21  /**
22   * Selects interesting tree entries during walking.
23   * <p>
24   * This is an abstract interface. Applications may implement a subclass, or use
25   * one of the predefined implementations already available within this package.
26   * <p>
27   * Unless specifically noted otherwise a TreeFilter implementation is not thread
28   * safe and may not be shared by different TreeWalk instances at the same time.
29   * This restriction allows TreeFilter implementations to cache state within
30   * their instances during {@link #include(TreeWalk)} if it is beneficial to
31   * their implementation. Deep clones created by {@link #clone()} may be used to
32   * construct a thread-safe copy of an existing filter.
33   *
34   * <p>
35   * <b>Path filters:</b>
36   * <ul>
37   * <li>Matching pathname:
38   * {@link org.eclipse.jgit.treewalk.filter.PathFilter}</li>
39   * </ul>
40   *
41   * <p>
42   * <b>Difference filters:</b>
43   * <ul>
44   * <li>Only select differences: {@link #ANY_DIFF}.</li>
45   * </ul>
46   *
47   * <p>
48   * <b>Boolean modifiers:</b>
49   * <ul>
50   * <li>AND: {@link org.eclipse.jgit.treewalk.filter.AndTreeFilter}</li>
51   * <li>OR: {@link org.eclipse.jgit.treewalk.filter.OrTreeFilter}</li>
52   * <li>NOT: {@link org.eclipse.jgit.treewalk.filter.NotTreeFilter}</li>
53   * </ul>
54   */
55  public abstract class TreeFilter {
56  	/** Selects all tree entries. */
57  	public static final TreeFilter ALL = new AllFilter();
58  
59  	private static final class AllFilter extends TreeFilter {
60  		@Override
61  		public boolean include(TreeWalk walker) {
62  			return true;
63  		}
64  
65  		@Override
66  		public boolean shouldBeRecursive() {
67  			return false;
68  		}
69  
70  		@Override
71  		public TreeFilter clone() {
72  			return this;
73  		}
74  
75  		@Override
76  		public String toString() {
77  			return "ALL"; //$NON-NLS-1$
78  		}
79  	}
80  
81  	/**
82  	 * Selects only tree entries which differ between at least 2 trees.
83  	 * <p>
84  	 * This filter also prevents a TreeWalk from recursing into a subtree if all
85  	 * parent trees have the identical subtree at the same path. This
86  	 * dramatically improves walk performance as only the changed subtrees are
87  	 * entered into.
88  	 * <p>
89  	 * If this filter is applied to a walker with only one tree it behaves like
90  	 * {@link #ALL}, or as though the walker was matching a virtual empty tree
91  	 * against the single tree it was actually given. Applications may wish to
92  	 * treat such a difference as "all names added".
93  	 * <p>
94  	 * When comparing {@link WorkingTreeIterator} and {@link DirCacheIterator}
95  	 * applications should use {@link IndexDiffFilter}.
96  	 */
97  	public static final TreeFilter ANY_DIFF = new AnyDiffFilter();
98  
99  	private static final class AnyDiffFilter extends TreeFilter {
100 		private static final int baseTree = 0;
101 
102 		@Override
103 		public boolean include(TreeWalk walker) {
104 			final int n = walker.getTreeCount();
105 			if (n == 1) // Assume they meant difference to empty tree.
106 				return true;
107 
108 			final int m = walker.getRawMode(baseTree);
109 			for (int i = 1; i < n; i++)
110 				if (walker.getRawMode(i) != m || !walker.idEqual(i, baseTree))
111 					return true;
112 			return false;
113 		}
114 
115 		@Override
116 		public boolean shouldBeRecursive() {
117 			return false;
118 		}
119 
120 		@Override
121 		public TreeFilter clone() {
122 			return this;
123 		}
124 
125 		@Override
126 		public String toString() {
127 			return "ANY_DIFF"; //$NON-NLS-1$
128 		}
129 	}
130 
131 	/**
132 	 * Create a new filter that does the opposite of this filter.
133 	 *
134 	 * @return a new filter that includes tree entries this filter rejects.
135 	 */
136 	public TreeFilter negate() {
137 		return NotTreeFilter.create(this);
138 	}
139 
140 	/**
141 	 * Determine if the current entry is interesting to report.
142 	 * <p>
143 	 * This method is consulted for subtree entries even if
144 	 * {@link org.eclipse.jgit.treewalk.TreeWalk#isRecursive()} is enabled. The
145 	 * consultation allows the filter to bypass subtree recursion on a
146 	 * case-by-case basis, even when recursion is enabled at the application
147 	 * level.
148 	 *
149 	 * @param walker
150 	 *            the walker the filter needs to examine.
151 	 * @return true if the current entry should be seen by the application;
152 	 *         false to hide the entry.
153 	 * @throws org.eclipse.jgit.errors.MissingObjectException
154 	 *             an object the filter needs to consult to determine its answer
155 	 *             does not exist in the Git repository the walker is operating
156 	 *             on. Filtering this current walker entry is impossible without
157 	 *             the object.
158 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
159 	 *             an object the filter needed to consult was not of the
160 	 *             expected object type. This usually indicates a corrupt
161 	 *             repository, as an object link is referencing the wrong type.
162 	 * @throws java.io.IOException
163 	 *             a loose object or pack file could not be read to obtain data
164 	 *             necessary for the filter to make its decision.
165 	 */
166 	public abstract boolean include(TreeWalk walker)
167 			throws MissingObjectException, IncorrectObjectTypeException,
168 			IOException;
169 
170 	/**
171 	 * Determine if the current entry is a parent, a match, or no match.
172 	 * <p>
173 	 * This method extends the result returned by {@link #include(TreeWalk)}
174 	 * with a third option (-1), splitting the value true. This gives the
175 	 * application a possibility to distinguish between an exact match and the
176 	 * case when a subtree to the current entry might be a match.
177 	 *
178 	 * @param walker
179 	 *            the walker the filter needs to examine.
180 	 * @return -1 if the current entry is a parent of the filter but no exact
181 	 *         match has been made; 0 if the current entry should be seen by the
182 	 *         application; 1 if it should be hidden.
183 	 * @throws org.eclipse.jgit.errors.MissingObjectException
184 	 *             as thrown by {@link #include(TreeWalk)}
185 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
186 	 *             as thrown by {@link #include(TreeWalk)}
187 	 * @throws java.io.IOException
188 	 *             as thrown by {@link #include(TreeWalk)}
189 	 * @since 4.7
190 	 */
191 	public int matchFilter(TreeWalk walker)
192 			throws MissingObjectException, IncorrectObjectTypeException,
193 			IOException
194 	{
195 		return include(walker) ? 0 : 1;
196 	}
197 
198 	/**
199 	 * Does this tree filter require a recursive walk to match everything?
200 	 * <p>
201 	 * If this tree filter is matching on full entry path names and its pattern
202 	 * is looking for a '/' then the filter would require a recursive TreeWalk
203 	 * to accurately make its decisions. The walker is not required to enable
204 	 * recursive behavior for any particular filter, this is only a hint.
205 	 *
206 	 * @return true if the filter would like to have the walker recurse into
207 	 *         subtrees to make sure it matches everything correctly; false if
208 	 *         the filter does not require entering subtrees.
209 	 */
210 	public abstract boolean shouldBeRecursive();
211 
212 	/**
213 	 * {@inheritDoc}
214 	 *
215 	 * Clone this tree filter, including its parameters.
216 	 * <p>
217 	 * This is a deep clone. If this filter embeds objects or other filters it
218 	 * must also clone those, to ensure the instances do not share mutable data.
219 	 */
220 	@Override
221 	public abstract TreeFilter clone();
222 
223 	/** {@inheritDoc} */
224 	@Override
225 	public String toString() {
226 		String n = getClass().getName();
227 		int lastDot = n.lastIndexOf('.');
228 		if (lastDot >= 0) {
229 			n = n.substring(lastDot + 1);
230 		}
231 		return n.replace('$', '.');
232 	}
233 }