You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AttributesRule.java 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. * Copyright (C) 2010, 2017 Red Hat Inc. 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. package org.eclipse.jgit.attributes;
  11. import static org.eclipse.jgit.ignore.IMatcher.NO_MATCH;
  12. import java.util.ArrayList;
  13. import java.util.Collections;
  14. import java.util.List;
  15. import org.eclipse.jgit.attributes.Attribute.State;
  16. import org.eclipse.jgit.errors.InvalidPatternException;
  17. import org.eclipse.jgit.ignore.FastIgnoreRule;
  18. import org.eclipse.jgit.ignore.IMatcher;
  19. import org.eclipse.jgit.ignore.internal.PathMatcher;
  20. /**
  21. * A single attributes rule corresponding to one line in a .gitattributes file.
  22. *
  23. * Inspiration from: {@link org.eclipse.jgit.ignore.FastIgnoreRule}
  24. *
  25. * @since 3.7
  26. */
  27. public class AttributesRule {
  28. /**
  29. * regular expression for splitting attributes - space, tab and \r (the C
  30. * implementation oddly enough allows \r between attributes)
  31. * */
  32. private static final String ATTRIBUTES_SPLIT_REGEX = "[ \t\r]"; //$NON-NLS-1$
  33. private static List<Attribute> parseAttributes(String attributesLine) {
  34. // the C implementation oddly enough allows \r between attributes too.
  35. ArrayList<Attribute> result = new ArrayList<>();
  36. for (String attribute : attributesLine.split(ATTRIBUTES_SPLIT_REGEX)) {
  37. attribute = attribute.trim();
  38. if (attribute.length() == 0)
  39. continue;
  40. if (attribute.startsWith("-")) {//$NON-NLS-1$
  41. if (attribute.length() > 1)
  42. result.add(new Attribute(attribute.substring(1),
  43. State.UNSET));
  44. continue;
  45. }
  46. if (attribute.startsWith("!")) {//$NON-NLS-1$
  47. if (attribute.length() > 1)
  48. result.add(new Attribute(attribute.substring(1),
  49. State.UNSPECIFIED));
  50. continue;
  51. }
  52. final int equalsIndex = attribute.indexOf('=');
  53. if (equalsIndex == -1)
  54. result.add(new Attribute(attribute, State.SET));
  55. else {
  56. String attributeKey = attribute.substring(0, equalsIndex);
  57. if (attributeKey.length() > 0) {
  58. String attributeValue = attribute
  59. .substring(equalsIndex + 1);
  60. result.add(new Attribute(attributeKey, attributeValue));
  61. }
  62. }
  63. }
  64. return result;
  65. }
  66. private final String pattern;
  67. private final List<Attribute> attributes;
  68. private final boolean nameOnly;
  69. private final boolean dirOnly;
  70. private final IMatcher matcher;
  71. /**
  72. * Create a new attribute rule with the given pattern. Assumes that the
  73. * pattern is already trimmed.
  74. *
  75. * @param pattern
  76. * Base pattern for the attributes rule. This pattern will be
  77. * parsed to generate rule parameters. It can not be
  78. * <code>null</code>.
  79. * @param attributes
  80. * the rule attributes. This string will be parsed to read the
  81. * attributes.
  82. */
  83. public AttributesRule(String pattern, String attributes) {
  84. this.attributes = parseAttributes(attributes);
  85. if (pattern.endsWith("/")) { //$NON-NLS-1$
  86. pattern = pattern.substring(0, pattern.length() - 1);
  87. dirOnly = true;
  88. } else {
  89. dirOnly = false;
  90. }
  91. int slashIndex = pattern.indexOf('/');
  92. if (slashIndex < 0) {
  93. nameOnly = true;
  94. } else if (slashIndex == 0) {
  95. nameOnly = false;
  96. } else {
  97. nameOnly = false;
  98. // Contains "/" but does not start with one
  99. // Adding / to the start should not interfere with matching
  100. pattern = "/" + pattern; //$NON-NLS-1$
  101. }
  102. IMatcher candidateMatcher = NO_MATCH;
  103. try {
  104. candidateMatcher = PathMatcher.createPathMatcher(pattern,
  105. Character.valueOf(FastIgnoreRule.PATH_SEPARATOR), dirOnly);
  106. } catch (InvalidPatternException e) {
  107. // ignore: invalid patterns are silently ignored
  108. }
  109. this.matcher = candidateMatcher;
  110. this.pattern = pattern;
  111. }
  112. /**
  113. * Whether to match directories only
  114. *
  115. * @return {@code true} if the pattern should match directories only
  116. * @since 4.3
  117. */
  118. public boolean isDirOnly() {
  119. return dirOnly;
  120. }
  121. /**
  122. * Return the attributes.
  123. *
  124. * @return an unmodifiable list of attributes (never returns
  125. * <code>null</code>)
  126. */
  127. public List<Attribute> getAttributes() {
  128. return Collections.unmodifiableList(attributes);
  129. }
  130. /**
  131. * Whether the pattern is only a file name and not a path
  132. *
  133. * @return <code>true</code> if the pattern is just a file name and not a
  134. * path
  135. */
  136. public boolean isNameOnly() {
  137. return nameOnly;
  138. }
  139. /**
  140. * Get the pattern
  141. *
  142. * @return The blob pattern to be used as a matcher (never returns
  143. * <code>null</code>)
  144. */
  145. public String getPattern() {
  146. return pattern;
  147. }
  148. /**
  149. * Returns <code>true</code> if a match was made.
  150. *
  151. * @param relativeTarget
  152. * Name pattern of the file, relative to the base directory of
  153. * this rule
  154. * @param isDirectory
  155. * Whether the target file is a directory or not
  156. * @return True if a match was made.
  157. */
  158. public boolean isMatch(String relativeTarget, boolean isDirectory) {
  159. if (relativeTarget == null)
  160. return false;
  161. if (relativeTarget.length() == 0)
  162. return false;
  163. boolean match = matcher.matches(relativeTarget, isDirectory, true);
  164. return match;
  165. }
  166. /** {@inheritDoc} */
  167. @Override
  168. public String toString() {
  169. StringBuilder sb = new StringBuilder();
  170. sb.append(pattern);
  171. for (Attribute a : attributes) {
  172. sb.append(" "); //$NON-NLS-1$
  173. sb.append(a);
  174. }
  175. return sb.toString();
  176. }
  177. }