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-2011 the original author or authors.
014     */
015    package org.fest.assertions;
016    
017    import static org.fest.util.Strings.quote;
018    import static org.fest.util.ToString.toStringOf;
019    
020    import java.util.*;
021    
022    /**
023     * Assertions for <code>{@link Map}</code>s.
024     * <p>
025     * To create a new instance of this class invoke <code>{@link Assertions#assertThat(Map)}</code>.
026     * </p>
027     *
028     * @author David DIDIER
029     * @author Yvonne Wang
030     * @author Alex Ruiz
031     */
032    public class MapAssert extends GroupAssert<MapAssert, Map<?, ?>> {
033    
034      private static final String ENTRY = "entry";
035      private static final String ENTRIES= "entries";
036    
037      /**
038       * Creates a new </code>{@link MapAssert}</code>.
039       * @param actual the target to verify.
040       */
041      protected MapAssert(Map<?, ?> actual) {
042        super(MapAssert.class, actual);
043      }
044    
045      /**
046       * Verifies that the actual <code>{@link Map}</code> contains the given entries.
047       * <p>
048       * Example:
049       * <pre>
050       * // static import org.fest.assertions.Assertions.*;
051       * // static import org.fest.assertions.MapAssert.*;
052       *
053       * 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));
054       * </pre>
055       * </p>
056       * @param entries the given entries.
057       * @return this assertion error.
058       * @throws AssertionError if the actual map is {@code null}.
059       * @throws AssertionError if the actual {@code Map} does not contain any of the given entries.
060       * @throws NullPointerException if the given array of entries is {@code null}.
061       * @throws NullPointerException if any of the entries in the given array is {@code null}.
062       */
063      public MapAssert includes(Entry...entries) {
064        isNotNull();
065        validate(ENTRIES, entries);
066        List<Entry> notFound = new ArrayList<Entry>();
067        for (Entry e : entries) if (!containsEntry(e)) notFound.add(e);
068        if (!notFound.isEmpty()) failIfNotFound(entryOrEntries(notFound), notFound);
069        return this;
070      }
071    
072      /**
073       * Verifies that the actual <code>{@link Map}</code> does not contain 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 #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));
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}.
086       * @throws AssertionError if the actual {@code Map} contains any of the given entries.
087       * @throws NullPointerException if the given array of entries is {@code null}.
088       * @throws NullPointerException if any of the entries in the given array is {@code null}.
089       */
090      public MapAssert excludes(Entry...entries) {
091        isNotNull();
092        validate(ENTRIES, entries);
093        List<Entry> found = new ArrayList<Entry>();
094        for (Entry e : entries) if (containsEntry(e)) found.add(e);
095        if (!found.isEmpty()) failIfFound(entryOrEntries(found), found);
096        return this;
097      }
098    
099      private boolean containsEntry(Entry e) {
100        if (e == null)
101          throw new NullPointerException(formattedErrorMessage("Entries to check should not contain null"));
102        if (!actual.containsKey(e.key)) return false;
103        return actual.get(e.key).equals(e.value);
104      }
105    
106      private String entryOrEntries(List<Entry> found) {
107        return found.size() == 1 ? ENTRY : ENTRIES;
108      }
109    
110      /**
111       * Creates a new map entry.
112       * @param key the key of the entry.
113       * @param value the value of the entry.
114       * @return the created entry.
115       * @see #includes(org.fest.assertions.MapAssert.Entry...)
116       */
117      public static Entry entry(Object key, Object value) {
118        return new Entry(key, value);
119      }
120    
121      /**
122       * An entry in a <code>{@link Map}</code>.
123       *
124       * @author Yvonne Wang
125       */
126      public static class Entry {
127        final Object key;
128        final Object value;
129    
130        Entry(Object key, Object value) {
131          this.key = key;
132          this.value = value;
133        }
134    
135        /** @see java.lang.Object#toString() */
136        @Override public String toString() {
137          return String.format("%s=%s", quote(key), quote(value));
138        }
139      }
140    
141      private void failIfNotFound(String description, Collection<?> notFound) {
142        failIfCustomMessageIsSet();
143        fail(String.format(
144            "the map:<%s> does not contain the %s:<%s>", formattedActual(), description, toStringOf(notFound)));
145      }
146    
147      private void validate(String description, Object[] objects) {
148        if (objects == null)
149          throw new NullPointerException(
150              formattedErrorMessage(String.format("The given array of %s should not be null", description)));
151      }
152    
153      private void failIfFound(String description, Collection<?> found) {
154        failIfCustomMessageIsSet();
155        fail(String.format("the map:<%s> contains the %s:<%s>", formattedActual(), description, toStringOf(found)));
156      }
157    
158      private String formattedActual() {
159        return toStringOf(actual);
160      }
161    
162      /**
163       * Returns the number of elements in the actual <code>{@link Map}</code>.
164       * @return the number of elements in the actual <code>{@link Map}</code>.
165       */
166      @Override protected int actualGroupSize() {
167        isNotNull();
168        return actual.size();
169      }
170    }