Package com.google.gwt.dev.resource.impl

Source Code of com.google.gwt.dev.resource.impl.DefaultFilters

/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.resource.impl;

import com.google.gwt.thirdparty.org.apache.tools.ant.types.ZipScanner;

import java.util.regex.Pattern;

/**
* A singleton class that provides blazingly fast implementation of the default
* excludes of Ant's {@link com.google.gwt.thirdparty.org.apache.tools.ant.DirectoryScanner}, assuming
* case-sensitiveness.
*
* TODO: this class needs to be revisited, when Gwt's Ant is upgraded.
*
* Currently, we do not go to ant if (a) the filterList is empty, or (b) the
* filterList has "common" patterns. Exception: When path ends in '/', we defer
* to ant.
*
* TODO: This code could be made more general and cleaner by removing the
* dependency on Ant completely. All ant patterns could be compiled into
* reg-exps. That could also make the code faster. Plus, at several places,
* Ant's documentation seems to be incomplete. Instead, perhaps, we should
* specify our own rules for writing patterns.
*/
public class DefaultFilters {

  /**
   * Constants to represent the type of files that will be filtered.
   */
  public static enum FilterFileType {
    RESOURCE_FILES(null), //
    JAVA_FILES(".java"), //
    CLASS_FILES(".class");

    private final String suffix;

    /* used when defaultExcludes is false */
    private final ResourceFilter justThisFileTypeFilter = new ResourceFilter() {
      public boolean allows(String path) {
        return defaultAntIncludes.allows(path) && matches(path);
      }
    };

    private final ResourceFilter defaultFilter = new ResourceFilter() {

      public boolean allows(String path) {
        return getFileTypeFilter().allows(path)
        && !isDefaultExcluded(path);
      }
    };

    private FilterFileType(String suffix) {
      this.suffix = suffix;
    }

    public ResourceFilter getDefaultFilter() {
      return defaultFilter;
    }

    /* used when defaultExcludes is false */
    public ResourceFilter getFileTypeFilter() {
      return justThisFileTypeFilter;
    }

    public String getSuffix() {
      return suffix;
    }

    public boolean matches(String path) {
      if (suffix == null) {
        return true;
      }
      return path.endsWith(suffix);
    }
  }

  // \w (word character), ., $, /, -, *, ~, #, %
  private static final Pattern antPattern = Pattern.compile("^[\\w\\.\\$/\\-\\*~#%]*$");

  // accepts all but paths starting with '/'. Default include list is '**'
  private static final ResourceFilter defaultAntIncludes = new ResourceFilter() {
    public boolean allows(String path) {
      return path.charAt(0) != '/';
    }
  };

  /**
   * @return <code>true</code> if given path should be excluded from resources.
   */
  private static boolean isDefaultExcluded(String path) {
    // CVS
    if (path.endsWith("/CVS") || path.contains("/CVS/") || path.startsWith("CVS/")
        || path.endsWith("/.cvsignore")) {
      return true;
    }
    // Subversion
    if (path.endsWith("/.svn") || path.contains("/.svn/") || path.startsWith(".svn/")
        || path.endsWith("/.svnignore")) {
      return true;
    }
    // Git
    if (path.endsWith("/.git") || path.contains("/.git/") || path.startsWith(".git/")
        || path.endsWith("/.gitignore")) {
      return true;
    }
    // SCCS
    if (path.endsWith("/SCCS") || path.contains("/SCCS/")) {
      return true;
    }
    // Visual SourceSafe
    if (path.endsWith("/vssver.scc")) {
      return true;
    }
    // Mac
    if (path.endsWith("/.DS_Store")) {
      return true;
    }
    return false;
  }

  /**
   * Returns a pattern string that can be passed in Java Pattern.compile(..).
   * For spec, see <a href="http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/manual/api/org/apache/tools/ant/DirectoryScanner.html"
   * >DirectoryScanner</a> From the spec: There is a special case regarding the
   * use of File.separators at the beginning of the pattern and the string to
   * match: When a pattern starts with a File.separator, the string to match
   * must also start with a File.separator. When a pattern does not start with a
   * File.separator, the string to match may not start with a File.separator.
   *
   * </p>
   *
   * TODO: This method could accept all ant patterns, but then all characters
   * that have a special meaning in Java's regular expression would need to be
   * escaped.
   *
   * @param antPatternString the ant pattern String.
   * @return a pattern string that can be passed in Java's Pattern.compile(..),
   *         null if cannot process the pattern.
   */
  static String getPatternFromAntPattern(String antPatternString) {
    if (!antPattern.matcher(antPatternString).matches()) {
      return null;
    }
    // do not handle patterns that have ***
    if (antPatternString.indexOf("***") != -1) {
      return null;
    }
    if (antPatternString.endsWith("/")) {
      /*
       * From the DirectoryScanner.html spec: When a pattern ends with a '/' or
       * '\', "**" is appended. if ant pattern = testing/, path = testing/foo,
       * result = true.
       */
      antPatternString = antPatternString + "**";
    }
    StringBuffer sb = new StringBuffer();
    int length = antPatternString.length();
    for (int i = 0; i < length; i++) {
      char c = antPatternString.charAt(i);
      switch (c) {
        case '.':
          sb.append("\\.");
          break;
        case '$':
          sb.append("\\$");
          break;
        case '/':
          // convert /** to (/[^/]*)* except when / is the first char.
          if (i != 0 && i + 2 < length && antPatternString.charAt(i + 1) == '*'
              && antPatternString.charAt(i + 2) == '*') {
            sb.append("(/[^/]*)*");
            i += 2; // handled 2 more chars than usual
          } else {
            sb.append(c);
          }
          break;
        case '*':
          // ** to .*
          if (i + 1 < length && antPatternString.charAt(i + 1) == '*') {
            if (i + 2 < length && antPatternString.charAt(i + 2) == '/') {
              if (i == 0) {
                /*
                 * When a pattern does not start with a File.separator, the
                 * string to match may not start with a File.separator.
                 */
                sb.append("([^/]+/)*");
              } else {
                // convert **/ to ([^/]*/)*
                sb.append("([^/]*/)*");
              }
              i += 2;
            } else {
              if (i == 0) {
                /*
                 * When a pattern does not start with a File.separator, the
                 * string to match may not start with a File.separator.
                 */
                sb.append("([^/].*)*");
              } else {
                sb.append(".*");
              }
              i++;
            }
          } else {
            sb.append("[^/]*");
          }
          break;
        default:
          sb.append(c);
          break;
      }
    }
    return sb.toString();
  }

  static ZipScanner getScanner(String[] includeList, String[] excludeList,
      String[] skipList, boolean defaultExcludes, boolean caseSensitive) {
    /*
     * Hijack Ant's ZipScanner to handle inclusions/exclusions exactly as Ant
     * does. We're only using its pattern-matching capabilities; the code path
     * I'm using never tries to hit the filesystem in Ant 1.6.5.
     */
    ZipScanner scanner = new ZipScanner();
    if (includeList.length > 0) {
      scanner.setIncludes(includeList);
    }
    if (excludeList.length > 0 || skipList.length > 0) {
      String[] excludeOrSkip = concatenate(excludeList, skipList);
      scanner.setExcludes(excludeOrSkip);
    }
    if (defaultExcludes) {
      scanner.addDefaultExcludes();
    }
    scanner.setCaseSensitive(caseSensitive);
    scanner.init();

    return scanner;
  }

  private static String[] concatenate(String[] array1, String[] array2) {
    String[] answer = new String[array1.length + array2.length];
    int i = 0;
    for (String entry : array1) {
      answer[i++] = entry;
    }
    for (String entry : array2) {
      answer[i++] = entry;
    }
    return answer;
  }

  private static Pattern getPatternFromStrings(String... patterns) {
    StringBuffer entirePattern = new StringBuffer("^");
    int length = patterns.length;
    int count = 0;
    for (String pattern : patterns) {
      entirePattern.append("(" + pattern + ")");
      if (count < length - 1) {
        entirePattern.append("|");
      }
      count++;
    }
    entirePattern.append("$");
    return Pattern.compile(entirePattern.toString());
  }

  private final ResourceFilter rejectAll = new ResourceFilter() {
    public boolean allows(String path) {
      return false;
    }
  };

  public ResourceFilter customClassFilesFilter(String includeList[],
      String excludeList[], String skipList[], boolean defaultExcludes,
      boolean caseSensitive) {
    return getCustomFilter(includeList, excludeList, skipList, defaultExcludes,
        caseSensitive, FilterFileType.CLASS_FILES);
  }

  public ResourceFilter customJavaFilter(String includeList[],
      String excludeList[], String skipList[], boolean defaultExcludes,
      boolean caseSensitive) {
    return getCustomFilter(includeList, excludeList, skipList, defaultExcludes,
        caseSensitive, FilterFileType.JAVA_FILES);
  }

  public ResourceFilter customResourceFilter(String includeList[],
      String excludeList[], String[] skipList, boolean defaultExcludes,
      boolean caseSensitive) {
    return getCustomFilter(includeList, excludeList, skipList, defaultExcludes,
        caseSensitive, FilterFileType.RESOURCE_FILES);
  }

  /**
   * Return a customResourceFiter that handles all the argument. If unable to
   * create a customResourceFilter that handles the arguments, catchAll is used
   * as the final ResourceFilter.
   */
  ResourceFilter customFilterWithCatchAll(final String includeList[],
      final String excludeList[], final String skipList[],
      final boolean defaultExcludes, final ResourceFilter catchAll,
      final FilterFileType filterFileType) {

    assert includeList.length > 0 || excludeList.length > 0
        || skipList.length > 0;

    final ResourceFilter includeFilter = getIncludesFilterPart(includeList);
    final ResourceFilter excludeFilter = getExcludesFilterPart(concatenate(
        excludeList, skipList));

    if (includeFilter == null || excludeFilter == null) {
      return catchAll;
    }
    // another common-case
    ResourceFilter filter = new ResourceFilter() {
      public boolean allows(String path) {
        // do not handle the case when pattern ends in '/'
        if (path.endsWith("/")) {
          return catchAll.allows(path);
        }
        return isPathAllowedByDefaults(path, defaultExcludes, filterFileType)
            && includeFilter.allows(path) && !excludeFilter.allows(path);
      }

      private boolean isPathAllowedByDefaults(String path,
          boolean defaultExcludes, FilterFileType filterFileType) {
        boolean fileTypeMatch = filterFileType.matches(path);
        if (!fileTypeMatch) {
          return false;
        }
        if (defaultExcludes) {
          return !isDefaultExcluded(path);
        }
        return true;
      }
    };
    return filter;
  }

  ResourceFilter getCustomFilter(final String includeList[],
      final String excludeList[], final String skipList[],
      final boolean defaultExcludes, final boolean caseSensitive,
      final FilterFileType filterFileType) {
    if (includeList.length == 0 && excludeList.length == 0
        && skipList.length == 0 && caseSensitive) {
      // optimize for the common case.
      return getMatchingDefaultFilter(defaultExcludes, filterFileType);
    }

    // don't create a catchAll in default cases
    ResourceFilter catchAll = new ResourceFilter() {
      ZipScanner scanner = getScanner(includeList, excludeList, skipList,
          defaultExcludes, caseSensitive);

      public boolean allows(String path) {
        return filterFileType.matches(path) && scanner.match(path);
      }
    };

    // for now, don't handle case sensitivity
    if (!caseSensitive) {
      return catchAll;
    }
    return customFilterWithCatchAll(includeList, excludeList, skipList,
        defaultExcludes, catchAll, filterFileType);
  }

  ResourceFilter getExcludesFilterPart(final String list[]) {
    return getFilterPart(list, false);
  }

  ResourceFilter getIncludesFilterPart(final String list[]) {
    return getFilterPart(list, true);
  }

  /**
   * @param list patterns to add to the filter.
   * @param isInclude Only used if the the array is empty. If <code>true</code>
   *          treat this as an include. Otherwise, assume this is an excludes
   *          filter and exclude all files.
   * @return
   */
  private ResourceFilter getFilterPart(final String list[],
      final boolean isInclude) {
    if (list.length == 0) {
      return isInclude ? defaultAntIncludes : rejectAll;
    }

    String patternStrings[] = new String[list.length];
    int count = 0;
    for (String antPatternString : list) {
      String patternString = getPatternFromAntPattern(antPatternString);
      if (patternString == null) {
        return null;
      }
      patternStrings[count++] = patternString;
    }

    final Pattern pattern = getPatternFromStrings(patternStrings);
    return new ResourceFilter() {
      public boolean allows(String path) {
        return pattern.matcher(path).matches();
      }
    };
  }

  /**
   * Obtain the appropriate resourceFilter based on defaultExcludes and isJava
   * values. Assumptions: caseSensitive = true,and the includesList and
   * excludesList are empty
   */
  private ResourceFilter getMatchingDefaultFilter(boolean defaultExcludes,
      FilterFileType filterFileType) {
    if (defaultExcludes) {
      return filterFileType.getDefaultFilter();
    }
    return filterFileType.getFileTypeFilter();
  }
}
TOP

Related Classes of com.google.gwt.dev.resource.impl.DefaultFilters

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.