Coverage Report - org.apache.myfaces.config.util.GAEUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
GAEUtils
0%
0/97
0%
0/84
4.611
GAEUtils$IOCase
0%
0/31
0%
0/28
4.611
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.config.util;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.io.Serializable;
 23  
 import java.net.URL;
 24  
 import java.util.ArrayList;
 25  
 import java.util.Collection;
 26  
 import java.util.Enumeration;
 27  
 import java.util.Set;
 28  
 import java.util.Stack;
 29  
 import java.util.TreeSet;
 30  
 import java.util.jar.JarEntry;
 31  
 import java.util.jar.JarFile;
 32  
 import javax.faces.context.ExternalContext;
 33  
 import org.apache.myfaces.shared.util.StringUtils;
 34  
 
 35  
 /**
 36  
  * Utility methods to use in Google Application Engine (GAE)
 37  
  * 
 38  
  * @author Leonardo Uribe
 39  
  */
 40  0
 public class GAEUtils
 41  
 {
 42  
 
 43  
     public static final String WEB_LIB_PREFIX = "/WEB-INF/lib/";
 44  
     
 45  
 
 46  
     /**
 47  
      * Look in all jars located inside /WEB-INF/lib/ folder for files that has
 48  
      * some specified prefix and suffix. It is a simplification that can be done
 49  
      * in GAE, because no JSF libraries are outside /WEB-INF/lib
 50  
      *
 51  
      * @param context
 52  
      * @param classloader
 53  
      * @param prefix
 54  
      * @param suffix
 55  
      * @return
 56  
      * @throws IOException
 57  
      */
 58  
     public static Collection<URL> searchInWebLib(
 59  
             ExternalContext context, ClassLoader classloader, String filter, 
 60  
             String prefix, String suffix) throws IOException
 61  
     {
 62  0
         if (!filter.equals("none"))
 63  
         {
 64  0
             String[] jarFilesToScan = StringUtils.trim(StringUtils.splitLongString(filter, ','));
 65  0
             Set<URL> urlSet = null;
 66  0
             Set<String> paths = context.getResourcePaths(WEB_LIB_PREFIX);
 67  0
             if (paths != null)
 68  
             {
 69  0
                 for (Object pathObject : paths)
 70  
                 {
 71  0
                     String path = (String) pathObject;
 72  0
                     if (path.endsWith(".jar") && wildcardMatch(path, jarFilesToScan, WEB_LIB_PREFIX))
 73  
                     {
 74  
                         // GAE does not use WAR format, so the app is just uncompressed in a directory
 75  
                         // What we need here is just take the path of the file, and open the file as a
 76  
                         // jar file. Then, if the jar should be scanned, try to find the required file.
 77  0
                         URL jarUrl = new URL("jar:" + context.getResource(path).toExternalForm() + "!/");
 78  0
                         JarFile jarFile = JarUtils.getJarFile(jarUrl);
 79  
 
 80  0
                         Enumeration<JarEntry> entries = jarFile.entries();
 81  0
                         while (entries.hasMoreElements())
 82  
                         {
 83  0
                             JarEntry entry = entries.nextElement();
 84  0
                             if (entry.isDirectory())
 85  
                             {
 86  0
                                 continue; // This is a directory
 87  
                             }
 88  0
                             String name = entry.getName();
 89  0
                             if (!name.startsWith(prefix))
 90  
                             {
 91  0
                                 continue; // Attribute files
 92  
                             }
 93  0
                             if (name.endsWith(suffix))
 94  
                             {
 95  
                                 // Get it from classloader, because no URL can be
 96  
                                 // derived from JarEntry
 97  0
                                 Enumeration<URL> alternateFacesConfigs = classloader.getResources(name);
 98  0
                                 while (alternateFacesConfigs.hasMoreElements())
 99  
                                 {
 100  0
                                     if (urlSet == null)
 101  
                                     {
 102  0
                                         urlSet = new TreeSet<URL>();
 103  
                                     }
 104  0
                                     urlSet.add(alternateFacesConfigs.nextElement());
 105  
                                 }
 106  
                             }
 107  0
                         }
 108  
                     }
 109  0
                 }
 110  
             }
 111  0
             return urlSet;
 112  
         }
 113  0
         return null;
 114  
     }
 115  
 
 116  
     public static boolean wildcardMatch(String filename, String[] wildcardMatchers, String prefix)
 117  
     {
 118  0
         for (String matcher : wildcardMatchers)
 119  
         {
 120  0
             if (wildcardMatch(filename, prefix + matcher))
 121  
             {
 122  0
                 return true;
 123  
             }
 124  
         }
 125  0
         return false;
 126  
     }
 127  
     
 128  
     // NOTE: CODE TAKEN FROM COMMONS-IO AND REFACTORED TO USE INSIDE GAE
 129  
     //-----------------------------------------------------------------------
 130  
     /**
 131  
      * Checks a filename to see if it matches the specified wildcard matcher,
 132  
      * always testing case-sensitive. <p> The wildcard matcher uses the
 133  
      * characters '?' and '*' to represent a single or multiple (zero or more)
 134  
      * wildcard characters. This is the same as often found on Dos/Unix command
 135  
      * lines. The check is case-sensitive always.
 136  
      * <pre>
 137  
      * wildcardMatch("c.txt", "*.txt")      --> true
 138  
      * wildcardMatch("c.txt", "*.jpg")      --> false
 139  
      * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
 140  
      * wildcardMatch("c.txt", "*.???")      --> true
 141  
      * wildcardMatch("c.txt", "*.????")     --> false
 142  
      * </pre> N.B. the sequence "*?" does not work properly at present in match
 143  
      * strings.
 144  
      *
 145  
      * @param filename the filename to match on
 146  
      * @param wildcardMatcher the wildcard string to match against
 147  
      * @return true if the filename matches the wilcard string
 148  
      * @see IOCase#SENSITIVE
 149  
      */
 150  
     static boolean wildcardMatch(String filename, String wildcardMatcher)
 151  
     {
 152  0
         return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
 153  
     }
 154  
 
 155  
     /**
 156  
      * Checks a filename to see if it matches the specified wildcard matcher
 157  
      * using the case rules of the system. <p> The wildcard matcher uses the
 158  
      * characters '?' and '*' to represent a single or multiple (zero or more)
 159  
      * wildcard characters. This is the same as often found on Dos/Unix command
 160  
      * lines. The check is case-sensitive on Unix and case-insensitive on
 161  
      * Windows.
 162  
      * <pre>
 163  
      * wildcardMatch("c.txt", "*.txt")      --> true
 164  
      * wildcardMatch("c.txt", "*.jpg")      --> false
 165  
      * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
 166  
      * wildcardMatch("c.txt", "*.???")      --> true
 167  
      * wildcardMatch("c.txt", "*.????")     --> false
 168  
      * </pre> N.B. the sequence "*?" does not work properly at present in match
 169  
      * strings.
 170  
      *
 171  
      * @param filename the filename to match on
 172  
      * @param wildcardMatcher the wildcard string to match against
 173  
      * @return true if the filename matches the wilcard string
 174  
      * @see IOCase#SYSTEM
 175  
      */
 176  
     static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher)
 177  
     {
 178  
         //return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM);
 179  0
         return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
 180  
     }
 181  
 
 182  
     /**
 183  
      * Checks a filename to see if it matches the specified wildcard matcher
 184  
      * allowing control over case-sensitivity. <p> The wildcard matcher uses the
 185  
      * characters '?' and '*' to represent a single or multiple (zero or more)
 186  
      * wildcard characters. N.B. the sequence "*?" does not work properly at
 187  
      * present in match strings.
 188  
      *
 189  
      * @param filename the filename to match on
 190  
      * @param wildcardMatcher the wildcard string to match against
 191  
      * @param caseSensitivity what case sensitivity rule to use, null means
 192  
      * case-sensitive
 193  
      * @return true if the filename matches the wilcard string
 194  
      * @since 1.3
 195  
      */
 196  
     static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity)
 197  
     {
 198  0
         if (filename == null && wildcardMatcher == null)
 199  
         {
 200  0
             return true;
 201  
         }
 202  0
         if (filename == null || wildcardMatcher == null)
 203  
         {
 204  0
             return false;
 205  
         }
 206  0
         if (caseSensitivity == null)
 207  
         {
 208  0
             caseSensitivity = IOCase.SENSITIVE;
 209  
         }
 210  0
         String[] wcs = splitOnTokens(wildcardMatcher);
 211  0
         boolean anyChars = false;
 212  0
         int textIdx = 0;
 213  0
         int wcsIdx = 0;
 214  0
         Stack<int[]> backtrack = new Stack<int[]>();
 215  
 
 216  
         // loop around a backtrack stack, to handle complex * matching
 217  
         do
 218  
         {
 219  0
             if (backtrack.size() > 0)
 220  
             {
 221  0
                 int[] array = backtrack.pop();
 222  0
                 wcsIdx = array[0];
 223  0
                 textIdx = array[1];
 224  0
                 anyChars = true;
 225  
             }
 226  
 
 227  
             // loop whilst tokens and text left to process
 228  0
             while (wcsIdx < wcs.length)
 229  
             {
 230  
 
 231  0
                 if (wcs[wcsIdx].equals("?"))
 232  
                 {
 233  
                     // ? so move to next text char
 234  0
                     textIdx++;
 235  0
                     if (textIdx > filename.length())
 236  
                     {
 237  0
                         break;
 238  
                     }
 239  0
                     anyChars = false;
 240  
 
 241  
                 }
 242  0
                 else if (wcs[wcsIdx].equals("*"))
 243  
                 {
 244  
                     // set any chars status
 245  0
                     anyChars = true;
 246  0
                     if (wcsIdx == wcs.length - 1)
 247  
                     {
 248  0
                         textIdx = filename.length();
 249  
                     }
 250  
 
 251  
                 }
 252  
                 else
 253  
                 {
 254  
                     // matching text token
 255  0
                     if (anyChars)
 256  
                     {
 257  
                         // any chars then try to locate text token
 258  0
                         textIdx = caseSensitivity.checkIndexOf(filename, textIdx, wcs[wcsIdx]);
 259  0
                         if (textIdx == -1)
 260  
                         {
 261  
                             // token not found
 262  0
                             break;
 263  
                         }
 264  0
                         int repeat = caseSensitivity.checkIndexOf(filename, textIdx + 1, wcs[wcsIdx]);
 265  0
                         if (repeat >= 0)
 266  
                         {
 267  0
                             backtrack.push(new int[]
 268  
                                     {
 269  
                                         wcsIdx, repeat
 270  
                                     });
 271  
                         }
 272  0
                     }
 273  
                     else
 274  
                     {
 275  
                         // matching from current position
 276  0
                         if (!caseSensitivity.checkRegionMatches(filename, textIdx, wcs[wcsIdx]))
 277  
                         {
 278  
                             // couldnt match token
 279  0
                             break;
 280  
                         }
 281  
                     }
 282  
 
 283  
                     // matched text token, move text index to end of matched token
 284  0
                     textIdx += wcs[wcsIdx].length();
 285  0
                     anyChars = false;
 286  
                 }
 287  
 
 288  0
                 wcsIdx++;
 289  
             }
 290  
 
 291  
             // full match
 292  0
             if (wcsIdx == wcs.length && textIdx == filename.length())
 293  
             {
 294  0
                 return true;
 295  
             }
 296  
 
 297  0
         } while (backtrack.size() > 0);
 298  
 
 299  0
         return false;
 300  
     }
 301  
 
 302  
     /**
 303  
      * Splits a string into a number of tokens. The text is split by '?' and
 304  
      * '*'. Where multiple '*' occur consecutively they are collapsed into a
 305  
      * single '*'.
 306  
      *
 307  
      * @param text the text to split
 308  
      * @return the array of tokens, never null
 309  
      */
 310  
     static String[] splitOnTokens(String text)
 311  
     {
 312  
         // used by wildcardMatch
 313  
         // package level so a unit test may run on this
 314  
 
 315  0
         if (text.indexOf('?') == -1 && text.indexOf('*') == -1)
 316  
         {
 317  0
             return new String[]
 318  
                     {
 319  
                         text
 320  
                     };
 321  
         }
 322  
 
 323  0
         char[] array = text.toCharArray();
 324  0
         ArrayList<String> list = new ArrayList<String>();
 325  0
         StringBuilder buffer = new StringBuilder();
 326  0
         for (int i = 0; i < array.length; i++)
 327  
         {
 328  0
             if (array[i] == '?' || array[i] == '*')
 329  
             {
 330  0
                 if (buffer.length() != 0)
 331  
                 {
 332  0
                     list.add(buffer.toString());
 333  0
                     buffer.setLength(0);
 334  
                 }
 335  0
                 if (array[i] == '?')
 336  
                 {
 337  0
                     list.add("?");
 338  
                 }
 339  0
                 else if (list.isEmpty()
 340  
                         || i > 0 && list.get(list.size() - 1).equals("*") == false)
 341  
                 {
 342  0
                     list.add("*");
 343  
                 }
 344  
             }
 345  
             else
 346  
             {
 347  0
                 buffer.append(array[i]);
 348  
             }
 349  
         }
 350  0
         if (buffer.length() != 0)
 351  
         {
 352  0
             list.add(buffer.toString());
 353  
         }
 354  
 
 355  0
         return list.toArray(new String[list.size()]);
 356  
     }
 357  
 
 358  0
     final static class IOCase implements Serializable
 359  
     {
 360  
 
 361  
         /**
 362  
          * The constant for case sensitive regardless of operating system.
 363  
          */
 364  0
         public static final IOCase SENSITIVE = new IOCase("Sensitive", true);
 365  
         /**
 366  
          * The constant for case insensitive regardless of operating system.
 367  
          */
 368  0
         public static final IOCase INSENSITIVE = new IOCase("Insensitive", false);
 369  
         /**
 370  
          * The constant for case sensitivity determined by the current operating
 371  
          * system. Windows is case-insensitive when comparing filenames, Unix is
 372  
          * case-sensitive. <p> <strong>Note:</strong> This only caters for
 373  
          * Windows and Unix. Other operating systems (e.g. OSX and OpenVMS) are
 374  
          * treated as case sensitive if they use the Unix file separator and
 375  
          * case-insensitive if they use the Windows file separator (see {@link java.io.File#separatorChar}).
 376  
          * <p> If you derialize this constant of Windows, and deserialize on
 377  
          * Unix, or vice versa, then the value of the case-sensitivity flag will
 378  
          * change.
 379  
          */
 380  
         //public static final IOCase SYSTEM = new IOCase("System", !FilenameUtils.isSystemWindows());
 381  
         /**
 382  
          * Serialization version.
 383  
          */
 384  
         private static final long serialVersionUID = -6343169151696340687L;
 385  
         /**
 386  
          * The enumeration name.
 387  
          */
 388  
         private final String name;
 389  
         /**
 390  
          * The sensitivity flag.
 391  
          */
 392  
         private final transient boolean sensitive;
 393  
 
 394  
         //-----------------------------------------------------------------------
 395  
         /**
 396  
          * Factory method to create an IOCase from a name.
 397  
          *
 398  
          * @param name the name to find
 399  
          * @return the IOCase object
 400  
          * @throws IllegalArgumentException if the name is invalid
 401  
          */
 402  
         public static IOCase forName(String name)
 403  
         {
 404  0
             if (IOCase.SENSITIVE.name.equals(name))
 405  
             {
 406  0
                 return IOCase.SENSITIVE;
 407  
             }
 408  0
             if (IOCase.INSENSITIVE.name.equals(name))
 409  
             {
 410  0
                 return IOCase.INSENSITIVE;
 411  
             }
 412  
             //if (IOCase.SYSTEM.name.equals(name)){
 413  
             //    return IOCase.SYSTEM;
 414  
             //}
 415  0
             throw new IllegalArgumentException("Invalid IOCase name: " + name);
 416  
         }
 417  
 
 418  
         //-----------------------------------------------------------------------
 419  
         /**
 420  
          * Private constructor.
 421  
          *
 422  
          * @param name the name
 423  
          * @param sensitive the sensitivity
 424  
          */
 425  
         private IOCase(String name, boolean sensitive)
 426  0
         {
 427  0
             this.name = name;
 428  0
             this.sensitive = sensitive;
 429  0
         }
 430  
 
 431  
         /**
 432  
          * Replaces the enumeration from the stream with a real one. This
 433  
          * ensures that the correct flag is set for SYSTEM.
 434  
          *
 435  
          * @return the resolved object
 436  
          */
 437  
         private Object readResolve()
 438  
         {
 439  0
             return forName(name);
 440  
         }
 441  
 
 442  
         //-----------------------------------------------------------------------
 443  
         /**
 444  
          * Gets the name of the constant.
 445  
          *
 446  
          * @return the name of the constant
 447  
          */
 448  
         public String getName()
 449  
         {
 450  0
             return name;
 451  
         }
 452  
 
 453  
         /**
 454  
          * Does the object represent case sensitive comparison.
 455  
          *
 456  
          * @return true if case sensitive
 457  
          */
 458  
         public boolean isCaseSensitive()
 459  
         {
 460  0
             return sensitive;
 461  
         }
 462  
 
 463  
         //-----------------------------------------------------------------------
 464  
         /**
 465  
          * Compares two strings using the case-sensitivity rule. <p> This method
 466  
          * mimics {@link String#compareTo} but takes case-sensitivity into
 467  
          * account.
 468  
          *
 469  
          * @param str1 the first string to compare, not null
 470  
          * @param str2 the second string to compare, not null
 471  
          * @return true if equal using the case rules
 472  
          * @throws NullPointerException if either string is null
 473  
          */
 474  
         public int checkCompareTo(String str1, String str2)
 475  
         {
 476  0
             if (str1 == null || str2 == null)
 477  
             {
 478  0
                 throw new NullPointerException("The strings must not be null");
 479  
             }
 480  0
             return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);
 481  
         }
 482  
 
 483  
         /**
 484  
          * Compares two strings using the case-sensitivity rule. <p> This method
 485  
          * mimics {@link String#equals} but takes case-sensitivity into account.
 486  
          *
 487  
          * @param str1 the first string to compare, not null
 488  
          * @param str2 the second string to compare, not null
 489  
          * @return true if equal using the case rules
 490  
          * @throws NullPointerException if either string is null
 491  
          */
 492  
         public boolean checkEquals(String str1, String str2)
 493  
         {
 494  0
             if (str1 == null || str2 == null)
 495  
             {
 496  0
                 throw new NullPointerException("The strings must not be null");
 497  
             }
 498  0
             return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
 499  
         }
 500  
 
 501  
         /**
 502  
          * Checks if one string starts with another using the case-sensitivity
 503  
          * rule. <p> This method mimics {@link String#startsWith(String)} but
 504  
          * takes case-sensitivity into account.
 505  
          *
 506  
          * @param str the string to check, not null
 507  
          * @param start the start to compare against, not null
 508  
          * @return true if equal using the case rules
 509  
          * @throws NullPointerException if either string is null
 510  
          */
 511  
         public boolean checkStartsWith(String str, String start)
 512  
         {
 513  0
             return str.regionMatches(!sensitive, 0, start, 0, start.length());
 514  
         }
 515  
 
 516  
         /**
 517  
          * Checks if one string ends with another using the case-sensitivity
 518  
          * rule. <p> This method mimics {@link String#endsWith} but takes
 519  
          * case-sensitivity into account.
 520  
          *
 521  
          * @param str the string to check, not null
 522  
          * @param end the end to compare against, not null
 523  
          * @return true if equal using the case rules
 524  
          * @throws NullPointerException if either string is null
 525  
          */
 526  
         public boolean checkEndsWith(String str, String end)
 527  
         {
 528  0
             int endLen = end.length();
 529  0
             return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
 530  
         }
 531  
 
 532  
         /**
 533  
          * Checks if one string contains another starting at a specific index
 534  
          * using the case-sensitivity rule. <p> This method mimics parts of {@link String#indexOf(String, int)}
 535  
          * but takes case-sensitivity into account.
 536  
          *
 537  
          * @param str the string to check, not null
 538  
          * @param strStartIndex the index to start at in str
 539  
          * @param search the start to search for, not null
 540  
          * @return the first index of the search String, -1 if no match or {@code null}
 541  
          * string input
 542  
          * @throws NullPointerException if either string is null
 543  
          * @since 2.0
 544  
          */
 545  
         public int checkIndexOf(String str, int strStartIndex, String search)
 546  
         {
 547  0
             int endIndex = str.length() - search.length();
 548  0
             if (endIndex >= strStartIndex)
 549  
             {
 550  0
                 for (int i = strStartIndex; i <= endIndex; i++)
 551  
                 {
 552  0
                     if (checkRegionMatches(str, i, search))
 553  
                     {
 554  0
                         return i;
 555  
                     }
 556  
                 }
 557  
             }
 558  0
             return -1;
 559  
         }
 560  
 
 561  
         /**
 562  
          * Checks if one string contains another at a specific index using the
 563  
          * case-sensitivity rule. <p> This method mimics parts of {@link 
 564  
          * String#regionMatches(boolean, int, String, int, int)}
 565  
          * but takes case-sensitivity into account.
 566  
          *
 567  
          * @param str the string to check, not null
 568  
          * @param strStartIndex the index to start at in str
 569  
          * @param search the start to search for, not null
 570  
          * @return true if equal using the case rules
 571  
          * @throws NullPointerException if either string is null
 572  
          */
 573  
         public boolean checkRegionMatches(String str, int strStartIndex, String search)
 574  
         {
 575  0
             return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
 576  
         }
 577  
 
 578  
         //-----------------------------------------------------------------------
 579  
         /**
 580  
          * Gets a string describing the sensitivity.
 581  
          *
 582  
          * @return a string describing the sensitivity
 583  
          */
 584  
         @Override
 585  
         public String toString()
 586  
         {
 587  0
             return name;
 588  
         }
 589  
     }
 590  
 }