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}("jedi", yoda), {@link #entry(Object, Object) entry}("sith", 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}("jedi", yoda), {@link #entry(Object, Object) entry}("sith", 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 }