GroupHead.java

  1. /*
  2.  * Copyright (C) 2008, Florian Köberle <florianskarten@web.de> 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.fnmatch;

  11. import java.text.MessageFormat;
  12. import java.util.ArrayList;
  13. import java.util.List;
  14. import java.util.regex.Matcher;
  15. import java.util.regex.Pattern;

  16. import org.eclipse.jgit.errors.InvalidPatternException;
  17. import org.eclipse.jgit.internal.JGitText;

  18. final class GroupHead extends AbstractHead {
  19.     private final List<CharacterPattern> characterClasses;

  20.     private static final Pattern REGEX_PATTERN = Pattern
  21.             .compile("([^-][-][^-]|\\[[.:=].*?[.:=]\\])"); //$NON-NLS-1$

  22.     private final boolean inverse;

  23.     GroupHead(String pattern, String wholePattern)
  24.             throws InvalidPatternException {
  25.         super(false);
  26.         this.characterClasses = new ArrayList<>();
  27.         this.inverse = pattern.startsWith("!"); //$NON-NLS-1$
  28.         if (inverse) {
  29.             pattern = pattern.substring(1);
  30.         }
  31.         final Matcher matcher = REGEX_PATTERN.matcher(pattern);
  32.         while (matcher.find()) {
  33.             final String characterClass = matcher.group(0);
  34.             if (characterClass.length() == 3 && characterClass.charAt(1) == '-') {
  35.                 final char start = characterClass.charAt(0);
  36.                 final char end = characterClass.charAt(2);
  37.                 characterClasses.add(new CharacterRange(start, end));
  38.             } else if (characterClass.equals("[:alnum:]")) { //$NON-NLS-1$
  39.                 characterClasses.add(LetterPattern.INSTANCE);
  40.                 characterClasses.add(DigitPattern.INSTANCE);
  41.             } else if (characterClass.equals("[:alpha:]")) { //$NON-NLS-1$
  42.                 characterClasses.add(LetterPattern.INSTANCE);
  43.             } else if (characterClass.equals("[:blank:]")) { //$NON-NLS-1$
  44.                 characterClasses.add(new OneCharacterPattern(' '));
  45.                 characterClasses.add(new OneCharacterPattern('\t'));
  46.             } else if (characterClass.equals("[:cntrl:]")) { //$NON-NLS-1$
  47.                 characterClasses.add(new CharacterRange('\u0000', '\u001F'));
  48.                 characterClasses.add(new OneCharacterPattern('\u007F'));
  49.             } else if (characterClass.equals("[:digit:]")) { //$NON-NLS-1$
  50.                 characterClasses.add(DigitPattern.INSTANCE);
  51.             } else if (characterClass.equals("[:graph:]")) { //$NON-NLS-1$
  52.                 characterClasses.add(new CharacterRange('\u0021', '\u007E'));
  53.                 characterClasses.add(LetterPattern.INSTANCE);
  54.                 characterClasses.add(DigitPattern.INSTANCE);
  55.             } else if (characterClass.equals("[:lower:]")) { //$NON-NLS-1$
  56.                 characterClasses.add(LowerPattern.INSTANCE);
  57.             } else if (characterClass.equals("[:print:]")) { //$NON-NLS-1$
  58.                 characterClasses.add(new CharacterRange('\u0020', '\u007E'));
  59.                 characterClasses.add(LetterPattern.INSTANCE);
  60.                 characterClasses.add(DigitPattern.INSTANCE);
  61.             } else if (characterClass.equals("[:punct:]")) { //$NON-NLS-1$
  62.                 characterClasses.add(PunctPattern.INSTANCE);
  63.             } else if (characterClass.equals("[:space:]")) { //$NON-NLS-1$
  64.                 characterClasses.add(WhitespacePattern.INSTANCE);
  65.             } else if (characterClass.equals("[:upper:]")) { //$NON-NLS-1$
  66.                 characterClasses.add(UpperPattern.INSTANCE);
  67.             } else if (characterClass.equals("[:xdigit:]")) { //$NON-NLS-1$
  68.                 characterClasses.add(new CharacterRange('0', '9'));
  69.                 characterClasses.add(new CharacterRange('a', 'f'));
  70.                 characterClasses.add(new CharacterRange('A', 'F'));
  71.             } else if (characterClass.equals("[:word:]")) { //$NON-NLS-1$
  72.                 characterClasses.add(new OneCharacterPattern('_'));
  73.                 characterClasses.add(LetterPattern.INSTANCE);
  74.                 characterClasses.add(DigitPattern.INSTANCE);
  75.             } else {
  76.                 final String message = MessageFormat.format(
  77.                         JGitText.get().characterClassIsNotSupported,
  78.                         characterClass);
  79.                 throw new InvalidPatternException(message, wholePattern);
  80.             }

  81.             pattern = matcher.replaceFirst(""); //$NON-NLS-1$
  82.             matcher.reset(pattern);
  83.         }
  84.         // pattern contains now no ranges
  85.         for (int i = 0; i < pattern.length(); i++) {
  86.             final char c = pattern.charAt(i);
  87.             characterClasses.add(new OneCharacterPattern(c));
  88.         }
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     protected final boolean matches(char c) {
  93.         for (CharacterPattern pattern : characterClasses) {
  94.             if (pattern.matches(c)) {
  95.                 return !inverse;
  96.             }
  97.         }
  98.         return inverse;
  99.     }

  100.     private interface CharacterPattern {
  101.         /**
  102.          * @param c
  103.          *            the character to test
  104.          * @return returns true if the character matches a pattern.
  105.          */
  106.         boolean matches(char c);
  107.     }

  108.     private static final class CharacterRange implements CharacterPattern {
  109.         private final char start;

  110.         private final char end;

  111.         CharacterRange(char start, char end) {
  112.             this.start = start;
  113.             this.end = end;
  114.         }

  115.         @Override
  116.         public final boolean matches(char c) {
  117.             return start <= c && c <= end;
  118.         }
  119.     }

  120.     private static final class DigitPattern implements CharacterPattern {
  121.         static final GroupHead.DigitPattern INSTANCE = new DigitPattern();

  122.         @Override
  123.         public final boolean matches(char c) {
  124.             return Character.isDigit(c);
  125.         }
  126.     }

  127.     private static final class LetterPattern implements CharacterPattern {
  128.         static final GroupHead.LetterPattern INSTANCE = new LetterPattern();

  129.         @Override
  130.         public final boolean matches(char c) {
  131.             return Character.isLetter(c);
  132.         }
  133.     }

  134.     private static final class LowerPattern implements CharacterPattern {
  135.         static final GroupHead.LowerPattern INSTANCE = new LowerPattern();

  136.         @Override
  137.         public final boolean matches(char c) {
  138.             return Character.isLowerCase(c);
  139.         }
  140.     }

  141.     private static final class UpperPattern implements CharacterPattern {
  142.         static final GroupHead.UpperPattern INSTANCE = new UpperPattern();

  143.         @Override
  144.         public final boolean matches(char c) {
  145.             return Character.isUpperCase(c);
  146.         }
  147.     }

  148.     private static final class WhitespacePattern implements CharacterPattern {
  149.         static final GroupHead.WhitespacePattern INSTANCE = new WhitespacePattern();

  150.         @Override
  151.         public final boolean matches(char c) {
  152.             return Character.isWhitespace(c);
  153.         }
  154.     }

  155.     private static final class OneCharacterPattern implements CharacterPattern {
  156.         private char expectedCharacter;

  157.         OneCharacterPattern(char c) {
  158.             this.expectedCharacter = c;
  159.         }

  160.         @Override
  161.         public final boolean matches(char c) {
  162.             return this.expectedCharacter == c;
  163.         }
  164.     }

  165.     private static final class PunctPattern implements CharacterPattern {
  166.         static final GroupHead.PunctPattern INSTANCE = new PunctPattern();

  167.         private static String punctCharacters = "-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"; //$NON-NLS-1$

  168.         @Override
  169.         public boolean matches(char c) {
  170.             return punctCharacters.indexOf(c) != -1;
  171.         }
  172.     }

  173. }