1 /* 2 * Copyright (C) 2010, Red Hat Inc. 3 * and other copyright owners as documented in the project's IP log. 4 * 5 * This program and the accompanying materials are made available 6 * under the terms of the Eclipse Distribution License v1.0 which 7 * accompanies this distribution, is reproduced below, and is 8 * available at http://www.eclipse.org/org/documents/edl-v10.php 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials provided 22 * with the distribution. 23 * 24 * - Neither the name of the Eclipse Foundation, Inc. nor the 25 * names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior 27 * written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 package org.eclipse.jgit.ignore; 44 45 import java.io.BufferedReader; 46 import java.io.IOException; 47 import java.io.InputStream; 48 import java.io.InputStreamReader; 49 import java.util.ArrayList; 50 import java.util.Collections; 51 import java.util.List; 52 53 import org.eclipse.jgit.lib.Constants; 54 55 /** 56 * Represents a bundle of ignore rules inherited from a base directory. 57 * 58 * This class is not thread safe, it maintains state about the last match. 59 */ 60 public class IgnoreNode { 61 /** Result from {@link IgnoreNode#isIgnored(String, boolean)}. */ 62 public static enum MatchResult { 63 /** The file is not ignored, due to a rule saying its not ignored. */ 64 NOT_IGNORED, 65 66 /** The file is ignored due to a rule in this node. */ 67 IGNORED, 68 69 /** The ignore status is unknown, check inherited rules. */ 70 CHECK_PARENT, 71 72 /** 73 * The first previous (parent) ignore rule match (if any) should be 74 * negated, and then inherited rules applied. 75 * 76 * @since 3.6 77 */ 78 CHECK_PARENT_NEGATE_FIRST_MATCH; 79 } 80 81 /** The rules that have been parsed into this node. */ 82 private final List<FastIgnoreRule> rules; 83 84 /** Create an empty ignore node with no rules. */ 85 public IgnoreNode() { 86 rules = new ArrayList<>(); 87 } 88 89 /** 90 * Create an ignore node with given rules. 91 * 92 * @param rules 93 * list of rules. 94 **/ 95 public IgnoreNode(List<FastIgnoreRule> rules) { 96 this.rules = rules; 97 } 98 99 /** 100 * Parse files according to gitignore standards. 101 * 102 * @param in 103 * input stream holding the standard ignore format. The caller is 104 * responsible for closing the stream. 105 * @throws IOException 106 * Error thrown when reading an ignore file. 107 */ 108 public void parse(InputStream in) throws IOException { 109 BufferedReader br = asReader(in); 110 String txt; 111 while ((txt = br.readLine()) != null) { 112 if (txt.length() > 0 && !txt.startsWith("#") && !txt.equals("/")) { //$NON-NLS-1$ //$NON-NLS-2$ 113 FastIgnoreRule rule = new FastIgnoreRule(txt); 114 if (!rule.isEmpty()) { 115 rules.add(rule); 116 } 117 } 118 } 119 } 120 121 private static BufferedReader asReader(InputStream in) { 122 return new BufferedReader(new InputStreamReader(in, Constants.CHARSET)); 123 } 124 125 /** @return list of all ignore rules held by this node. */ 126 public List<FastIgnoreRule> getRules() { 127 return Collections.unmodifiableList(rules); 128 } 129 130 /** 131 * Determine if an entry path matches an ignore rule. 132 * 133 * @param entryPath 134 * the path to test. The path must be relative to this ignore 135 * node's own repository path, and in repository path format 136 * (uses '/' and not '\'). 137 * @param isDirectory 138 * true if the target item is a directory. 139 * @return status of the path. 140 */ 141 public MatchResult isIgnored(String entryPath, boolean isDirectory) { 142 return isIgnored(entryPath, isDirectory, false); 143 } 144 145 /** 146 * Determine if an entry path matches an ignore rule. 147 * 148 * @param entryPath 149 * the path to test. The path must be relative to this ignore 150 * node's own repository path, and in repository path format 151 * (uses '/' and not '\'). 152 * @param isDirectory 153 * true if the target item is a directory. 154 * @param negateFirstMatch 155 * true if the first match should be negated 156 * @return status of the path. 157 * @since 3.6 158 */ 159 public MatchResult isIgnored(String entryPath, boolean isDirectory, 160 boolean negateFirstMatch) { 161 if (rules.isEmpty()) 162 if (negateFirstMatch) 163 return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH; 164 else 165 return MatchResult.CHECK_PARENT; 166 167 // Parse rules in the reverse order that they were read 168 for (int i = rules.size() - 1; i > -1; i--) { 169 FastIgnoreRule rule = rules.get(i); 170 if (rule.isMatch(entryPath, isDirectory)) { 171 if (rule.getResult()) { 172 // rule matches: path could be ignored 173 if (negateFirstMatch) 174 // ignore current match, reset "negate" flag, continue 175 negateFirstMatch = false; 176 else 177 // valid match, just return 178 return MatchResult.IGNORED; 179 } else { 180 // found negated rule 181 if (negateFirstMatch) 182 // not possible to re-include excluded ignore rule 183 return MatchResult.NOT_IGNORED; 184 else 185 // set the flag and continue 186 negateFirstMatch = true; 187 } 188 } 189 } 190 if (negateFirstMatch) 191 // negated rule found but there is no previous rule in *this* file 192 return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH; 193 // *this* file has no matching rules 194 return MatchResult.CHECK_PARENT; 195 } 196 197 @Override 198 public String toString() { 199 return rules.toString(); 200 } 201 }