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.core.BasicClass;
    18  import org.as2lib.util.StringUtil;
    19  import org.as2lib.data.holder.Map;
    20  import org.as2lib.data.holder.map.PrimitiveTypeMap;
    21  import org.as2lib.env.except.IllegalArgumentException;
    22  import org.as2lib.test.mock.ArgumentMatcher;
    23  
    24  /**
    25   * {@code StateArgumentMatcher} checks whether one argument is in an expected state.
    26   * 
    27   * @author Simon Wacker
    28   */
    29  class org.as2lib.test.mock.support.StateArgumentMatcher extends BasicClass implements ArgumentMatcher {
    30  	
    31  	/** The expected type. */
    32  	private var expectedType:Function;
    33  	
    34  	/** The expected properties. */
    35  	private var expectedProperties:Map;
    36  	
    37  	/**
    38  	 * Constructs a new {@code StateArgumentMatcher} instance.
    39  	 * 
    40  	 * <p>If every type is allowed, specifc {@code Object} as expected type.
    41  	 * 
    42  	 * @param expectedType the expected type of the argument
    43  	 * @throws IllegalArgumentException if {@code type} is {@code null} or
    44  	 * {@code undefined}
    45  	 */
    46  	public function StateArgumentMatcher(expectedType:Function) {
    47  		if (!expectedType) throw new IllegalArgumentException("Argument 'expectedType' [" + expectedType + "] must not be 'null' nor 'undefined'.", this, arguments);
    48  		this.expectedType = expectedType;
    49  		this.expectedProperties = new PrimitiveTypeMap();
    50  	}
    51  	
    52  	/**
    53  	 * Adds a new property the argument is expected to have.
    54  	 * 
    55  	 * @param propertyName the name of the expected property
    56  	 * @param propertyValue the value of the expected property
    57  	 * @see #checkProperty
    58  	 */
    59  	public function addExpectedProperty(propertyName:String, propertyValue):Void {
    60  		this.expectedProperties.put(propertyName, propertyValue);
    61  	}
    62  	
    63  	/**
    64  	 * Checks whether the passed-in {@code argument} is in the expected state.
    65  	 * 
    66  	 * <p>{@code false} will be returned if:
    67  	 * <ul>
    68  	 *   <li>{@code argument} is {@code null} or {@code undefined}.</li>
    69  	 *   <li>{@code argument} is not of the expected type.</li>
    70  	 *   <li>{@code argument} is not in the expected state.</li>
    71  	 * </ul>
    72  	 * 
    73  	 * @param argument the argument to check whether it is in the expected state
    74  	 * @return {@code true} if the argument is in the expected state else {@code false}
    75  	 */
    76  	public function matchArgument(argument):Boolean {
    77  		if (!argument) return false;
    78  		if (!(argument instanceof this.expectedType)) return false;
    79  		var keys:Array = this.expectedProperties.getKeys();
    80  		var values:Array = this.expectedProperties.getValues();
    81  		for (var i:Number = 0; i < keys.length; i++) {
    82  			if (!checkProperty(argument, keys[i], values[i])) {
    83  				return false;
    84  			}
    85  		}
    86  		return true;
    87  	}
    88  	
    89  	/**
    90  	 * Checks whether the value of the property with name {@code propertyName} on the
    91  	 * passed-in {@code target} matches the {@code expectedPropertyValue}.
    92  	 * 
    93  	 * <p>It is first checked whether a getter method with the passed-in
    94  	 * {@code propertyName} exists. If so it will be used to get the property value
    95  	 * that is then compared with the {@code expectedPropertyValue}. Otherwise the
    96  	 * {@code propertyName} will be used directly and its value will be compared with
    97  	 * the {@code expectedPropertyValue}.
    98  	 * 
    99  	 * @param target the target object that declares the property
   100  	 * @param propertyName the name of the property
   101  	 * @param expectedPropertyValue the expected value of the property
   102  	 * @return {@code true} if the property has the expected value else {@code false}
   103  	 */
   104  	private function checkProperty(target, propertyName:String, expectedPropertyValue):Boolean {
   105  		var getter:String = "get" + StringUtil.ucFirst(propertyName);
   106  		if (target[getter]) {
   107  			return (target[getter]() === expectedPropertyValue);
   108  		}
   109  		return (target[propertyName] === expectedPropertyValue);
   110  	}
   111  	
   112  }