View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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(final String rawpattern)
77      {
78          FileSystem fs = FileSystems.getDefault();
79          
80          String pattern = rawpattern;
81          
82          // Strip trailing slash (if present)
83          int lastchar = pattern.charAt(pattern.length() - 1);
84          if (lastchar == '/' || lastchar == '\\')
85          {
86              pattern = pattern.substring(0,pattern.length() - 1);
87          }
88  
89          // If using FileSystem.getPathMatcher() with "glob:" or "regex:"
90          // use FileSystem default pattern behavior
91          if (pattern.startsWith("glob:") || pattern.startsWith("regex:"))
92          {
93              StartLog.debug("Using Standard " + fs.getClass().getName() + " pattern: " + pattern);
94              return fs.getPathMatcher(pattern);
95          }
96  
97          // If the pattern starts with a root path then its assumed to
98          // be a full system path
99          if (isAbsolute(pattern))
100         {
101             String pat = "glob:" + pattern;
102             StartLog.debug("Using absolute path pattern: " + pat);
103             return fs.getPathMatcher(pat);
104         }
105 
106         // Doesn't start with filesystem root, then assume the pattern
107         // is a relative file path pattern.
108         String pat = "glob:**/" + pattern;
109         StartLog.debug("Using relative path pattern: " + pat);
110         return fs.getPathMatcher(pat);
111     }
112 
113     public static PathMatcher getNonHidden()
114     {
115         return new NonHiddenMatcher();
116     }
117 
118     /**
119      * Provide the non-glob / non-regex prefix on the pattern as a Path reference.
120      * 
121      * @param pattern
122      *            the pattern to test
123      * @return the Path representing the search root for the pattern provided.
124      */
125     public static Path getSearchRoot(final String pattern)
126     {
127         StringBuilder root = new StringBuilder();
128 
129         int start = 0;
130         boolean syntaxed = false;
131         if (pattern.startsWith("glob:"))
132         {
133             start = "glob:".length();
134             syntaxed = true;
135         }
136         else if (pattern.startsWith("regex:"))
137         {
138             start = "regex:".length();
139             syntaxed = true;
140         }
141         int len = pattern.length();
142         int lastSep = 0;
143         for (int i = start; i < len; i++)
144         {
145             int cp = pattern.codePointAt(i);
146             if (cp < 127)
147             {
148                 char c = (char)cp;
149 
150                 // unix path case
151                 if (c == '/')
152                 {
153                     root.append(c);
154                     lastSep = root.length();
155                 }
156                 else if (c == '\\')
157                 {
158                     root.append("\\");
159                     lastSep = root.length();
160 
161                     // possible escaped sequence.
162                     // only really interested in windows escape sequences "\\"
163                     int count = countChars(pattern,i+1,'\\');
164                     if (count > 0)
165                     {
166                         // skip extra slashes
167                         i += count;
168                     }
169                 }
170                 else
171                 {
172                     if (isGlob(c,syntaxed))
173                     {
174                         break;
175                     }
176                     root.append(c);
177                 }
178             }
179             else
180             {
181                 root.appendCodePoint(cp);
182             }
183         }
184 
185         String rootPath = root.substring(0,lastSep);
186         if (rootPath.length() <= 0)
187         {
188             return EMPTY_PATH;
189         }
190 
191         return asPath(rootPath);
192     }
193 
194     private static int countChars(String pattern, int offset, char c)
195     {
196         int count = 0;
197         int len = pattern.length();
198         for (int i = offset; i < len; i++)
199         {
200             if (pattern.charAt(i) == c)
201             {
202                 count++;
203             }
204             else
205             {
206                 break;
207             }
208         }
209         return count;
210     }
211 
212     /**
213      * Tests if provided pattern is an absolute reference (or not)
214      * 
215      * @param pattern
216      *            the pattern to test
217      * @return true if pattern is an absolute reference.
218      */
219     public static boolean isAbsolute(final String pattern)
220     {
221         Path searchRoot = getSearchRoot(pattern);
222         if (searchRoot == EMPTY_PATH)
223         {
224             return false;
225         }
226         return searchRoot.isAbsolute();
227     }
228 
229     /**
230      * Determine if part is a glob pattern.
231      * 
232      * @param part
233      *            the string to check
234      * @param syntaxed
235      *            true if overall pattern is syntaxed with <code>"glob:"</code> or <code>"regex:"</code>
236      * @return true if part has glob characters
237      */
238     private static boolean isGlob(char c, boolean syntaxed)
239     {
240         for (char g : GLOB_CHARS)
241         {
242             if (c == g)
243             {
244                 return true;
245             }
246         }
247         if (syntaxed)
248         {
249             for (char g : SYNTAXED_GLOB_CHARS)
250             {
251                 if (c == g)
252                 {
253                     return true;
254                 }
255             }
256         }
257         return false;
258     }
259 }