View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.start;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.file.FileSystem;
24  import java.nio.file.FileSystems;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.PathMatcher;
28  
29  /**
30   * Common PathMatcher implementations.
31   */
32  public class PathMatchers
33  {
34      private static class NonHiddenMatcher implements PathMatcher
35      {
36          @Override
37          public boolean matches(Path path)
38          {
39              try
40              {
41                  return !Files.isHidden(path);
42              }
43              catch (IOException e)
44              {
45                  StartLog.debug(e);
46                  return false;
47              }
48          }
49      }
50  
51      private static final char GLOB_CHARS[] = "*?".toCharArray();
52      private static final char SYNTAXED_GLOB_CHARS[] = "{}[]|:".toCharArray();
53      private static final Path EMPTY_PATH = new File(".").toPath();
54  
55      /**
56       * Convert a pattern to a Path object.
57       * 
58       * @param pattern
59       *            the raw pattern (can contain "glob:" or "regex:" syntax indicator)
60       * @return the Path version of the pattern provided.
61       */
62      private static Path asPath(final String pattern)
63      {
64          String test = pattern;
65          if (test.startsWith("glob:"))
66          {
67              test = test.substring("glob:".length());
68          }
69          else if (test.startsWith("regex:"))
70          {
71              test = test.substring("regex:".length());
72          }
73          return new File(test).toPath();
74      }
75  
76      public static PathMatcher getMatcher(String pattern)
77      {
78          FileSystem fs = FileSystems.getDefault();
79  
80          // If using FileSystem.getPathMatcher() with "glob:" or "regex:"
81          // use FileSystem default pattern behavior
82          if (pattern.startsWith("glob:") || pattern.startsWith("regex:"))
83          {
84              StartLog.debug("Using Standard " + fs.getClass().getName() + " pattern: " + pattern);
85              return fs.getPathMatcher(pattern);
86          }
87  
88          // If the pattern starts with a root path then its assumed to
89          // be a full system path
90          for (Path root : fs.getRootDirectories())
91          {
92              StartLog.debug("root: " + root);
93              if (pattern.startsWith(root.toString()))
94              {
95                  String pat = "glob:" + pattern;
96                  StartLog.debug("Using absolute path pattern: " + pat);
97                  return fs.getPathMatcher(pat);
98              }
99          }
100 
101         // Doesn't start with filesystem root, then assume the pattern
102         // is a relative file path pattern.
103         String pat = "glob:**/" + pattern;
104         StartLog.debug("Using relative path pattern: " + pat);
105         return fs.getPathMatcher(pat);
106     }
107 
108     public static PathMatcher getNonHidden()
109     {
110         return new NonHiddenMatcher();
111     }
112 
113     /**
114      * Provide the non-glob / non-regex prefix on the pattern as a Path reference.
115      * 
116      * @param pattern
117      *            the pattern to test
118      * @return the Path representing the search root for the pattern provided.
119      */
120     public static Path getSearchRoot(final String pattern)
121     {
122         StringBuilder root = new StringBuilder();
123 
124         int start = 0;
125         boolean syntaxed = false;
126         if (pattern.startsWith("glob:"))
127         {
128             start = "glob:".length();
129             syntaxed = true;
130         }
131         else if (pattern.startsWith("regex:"))
132         {
133             start = "regex:".length();
134             syntaxed = true;
135         }
136         int len = pattern.length();
137         int lastSep = 0;
138         for (int i = start; i < len; i++)
139         {
140             int cp = pattern.codePointAt(i);
141             if (cp < 127)
142             {
143                 char c = (char)cp;
144 
145                 // unix path case
146                 if (c == '/')
147                 {
148                     root.append(c);
149                     lastSep = root.length();
150                 }
151                 else if (c == '\\')
152                 {
153                     root.append("\\");
154                     lastSep = root.length();
155 
156                     // possible escaped sequence.
157                     // only really interested in windows escape sequences "\\"
158                     int count = countChars(pattern,i+1,'\\');
159                     if (count > 0)
160                     {
161                         // skip extra slashes
162                         i += count;
163                     }
164                 }
165                 else
166                 {
167                     if (isGlob(c,syntaxed))
168                     {
169                         break;
170                     }
171                     root.append(c);
172                 }
173             }
174             else
175             {
176                 root.appendCodePoint(cp);
177             }
178         }
179 
180         String rootPath = root.substring(0,lastSep);
181         if (rootPath.length() <= 0)
182         {
183             return EMPTY_PATH;
184         }
185 
186         return asPath(rootPath);
187     }
188 
189     private static int countChars(String pattern, int offset, char c)
190     {
191         int count = 0;
192         int len = pattern.length();
193         for (int i = offset; i < len; i++)
194         {
195             if (pattern.charAt(i) == c)
196             {
197                 count++;
198             }
199             else
200             {
201                 break;
202             }
203         }
204         return count;
205     }
206 
207     /**
208      * Tests if provided pattern is an absolute reference (or not)
209      * 
210      * @param pattern
211      *            the pattern to test
212      * @return true if pattern is an absolute reference.
213      */
214     public static boolean isAbsolute(final String pattern)
215     {
216         Path searchRoot = getSearchRoot(pattern);
217         if (searchRoot == EMPTY_PATH)
218         {
219             return false;
220         }
221         return searchRoot.isAbsolute();
222     }
223 
224     /**
225      * Determine if part is a glob pattern.
226      * 
227      * @param part
228      *            the string to check
229      * @param syntaxed
230      *            true if overall pattern is syntaxed with <code>"glob:"</code> or <code>"regex:"</code>
231      * @return true if part has glob characters
232      */
233     private static boolean isGlob(char c, boolean syntaxed)
234     {
235         for (char g : GLOB_CHARS)
236         {
237             if (c == g)
238             {
239                 return true;
240             }
241         }
242         if (syntaxed)
243         {
244             for (char g : SYNTAXED_GLOB_CHARS)
245             {
246                 if (c == g)
247                 {
248                     return true;
249                 }
250             }
251         }
252         return false;
253     }
254 }