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 }