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<FastIgnoreRule>(); 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 txt = txt.trim(); 113 if (txt.length() > 0 && !txt.startsWith("#") && !txt.equals("/")) //$NON-NLS-1$ //$NON-NLS-2$ 114 rules.add(new FastIgnoreRule(txt)); 115 } 116 } 117 118 private static BufferedReader asReader(InputStream in) { 119 return new BufferedReader(new InputStreamReader(in, Constants.CHARSET)); 120 } 121 122 /** @return list of all ignore rules held by this node. */ 123 public List<FastIgnoreRule> getRules() { 124 return Collections.unmodifiableList(rules); 125 } 126 127 /** 128 * Determine if an entry path matches an ignore rule. 129 * 130 * @param entryPath 131 * the path to test. The path must be relative to this ignore 132 * node's own repository path, and in repository path format 133 * (uses '/' and not '\'). 134 * @param isDirectory 135 * true if the target item is a directory. 136 * @return status of the path. 137 */ 138 public MatchResult isIgnored(String entryPath, boolean isDirectory) { 139 return isIgnored(entryPath, isDirectory, false); 140 } 141 142 /** 143 * Determine if an entry path matches an ignore rule. 144 * 145 * @param entryPath 146 * the path to test. The path must be relative to this ignore 147 * node's own repository path, and in repository path format 148 * (uses '/' and not '\'). 149 * @param isDirectory 150 * true if the target item is a directory. 151 * @param negateFirstMatch 152 * true if the first match should be negated 153 * @return status of the path. 154 * @since 3.6 155 */ 156 public MatchResult isIgnored(String entryPath, boolean isDirectory, 157 boolean negateFirstMatch) { 158 if (rules.isEmpty()) 159 if (negateFirstMatch) 160 return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH; 161 else 162 return MatchResult.CHECK_PARENT; 163 164 // Parse rules in the reverse order that they were read 165 for (int i = rules.size() - 1; i > -1; i--) { 166 FastIgnoreRule rule = rules.get(i); 167 if (rule.isMatch(entryPath, isDirectory)) { 168 if (rule.getResult()) { 169 // rule matches: path could be ignored 170 if (negateFirstMatch) 171 // ignore current match, reset "negate" flag, continue 172 negateFirstMatch = false; 173 else 174 // valid match, just return 175 return MatchResult.IGNORED; 176 } else { 177 // found negated rule 178 if (negateFirstMatch) 179 // not possible to re-include excluded ignore rule 180 return MatchResult.NOT_IGNORED; 181 else 182 // set the flag and continue 183 negateFirstMatch = true; 184 } 185 } 186 } 187 if (negateFirstMatch) 188 // negated rule found but there is no previous rule in *this* file 189 return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH; 190 // *this* file has no matching rules 191 return MatchResult.CHECK_PARENT; 192 } 193 194 @Override 195 public String toString() { 196 return rules.toString(); 197 } 198 }