1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.aop.Matcher;
    18  import org.as2lib.core.BasicClass;
    19  import org.as2lib.util.StringUtil;
    20  
    21  /**
    22   * {@code WildcardMatcher} matches a join point with a pattern that may contain
    23   * wildcards.
    24   * 
    25   * TODO: Add a list of supported wildcards and how they can be used.
    26   * 
    27   * @author Simon Wacker
    28   */
    29  class org.as2lib.aop.matcher.WildcardMatcher extends BasicClass implements Matcher {
    30  	
    31  	/**
    32  	 * Constructs a new {@code WildcardMatcher} instance.
    33  	 */
    34  	public function WildcardMatcher(Void) {
    35  	}
    36  	
    37  	/**
    38  	 * Checks if the passed {@code joinPoint} represented by a string matches the
    39  	 * given {@code pattern}. 
    40  	 *
    41  	 * <p>Supported wildcards are '*' and '..'.
    42  	 *
    43  	 * <p>{@code false} will be returned if:
    44  	 * <ul>
    45  	 *   <li>
    46  	 *     The passed-in {@code joinPoint} is {@code null}, {@code undefined} or an
    47  	 *     empty string.
    48  	 *   </li>
    49  	 *   <li>The given {@code pattern} does not match the given {@code joinPoint}.</li>
    50  	 * </ul>
    51  	 *
    52  	 * <p>A {@code pattern} of value {@code null}, {@code undefined} or empty string
    53  	 * matches every join point.
    54  	 *
    55  	 * @param joinPoint the string representation of the join point to match with the
    56  	 * given {@code pattern}
    57  	 * @param pattern the pattern to match with the {@code joinPoint}
    58  	 * @return {@code true} if the {@code joinPoint} matches the {@code pattern} else
    59  	 * {@code false}
    60  	 */
    61  	public function match(joinPoint:String, pattern:String):Boolean {
    62  		if (!joinPoint) return false;
    63  		if (!pattern) return true;
    64  		if (pattern == "* ..*.*") return true;
    65  		if (pattern == "..*.*") {
    66  			return (joinPoint.indexOf("static ") == -1);
    67  		}
    68  		if (pattern.indexOf("*") < 0
    69  				&& pattern.indexOf("..") < -1) {
    70  			return (joinPoint == pattern);
    71  		}
    72  		if (pattern.indexOf("* ") == 0 && joinPoint.indexOf("static ") == -1) {
    73  			pattern = pattern.substring(2, pattern.length);
    74  		}
    75  		while (pattern.indexOf("**") > -1) {
    76  			pattern = StringUtil.replace(pattern, "**", "*");
    77  		}
    78  		while (pattern.indexOf("...") > -1) {
    79  			pattern = StringUtil.replace(pattern, "...", "..");
    80  		}
    81  		return wildcardMatch(joinPoint, pattern);
    82  	}
    83  	
    84  	/**
    85  	 * TODO: Documentation
    86  	 */
    87  	private function wildcardMatch(jp:String, p:String):Boolean {
    88  		var a:Array = jp.split(".");
    89  		var b:Array = p.split(".");
    90  		var x:Number = b.length;
    91  		var y:Number = a.length;
    92  		while (b[--x] == "*" && a[--y] != null) {
    93  			b.pop();
    94  			a.pop();
    95  		}
    96  		var d:Number = a.length;
    97  		var e:Number = b.length;
    98  		if (p.indexOf("..") < 0 && d != e) return false;
    99  		if (b[0] == "") b.shift();
   100  		if (b[b.length - 1] == "" && b[b.length - 2] == "") b.pop();
   101  		for (var i:Number = 0; i < d; i++) {
   102  			var f:String = b[i];
   103  			if (f == "") {
   104  				f = b[i + 1];
   105  				if (f == null) return true;
   106  				var g:Boolean = false;
   107  				for (var k:Number = i; k < d; k++) {
   108  					if (matchString(a[k], f)) {
   109  						if (k == i) b.shift();
   110  						g = true;
   111  						i = k;
   112  						break;
   113  					}
   114  					if (k > i) b.unshift("");
   115  				}
   116  				if (!g) return false;
   117  			} else {
   118  				if (!matchString(a[i], f)) {
   119  					if (b[i - 1] != "*" || b[i - 2] != "") {
   120  						return false;
   121  					} else {
   122  						b.unshift("");
   123  					}
   124  				}
   125  			}
   126  		}
   127  		if (a.length == b.length - 1 && b[b.length - 1] == "") return true;
   128  		if (a.length != b.length) return false;
   129  		return true;
   130  	}
   131  	
   132  	/**
   133  	 * TODO: Documentation
   134  	 */
   135  	private static function matchString(s:String, p:String):Boolean {
   136  		if (p == "*") return true;
   137  		if (p.indexOf("*") > -1) {
   138  			var a:Array = p.split("*");
   139  			var b:Number = a.length;
   140  			var z:Number = -1;
   141  			for (var i:Number = 0; i < b; i++) {
   142  				var c:String = a[i];
   143  				if (c == "") continue;
   144  				var d:Number = s.indexOf(c);
   145  				if (d < 0) return false;
   146  				if (d < z) return false;
   147  				if (d > 0 && i == 0) {
   148  					return false;
   149  				}
   150  				if (i == b - 1 && d + c.length < s.length) {
   151  					return false;
   152  				}
   153  				z = d;
   154  			}
   155  			return true;
   156  		} else {
   157  			return (s == p);
   158  		}
   159  	}
   160  	
   161  }