001    /*
002     * Created on Jan 23, 2008
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005     * 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 is distributed on
010     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011     * specific language governing permissions and limitations under the License.
012     *
013     * Copyright @2008-2009 the original author or authors.
014     */
015    package org.fest.assertions;
016    
017    import static org.fest.assertions.Formatting.inBrackets;
018    import static org.fest.util.Strings.concat;
019    import static org.fest.util.Strings.quote;
020    
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.List;
024    import java.util.Map;
025    
026    import org.fest.util.Maps;
027    
028    /**
029     * Understands assertions for <code>{@link Map}</code>s. To create a new instance of this class use the method
030     * <code>{@link Assertions#assertThat(Map)}</code>.
031     *
032     * @author David DIDIER
033     * @author Yvonne Wang
034     * @author Alex Ruiz
035     */
036    public class MapAssert extends GroupAssert<Map<?, ?>> {
037    
038      private static final String ENTRY = "entry";
039      private static final String ENTRIES= "entries";
040    
041      /**
042       * Creates a new </code>{@link MapAssert}</code>.
043       * @param actual the target to verify.
044       */
045      protected MapAssert(Map<?, ?> actual) {
046        super(actual);
047      }
048    
049      /** {@inheritDoc} */
050      public MapAssert as(String description) {
051        description(description);
052        return this;
053      }
054    
055      /** {@inheritDoc} */
056      public MapAssert describedAs(String description) {
057        return as(description);
058      }
059    
060      /** {@inheritDoc} */
061      public MapAssert as(Description description) {
062        description(description);
063        return this;
064      }
065    
066      /** {@inheritDoc} */
067      public MapAssert describedAs(Description description) {
068        return as(description);
069      }
070    
071    
072      /**
073       * Verifies that the actual <code>{@link Map}</code> contains the given entries.
074       * <p>
075       * Example:
076       * <pre>
077       * // static import org.fest.assertions.Assertions.*;
078       * // static import org.fest.assertions.MapAssert.*;
079       *
080       * assertThat(myMap).{@link #includes(org.fest.assertions.MapAssert.Entry...) includes}({@link #entry(Object, Object) entry}(&quot;jedi&quot;, yoda), {@link #entry(Object, Object) entry}(&quot;sith&quot;, anakin));
081       * </pre>
082       * </p>
083       * @param entries the given entries.
084       * @return this assertion error.
085       * @throws AssertionError if the actual map is <code>null</code>.
086       * @throws AssertionError if the actual <code>Map</code> does not contain any of the given entries.
087       * @throws NullPointerException if the given array of entries is <code>null</code>.
088       * @throws NullPointerException if any of the entries in the given array is <code>null</code>.
089       */
090      public MapAssert includes(Entry...entries) {
091        isNotNull();
092        validate(ENTRIES, entries);
093        List<Entry> notFound = new ArrayList<Entry>();
094        for (Entry e : entries) if (!containsEntry(e)) notFound.add(e);
095        if (!notFound.isEmpty()) failIfNotFound(entryOrEntries(notFound), notFound);
096        return this;
097      }
098    
099      /**
100       * Verifies that the actual <code>{@link Map}</code> does not contain the given entries.
101       * <p>
102       * Example:
103       * <pre>
104       * // static import org.fest.assertions.Assertions.*;
105       * // static import org.fest.assertions.MapAssert.*;
106       *
107       * assertThat(myMap).{@link #excludes(org.fest.assertions.MapAssert.Entry...) excludes}({@link #entry(Object, Object) entry}(&quot;jedi&quot;, yoda), {@link #entry(Object, Object) entry}(&quot;sith&quot;, anakin));
108       * </pre>
109       * </p>
110       * @param entries the given entries.
111       * @return this assertion error.
112       * @throws AssertionError if the actual map is <code>null</code>.
113       * @throws AssertionError if the actual <code>Map</code> contains any of the given entries.
114       * @throws NullPointerException if the given array of entries is <code>null</code>.
115       * @throws NullPointerException if any of the entries in the given array is <code>null</code>.
116       */
117      public MapAssert excludes(Entry...entries) {
118        isNotNull();
119        validate(ENTRIES, entries);
120        List<Entry> found = new ArrayList<Entry>();
121        for (Entry e : entries) if (containsEntry(e)) found.add(e);
122        if (!found.isEmpty()) failIfFound(entryOrEntries(found), found);
123        return this;
124      }
125    
126      private boolean containsEntry(Entry e) {
127        if (e == null)
128          throw new NullPointerException(formattedErrorMessage("Entries to check should not contain null"));
129        if (!actual.containsKey(e.key)) return false;
130        return actual.containsValue(e.value);
131      }
132    
133      private String entryOrEntries(List<Entry> found) {
134        return found.size() == 1 ? ENTRY : ENTRIES;
135      }
136    
137      /**
138       * Creates a new map entry.
139       * @param key the key of the entry.
140       * @param value the value of the entry.
141       * @return the created entry.
142       * @see #includes(org.fest.assertions.MapAssert.Entry...)
143       */
144      public static Entry entry(Object key, Object value) {
145        return new Entry(key, value);
146      }
147    
148      /**
149       * Understands an entry in a <code>{@link Map}</code>.
150       *
151       * @author Yvonne Wang
152       */
153      public static class Entry {
154        final Object key;
155        final Object value;
156    
157        Entry(Object key, Object value) {
158          this.key = key;
159          this.value = value;
160        }
161    
162        /** @see java.lang.Object#toString() */
163        @Override public String toString() {
164          return concat(quote(key), "=", quote(value));
165        }
166      }
167    
168      private void failIfNotFound(String description, Collection<?> notFound) {
169        failIfCustomMessageIsSet();
170        fail(concat("the map:", formattedActual(), " does not contain the ", description, ":", inBrackets(notFound)));
171      }
172    
173      private void validate(String description, Object[] objects) {
174        if (objects == null)
175          throw new NullPointerException(
176              formattedErrorMessage(concat("The given array of ", description, " should not be null")));
177      }
178    
179      private void failIfFound(String description, Collection<?> found) {
180        failIfCustomMessageIsSet();
181        fail(concat("the map:", formattedActual(), " contains the ", description, ":", inBrackets(found)));
182      }
183    
184      /**
185       * Verifies that the number of elements in the actual <code>{@link Map}</code> is equal to the given one.
186       * @param expected the expected number of elements in the actual <code>Map</code>.
187       * @return this assertion object.
188       * @throws AssertionError if the actual map is <code>null</code>.
189       * @throws AssertionError if the number of elements of the actual <code>Map</code> is not equal to the given one.
190       */
191      public MapAssert hasSize(int expected) {
192        isNotNull();
193        int actualSize = actualGroupSize();
194        if (actualSize == expected) return this;
195        failIfCustomMessageIsSet();
196        throw failure(concat(
197              "expected size:", inBrackets(expected)," but was:", inBrackets(actualSize), " for map:", inBrackets(actual)));
198      }
199    
200      /**
201       * Verifies that the actual <code>{@link Map}</code> is <code>null</code> or empty.
202       * @throws AssertionError if the actual <code>Map</code> is not <code>null</code> or not empty.
203       */
204      public final void isNullOrEmpty() {
205        if (Maps.isEmpty(actual)) return;
206        failIfCustomMessageIsSet();
207        fail(concat("expecting a null or empty map, but was:", formattedActual()));
208      }
209    
210      /**
211       * Verifies that the actual <code>{@link Map}</code> is empty.
212       * @throws AssertionError if the actual <code>Map</code> is <code>null</code> or not empty.
213       */
214      public void isEmpty() {
215        isNotNull();
216        if (actual.isEmpty()) return;
217        failIfCustomMessageIsSet();
218        fail(concat("expecting empty map, but was:", formattedActual()));
219      }
220    
221      private String formattedActual() {
222        return inBrackets(actual);
223      }
224    
225      /**
226       * Verifies that the actual <code>{@link Map}</code> is equal to the given one.
227       * @param expected the given map to compare the actual <code>Map</code> to.
228       * @return this assertion object.
229       * @throws AssertionError if the actual <code>Map</code> is not equal to the given one.
230       */
231      public MapAssert isEqualTo(Map<?, ?> expected) {
232        assertEqualTo(expected);
233        return this;
234      }
235    
236      /**
237       * Verifies that the actual <code>{@link Map}</code> contains at least on element.
238       * @return this assertion object.
239       * @throws AssertionError if the actual <code>Map</code> is empty.
240       */
241      public MapAssert isNotEmpty() {
242        isNotNull();
243        if (!actual.isEmpty()) return this;
244        failIfCustomMessageIsSet();
245        throw failure("expecting non-empty map, but it was empty");
246      }
247    
248      /**
249       * Verifies that the actual <code>{@link Map}</code> is not equal to the given one.
250       * @param other the given map to compare the actual <code>Map</code> to.
251       * @return this assertion object.
252       * @throws AssertionError if the actual <code>Map</code> is equal to the given one.
253       */
254      public MapAssert isNotEqualTo(Map<?, ?> other) {
255        assertNotEqualTo(other);
256        return this;
257      }
258    
259      /**
260       * Verifies that the actual <code>{@link Map}</code> is not <code>null</code>.
261       * @return this assertion object.
262       * @throws AssertionError if the actual <code>Map</code> is <code>null</code>.
263       */
264      public MapAssert isNotNull() {
265        if (actual == null) fail("expecting a non-null map, but it was null");
266        return this;
267      }
268    
269      /**
270       * Verifies that the actual <code>{@link Map}</code> is not the same as the given one.
271       * @param other the given map to compare the actual <code>Map</code> to.
272       * @return this assertion object.
273       * @throws AssertionError if the actual <code>Map</code> is the same as the given one.
274       */
275      public MapAssert isNotSameAs(Map<?, ?> other) {
276        assertNotSameAs(other);
277        return this;
278      }
279    
280      /**
281       * Verifies that the actual <code>{@link Map}</code> is the same as the given one.
282       * @param expected the given map to compare the actual <code>Map</code> to.
283       * @return this assertion object.
284       * @throws AssertionError if the actual <code>Map</code> is not the same as the given one.
285       */
286      public MapAssert isSameAs(Map<?, ?> expected) {
287        assertSameAs(expected);
288        return this;
289      }
290    
291      /**
292       * Verifies that the actual <code>{@link Map}</code> satisfies the given condition.
293       * @param condition the given condition.
294       * @return this assertion object.
295       * @throws NullPointerException if the given condition is <code>null</code>.
296       * @throws AssertionError if the actual <code>Map</code> does not satisfy the given condition.
297       * @see #is(Condition)
298       */
299      public MapAssert satisfies(Condition<Map<?, ?>> condition) {
300        assertSatisfies(condition);
301        return this;
302      }
303    
304      /**
305       * Verifies that the actual <code>{@link Map}</code> does not satisfy the given condition.
306       * @param condition the given condition.
307       * @return this assertion object.
308       * @throws NullPointerException if the given condition is <code>null</code>.
309       * @throws AssertionError if the actual <code>Map</code> satisfies the given condition.
310       * @see #isNot(Condition)
311       */
312      public MapAssert doesNotSatisfy(Condition<Map<?, ?>> condition) {
313        assertDoesNotSatisfy(condition);
314        return this;
315      }
316    
317      /**
318       * Alias for <code>{@link #satisfies(Condition)}</code>.
319       * @param condition the given condition.
320       * @return this assertion object.
321       * @throws NullPointerException if the given condition is <code>null</code>.
322       * @throws AssertionError if the actual <code>Map</code> does not satisfy the given condition.
323       * @since 1.2
324       */
325      public MapAssert is(Condition<Map<?, ?>> condition) {
326        assertIs(condition);
327        return this;
328      }
329    
330      /**
331       * Alias for <code>{@link #doesNotSatisfy(Condition)}</code>.
332       * @param condition the given condition.
333       * @return this assertion object.
334       * @throws NullPointerException if the given condition is <code>null</code>.
335       * @throws AssertionError if the actual <code>Map</code> satisfies the given condition.
336       * @since 1.2
337       */
338      public MapAssert isNot(Condition<Map<?, ?>> condition) {
339        assertIsNot(condition);
340        return this;
341      }
342    
343      /**
344       * Returns the number of elements in the actual <code>{@link Map}</code>.
345       * @return the number of elements in the actual <code>{@link Map}</code>.
346       */
347      protected int actualGroupSize() {
348        isNotNull();
349        return actual.size();
350      }
351    
352      /** {@inheritDoc} */
353      public MapAssert overridingErrorMessage(String message) {
354        replaceDefaultErrorMessagesWith(message);
355        return this;
356      }
357    }