001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  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,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.commons.matchers;
021
022import java.io.File;
023import java.io.IOException;
024import java.util.Arrays;
025import java.util.List;
026import org.hamcrest.*;
027import org.hamcrest.core.IsEqual;
028import org.hamcrest.core.StringContains;
029import org.hamcrest.core.StringEndsWith;
030import org.hamcrest.core.StringStartsWith;
031import org.apache.isis.core.commons.lang.StringExtensions;
032
033import static org.hamcrest.CoreMatchers.nullValue;
034
035/**
036 * Hamcrest {@link Matcher} implementations.
037 * 
038 */
039public final class IsisMatchers {
040
041    private IsisMatchers() {
042    }
043
044    @Factory
045    public static Matcher<String> containsStripNewLines(final String expected) {
046        final String strippedExpected = StringExtensions.stripNewLines(expected);
047        return new StringContains(strippedExpected) {
048            @Override
049            public boolean matchesSafely(final String actual) {
050                return super.matchesSafely(StringExtensions.stripNewLines(actual));
051            }
052
053            @Override
054            public void describeTo(final Description description) {
055                description.appendText("a string (ignoring new lines) containing").appendValue(strippedExpected);
056            }
057        };
058    }
059
060    @Factory
061    public static Matcher<String> equalToStripNewLines(final String expected) {
062        final String strippedExpected = StringExtensions.stripNewLines(expected);
063        return new IsEqual<String>(strippedExpected) {
064            @Override
065            public boolean matches(final Object actualObj) {
066                final String actual = (String) actualObj;
067                return super.matches(StringExtensions.stripNewLines(actual));
068            }
069
070            @Override
071            public void describeTo(final Description description) {
072                description.appendText("a string (ignoring new lines) equal to").appendValue(strippedExpected);
073            }
074        };
075    }
076
077    @Factory
078    public static StringStartsWith startsWithStripNewLines(final String expected) {
079        final String strippedExpected = StringExtensions.stripNewLines(expected);
080        return new StringStartsWith(strippedExpected) {
081            @Override
082            public boolean matchesSafely(final String actual) {
083                return super.matchesSafely(StringExtensions.stripNewLines(actual));
084            }
085
086            @Override
087            public void describeTo(final Description description) {
088                description.appendText("a string (ignoring new lines) starting with").appendValue(strippedExpected);
089            }
090        };
091    }
092
093    @Factory
094    public static Matcher<String> endsWithStripNewLines(final String expected) {
095        final String strippedExpected = StringExtensions.stripNewLines(expected);
096        return new StringEndsWith(strippedExpected) {
097            @Override
098            public boolean matchesSafely(final String actual) {
099                return super.matchesSafely(StringExtensions.stripNewLines(actual));
100            }
101
102            @Override
103            public void describeTo(final Description description) {
104                description.appendText("a string (ignoring new lines) ending with").appendValue(strippedExpected);
105            }
106        };
107    }
108
109    @Factory
110    public static <T> Matcher<T> anInstanceOf(final Class<T> expected) {
111        return new TypeSafeMatcher<T>() {
112            @Override
113            public boolean matchesSafely(final T actual) {
114                return expected.isAssignableFrom(actual.getClass());
115            }
116
117            @Override
118            public void describeTo(final Description description) {
119                description.appendText("an instance of ").appendValue(expected);
120            }
121        };
122    }
123
124    @Factory
125    public static Matcher<String> nonEmptyString() {
126        return new TypeSafeMatcher<String>() {
127            @Override
128            public boolean matchesSafely(final String str) {
129                return str != null && str.length() > 0;
130            }
131
132            @Override
133            public void describeTo(final Description description) {
134                description.appendText("a non empty string");
135            }
136
137        };
138    }
139
140    @Factory
141    @SuppressWarnings("unchecked")
142    public static Matcher<String> nonEmptyStringOrNull() {
143        return CoreMatchers.anyOf(nullValue(String.class), nonEmptyString());
144    }
145
146    @Factory
147    public static Matcher<List<?>> containsElementThat(final Matcher<?> elementMatcher) {
148        return new TypeSafeMatcher<List<?>>() {
149            @Override
150            public boolean matchesSafely(final List<?> list) {
151                for (final Object o : list) {
152                    if (elementMatcher.matches(o)) {
153                        return true;
154                    }
155                }
156                return false;
157            }
158
159            @Override
160            public void describeTo(final Description description) {
161                description.appendText("contains element that ").appendDescriptionOf(elementMatcher);
162            }
163        };
164    }
165
166    @Factory
167    public static <T extends Comparable<T>> Matcher<T> greaterThan(final T c) {
168        return Matchers.greaterThan(c);
169    }
170
171    @Factory
172    public static Matcher<Class<?>> classEqualTo(final Class<?> operand) {
173
174        class ClassEqualsMatcher extends TypeSafeMatcher<Class<?>> {
175            private final Class<?> clazz;
176
177            public ClassEqualsMatcher(final Class<?> clazz) {
178                this.clazz = clazz;
179            }
180
181            @Override
182            public boolean matchesSafely(final Class<?> arg) {
183                return clazz == arg;
184            }
185
186            @Override
187            public void describeTo(final Description description) {
188                description.appendValue(clazz);
189            }
190        }
191
192        return new ClassEqualsMatcher(operand);
193    }
194
195    @Factory
196    public static Matcher<File> existsAndNotEmpty() {
197
198        return new TypeSafeMatcher<File>() {
199
200            @Override
201            public void describeTo(final Description arg0) {
202                arg0.appendText("exists and is not empty");
203            }
204
205            @Override
206            public boolean matchesSafely(final File f) {
207                return f.exists() && f.length() > 0;
208            }
209        };
210    }
211
212    @Factory
213    public static Matcher<String> matches(final String regex) {
214        return new TypeSafeMatcher<String>() {
215
216            @Override
217            public void describeTo(final Description description) {
218                description.appendText("string matching " + regex);
219            }
220
221            @Override
222            public boolean matchesSafely(final String str) {
223                return str.matches(regex);
224            }
225        };
226    }
227
228    @Factory
229    public static <X> Matcher<Class<X>> anySubclassOf(final Class<X> cls) {
230        return new TypeSafeMatcher<Class<X>>() {
231
232            @Override
233            public void describeTo(final Description arg0) {
234                arg0.appendText("is subclass of ").appendText(cls.getName());
235            }
236
237            @Override
238            public boolean matchesSafely(final Class<X> item) {
239                return cls.isAssignableFrom(item);
240            }
241        };
242    }
243
244    @Factory
245    public static <T> Matcher<List<T>> sameContentsAs(final List<T> expected) {
246        return new TypeSafeMatcher<List<T>>() {
247
248            @Override
249            public void describeTo(final Description description) {
250                description.appendText("same sequence as " + expected);
251            }
252
253            @Override
254            public boolean matchesSafely(final List<T> actual) {
255                return actual.containsAll(expected) && expected.containsAll(actual);
256            }
257        };
258    }
259
260    @Factory
261    public static <T> Matcher<List<T>> listContaining(final T t) {
262        return new TypeSafeMatcher<List<T>>() {
263    
264            @Override
265            public void describeTo(Description arg0) {
266                arg0.appendText("list containing ").appendValue(t);
267            }
268    
269            @Override
270            public boolean matchesSafely(List<T> arg0) {
271                return arg0.contains(t);
272            }
273        };
274    }
275
276    @Factory
277    public static <T> Matcher<List<T>> listContainingAll(final T... items) {
278        return new TypeSafeMatcher<List<T>>() {
279
280            @Override
281            public void describeTo(Description arg0) {
282                arg0.appendText("has items ").appendValue(items);
283                
284            }
285
286            @Override
287            public boolean matchesSafely(List<T> arg0) {
288                return arg0.containsAll(Arrays.asList(items));
289            }
290        };
291    }
292
293    @Factory
294    public static Matcher<List<Object>> containsObjectOfType(final Class<?> cls) {
295        return new TypeSafeMatcher<List<Object>>() {
296
297            @Override
298            public void describeTo(final Description desc) {
299                desc.appendText("contains instance of type " + cls.getName());
300            }
301
302            @Override
303            public boolean matchesSafely(final List<Object> items) {
304                for (final Object object : items) {
305                    if (cls.isAssignableFrom(object.getClass())) {
306                        return true;
307                    }
308                }
309                return false;
310            }
311        };
312    }
313
314    @Factory
315    public static Matcher<String> startsWith(final String expected) {
316        return new TypeSafeMatcher<String>() {
317
318            @Override
319            public void describeTo(Description description) {
320                description.appendText(" starts with '" + expected + "'");
321            }
322
323            @Override
324            public boolean matchesSafely(String actual) {
325                return actual.startsWith(expected);
326            }
327        };
328    }
329
330    @Factory
331    public static Matcher<String> contains(final String expected) {
332        return new TypeSafeMatcher<String>() {
333
334            @Override
335            public void describeTo(Description description) {
336                description.appendText(" contains '" + expected + "'");
337            }
338
339            @Override
340            public boolean matchesSafely(String actual) {
341                return actual.contains(expected);
342            }
343        };
344    }
345
346    
347    @Factory
348    public static Matcher<File> equalsFile(final File file) throws IOException {
349        final String canonicalPath = file.getCanonicalPath();
350        return new TypeSafeMatcher<File>() {
351
352            @Override
353            public void describeTo(Description arg0) {
354                arg0.appendText("file '" + canonicalPath + "'");
355            }
356
357            @Override
358            public boolean matchesSafely(File arg0) {
359                try {
360                    return arg0.getCanonicalPath().equals(canonicalPath);
361                } catch (IOException e) {
362                    return false;
363                }
364            }
365        };
366    }
367
368}