blob: ef67d494190cbbfc817acf67bcdbdfad82654876 [file] [log] [blame]
/*
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.ignore;
import static org.eclipse.jgit.ignore.internal.IMatcher.NO_MATCH;
import static org.eclipse.jgit.ignore.internal.Strings.isDirectoryPattern;
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailing;
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailingWhitespace;
import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.ignore.internal.IMatcher;
import org.eclipse.jgit.ignore.internal.PathMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* "Fast" (compared with IgnoreRule) git ignore rule implementation supporting
* also double star <code>**<code> pattern.
* <p>
* This class is immutable and thread safe.
*
* @since 3.6
*/
public class FastIgnoreRule {
private final static Logger LOG = LoggerFactory
.getLogger(FastIgnoreRule.class);
/**
* Character used as default path separator for ignore entries
*/
public static final char PATH_SEPARATOR = '/';
private final IMatcher matcher;
private final boolean inverse;
private final boolean dirOnly;
/**
*
* @param pattern
* ignore pattern as described in <a href=
* "https://www.kernel.org/pub/software/scm/git/docs/gitignore.html"
* >git manual</a>. If pattern is invalid or is not a pattern
* (comment), this rule doesn't match anything.
*/
public FastIgnoreRule(String pattern) {
if (pattern == null)
throw new IllegalArgumentException("Pattern must not be null!"); //$NON-NLS-1$
if (pattern.length() == 0) {
dirOnly = false;
inverse = false;
this.matcher = NO_MATCH;
return;
}
inverse = pattern.charAt(0) == '!';
if (inverse) {
pattern = pattern.substring(1);
if (pattern.length() == 0) {
dirOnly = false;
this.matcher = NO_MATCH;
return;
}
}
if (pattern.charAt(0) == '#') {
this.matcher = NO_MATCH;
dirOnly = false;
return;
}
if (pattern.charAt(0) == '\\' && pattern.length() > 1) {
char next = pattern.charAt(1);
if (next == '!' || next == '#') {
// remove backslash escaping first special characters
pattern = pattern.substring(1);
}
}
dirOnly = isDirectoryPattern(pattern);
if (dirOnly) {
pattern = stripTrailingWhitespace(pattern);
pattern = stripTrailing(pattern, PATH_SEPARATOR);
if (pattern.length() == 0) {
this.matcher = NO_MATCH;
return;
}
}
IMatcher m;
try {
m = PathMatcher.createPathMatcher(pattern,
Character.valueOf(PATH_SEPARATOR), dirOnly);
} catch (InvalidPatternException e) {
m = NO_MATCH;
LOG.error(e.getMessage(), e);
}
this.matcher = m;
}
/**
* Returns true if a match was made. <br>
* This function does NOT return the actual ignore status of the target!
* Please consult {@link #getResult()} for the negation status. The actual
* ignore status may be true or false depending on whether this rule is an
* ignore rule or a negation rule.
*
* @param path
* Name pattern of the file, relative to the base directory of
* this rule
* @param directory
* Whether the target file is a directory or not
* @return True if a match was made. This does not necessarily mean that the
* target is ignored. Call {@link #getResult() getResult()} for the
* result.
*/
public boolean isMatch(String path, boolean directory) {
if (path == null)
return false;
if (path.length() == 0)
return false;
boolean match = matcher.matches(path, directory);
return match;
}
/**
* @return True if the pattern is just a file name and not a path
*/
public boolean getNameOnly() {
return !(matcher instanceof PathMatcher);
}
/**
*
* @return True if the pattern should match directories only
*/
public boolean dirOnly() {
return dirOnly;
}
/**
* Indicates whether the rule is non-negation or negation.
*
* @return True if the pattern had a "!" in front of it
*/
public boolean getNegation() {
return inverse;
}
/**
* Indicates whether the rule is non-negation or negation.
*
* @return True if the target is to be ignored, false otherwise.
*/
public boolean getResult() {
return !inverse;
}
/**
* @return true if the rule never matches (comment line or broken pattern)
* @since 4.1
*/
public boolean isEmpty() {
return matcher == NO_MATCH;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (inverse)
sb.append('!');
sb.append(matcher);
if (dirOnly)
sb.append(PATH_SEPARATOR);
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (inverse ? 1231 : 1237);
result = prime * result + (dirOnly ? 1231 : 1237);
result = prime * result + ((matcher == null) ? 0 : matcher.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof FastIgnoreRule))
return false;
FastIgnoreRule other = (FastIgnoreRule) obj;
if (inverse != other.inverse)
return false;
if (dirOnly != other.dirOnly)
return false;
return matcher.equals(other.matcher);
}
}