001    /*
002     * $Id: ComparableTester.java,v 1.2 2011/09/25 19:41:52 oboehm Exp $
003     *
004     * Copyright (c) 2011 by Oliver Boehm
005     *
006     * Licensed under the Apache License, Version 2.0 (the "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     *
018     * (c)reated 21.09.2011 by oliver (ob@oasd.de)
019     */
020    
021    package patterntesting.runtime.junit;
022    
023    import java.util.*;
024    
025    import org.junit.Assert;
026    import org.slf4j.*;
027    
028    import patterntesting.runtime.monitor.ClasspathMonitor;
029    
030    /**
031     * This utility class checks classes which implements the {@link Comparable}
032     * interface. E.g. for two objects which are equals it is expected that the
033     * {@link Comparable#compareTo(Object)} method returns 0.
034     * 
035     * @author oliver (ob@aosd.de)
036     * @since 1.2 (21.09.2011)
037     */
038    public final class ComparableTester {
039        
040        private static final Logger log = LoggerFactory.getLogger(ComparableTester.class);
041        private static final ClasspathMonitor classpathMonitor = ClasspathMonitor.getInstance();
042    
043        /** Utility class - no need to instantiate it. */
044        private ComparableTester() {}
045        
046        /**
047         * The {@link Comparable#compareTo(Object)} method should return 0 if the
048         * given objects are eqals. If they are not equals the shouldn't return 0.
049         * This is checked here.
050         * 
051         * @param c1 the first Comparable
052         * @param c2 the second Comparable
053         * @throws AssertionError if the check fails
054         */
055        @SuppressWarnings({ "rawtypes", "unchecked" })
056        public static void assertCompareTo(final Comparable c1, final Comparable c2) {
057            int ret1 = c1.compareTo(c2);
058            int ret2 = c2.compareTo(c1);
059            if (c1.equals(c2)) {
060                String msg = c1.getClass() + ": compareTo(..) should return 0 for equals objects";
061                Assert.assertEquals(msg, 0, ret1);
062                Assert.assertEquals(msg, 0, ret2);
063            } else {
064                String msg = c1.getClass()
065                        + ": compareTo(..) should return not 0 for not equals objects " + c1 + " and "
066                        + c2;
067                Assert.assertTrue(msg, ret1 != 0);
068                Assert.assertTrue(msg, ret2 != 0);
069                msg = c1.getClass() + ": <" + c2 + ">.compareTo(<" + c1 + ">) should return "
070                        + (-ret2) + " (not " + ret2 + ")";
071                if (ret1 < 0) {
072                    Assert.assertTrue(msg, ret2 > 0);
073                } else {
074                    Assert.assertTrue(msg, ret2 < 0);
075                }
076            }
077            log.info("compareTo implementation of " + c1.getClass() + " seems to be ok");
078        }
079        
080        /**
081         * This method will create two objects of the given class using the
082         * default constructor and compares them. So two preconditions must be
083         * true:
084         * <ol>
085         *  <li>the class must not be abstract</li>
086         *  <li>there must be a (public) default constructor</li>
087         *  </li>
088         * </ol>
089         *
090         * @param clazz the clazz
091         * @throws AssertionError if the check fails
092         */
093        @SuppressWarnings("rawtypes")
094        public static void assertCompareTo(final Class<? extends Comparable> clazz)
095                throws AssertionError {
096            log.trace("checking {}.compareTo(..)...", clazz);
097            Comparable<?> comp = (Comparable<?>) ObjectTester.newInstanceOf(clazz);
098            Comparable<?> clone = (Comparable<?>) ObjectTester.clone(comp);
099            assertCompareTo(comp, clone);
100        }
101        
102        /**
103         * Check for each class in the given collection if the compareTo method
104         * works as expected
105         *
106         * @param classes a collection of classes to be checked
107         */
108        @SuppressWarnings("rawtypes")
109        public static void assertCompareTo(final Collection<Class<Comparable>> classes) {
110            for (Class<Comparable> clazz : classes) {
111                assertCompareTo(clazz);
112            }
113        }
114    
115        /**
116         * Check for each {@link Comparable} class in the given package if the
117         * compareTo(..) method works as expected.
118         * <br/>
119         * To get a name of a package call {@link Package#getPackage(String)}.
120         * But be sure that you can't get <em>null</em> as result. In this case
121         * use {@link #assertCompareToOfPackage(String)}.
122         *
123         * @param pkg the package e.g. "patterntesting.runtime"
124         * @see #assertCompareToOfPackage(String)
125         */
126        public static void assertCompareTo(final Package pkg) {
127            assert pkg!= null;
128            assertCompareToOfPackage(pkg.getName());
129        }
130        
131        /**
132         * Check for each {@link Comparable} class in the given package if the
133         * compareTo(..) method works as expected.
134         *
135         * @param packageName the package name e.g. "patterntesting.runtime"
136         */
137        @SuppressWarnings("rawtypes")
138        public static void assertCompareToOfPackage(final String packageName) {
139            assert packageName != null;
140            Collection<Class<Comparable>> comparables = getComparableClasses(packageName);
141            assertCompareTo(comparables);
142        }
143    
144        /**
145         * Check for each {@link Comparable} class in the given package if the
146         * compareTo(..) method works as expected.
147         *
148         * @param packageName the package name e.g. "patterntesting.runtime"
149         * @param excluded classes which should be excluded from the check
150         * @see #assertCompareToOfPackage(String)
151         */
152        public static void assertCompareToOfPackage(final String packageName,
153                final Class<? extends Comparable<?>>... excluded) {
154            List<Class<? extends Comparable<?>>> excludedList = Arrays.asList(excluded);
155            assertCompareToOfPackage(packageName, excludedList);
156        }
157    
158        /**
159         * Check for each {@link Comparable} class in the given package if the
160         * compareTo(..) method works as expected.
161         *
162         * @param packageName the package name e.g. "patterntesting.runtime"
163         * @param excluded classes which should be excluded from the check
164         * @see #assertCompareToOfPackage(String)
165         */
166        @SuppressWarnings("rawtypes")
167        public static void assertCompareToOfPackage(final String packageName,
168                final List<Class<? extends Comparable<?>>> excluded) {
169            Collection<Class<Comparable>> classes = getComparableClasses(packageName);
170            log.debug("{} will be excluded from check", excluded);
171            ObjectTester.removeClasses(classes, excluded);
172            assertCompareTo(classes);
173        }
174    
175        @SuppressWarnings( "rawtypes" )
176        private static Collection<Class<Comparable>> getComparableClasses(final String packageName) {
177            Collection<Class<Comparable>> comparables = classpathMonitor.getClassList(packageName,
178                    Comparable.class);
179            return comparables;
180        }
181    
182    }
183