001    /*
002     * Created on Jan 12, 2009
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005     * in compliance with the License. You may obtain a copy of the License at
006     *
007     * http://www.apache.org/licenses/LICENSE-2.0
008     *
009     * Unless required by applicable law or agreed to in writing, software distributed under the License
010     * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011     * or implied. See the License for the specific language governing permissions and limitations under
012     * the License.
013     *
014     * Copyright @2009-2010 the original author or authors.
015     */
016    package org.fest.swing.core.matcher;
017    
018    import static org.fest.swing.util.Strings.areEqualOrMatch;
019    import static org.fest.swing.util.Strings.match;
020    import static org.fest.util.Objects.areEqual;
021    import static org.fest.util.Strings.quote;
022    
023    import java.awt.Component;
024    import java.util.regex.Pattern;
025    
026    import org.fest.swing.core.GenericTypeMatcher;
027    
028    /**
029     * Understands a template for matching components by name. Subclasses are free to add other properties to use as search
030     * criteria.
031     * @param <T> the type of <code>Component</code> supported by this matcher.
032     *
033     * @author Alex Ruiz
034     */
035    public abstract class NamedComponentMatcherTemplate<T extends Component> extends GenericTypeMatcher<T> {
036    
037      /**
038       * Indicates that a property value to use as search criteria has not been set.
039       */
040      protected static final Object ANY = new Object() {
041        @Override public String toString() { return "<Any>"; }
042      };
043    
044      /** The component name to match. **/
045      protected final Object name;
046    
047      /**
048       * Creates a new </code>{@link NamedComponentMatcherTemplate}</code>.
049       * @param supportedType the type supported by this matcher.
050       * @throws NullPointerException if the given type is <code>null</code>.
051       */
052      protected NamedComponentMatcherTemplate(Class<T> supportedType) {
053        super(supportedType);
054        this.name = ANY;
055      }
056    
057      /**
058       * Creates a new </code>{@link NamedComponentMatcherTemplate}</code>.
059       * @param supportedType the type supported by this matcher.
060       * @param name the component name to match.
061       * @throws NullPointerException if the given type is <code>null</code>.
062       */
063      protected NamedComponentMatcherTemplate(Class<T> supportedType, Object name) {
064        super(supportedType);
065        this.name = name;
066      }
067    
068      /**
069       * Returns the component name to match surrounded by double quotes. If the component name has not been set, it will
070       * return <code>{@link #ANY}</code>. This method is commonly used in implementations of <code>toString</code>.
071       * @return the component name to match surrounded by double quotes, or <code>{@link #ANY}</code> if the component name
072       * has not been set.
073       */
074      protected final Object quotedName() {
075        return quoted(name);
076      }
077    
078      /**
079       * Returns the given property value to match surrounded by double quotes. If the property has not been set, it will
080       * return <code>{@link #ANY}</code>. This method is commonly used in implementations of <code>toString</code>.
081       * @param propertyValue the given property value.
082       * @return the given property value to match surrounded by double quotes, or <code>{@link #ANY}</code> if the property
083       * value has not been set.
084       */
085      protected final Object quoted(Object propertyValue) {
086        if (ANY.equals(propertyValue)) return ANY;
087        if (propertyValue instanceof Pattern) return quote(((Pattern)propertyValue).pattern());
088        return quote(propertyValue);
089      }
090    
091      /**
092       * Indicates whether the given value matches the name in this matcher. It always returns <code>true</code> if this
093       * matcher's name is <code>{@link #ANY}</code>.
094       * @param actual the actual component name.
095       * @return <code>true</code> if this matcher's name is <code>ANY</code> or if both the actual name is equal to the one
096       * in this matcher. Otherwise <code>false</code>.
097       */
098      protected final boolean isNameMatching(String actual) {
099        if (ANY.equals(name)) return true;
100        return areEqual(name, actual);
101      }
102    
103      /**
104       * Indicates whether the given value matches the expected value in this matcher. Matching is performed as follows:
105       * <ol>
106       * <li>it always returns <code>true</code> if the expected value is <code>{@link #ANY}</code></li>
107       * <li>if both the expected and actual values are <code>String</code>s, it checks for equality first. If this fails,
108       * it tries to match the values assuming the expected value can be a regular expression</li>
109       * <li>if the expected value is a <code>{@link Pattern}</code> and the actual value is a
110       * <code>{@link CharSequence}</code>, regular expression matching is performed</li>
111       * <li>otherwise, it checks that both the expected and actual values are equal</li>
112       * </ol>
113       * @param expected the expected value in this matcher.
114       * @param actual the actual property value.
115       * @return <code>true</code> if the values match, otherwise <code>false</code>.
116       */
117      protected final boolean arePropertyValuesMatching(Object expected, Object actual) {
118        if (ANY.equals(expected)) return true;
119        if (expected instanceof String && actual instanceof String)
120          return areEqualOrMatch((String)expected, (String)actual);
121        if (expected instanceof Pattern && actual instanceof CharSequence)
122          return match((Pattern)expected, (CharSequence)actual);
123        return areEqual(expected, actual);
124      }
125    
126    }