001 /*--------------------------------------------------------------------------+
002 $Id: Assessment.java 27816 2010-05-20 16:13:15Z hummelb $
003 | |
004 | Copyright 2005-2010 Technische Universitaet Muenchen |
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 or implied. |
015 | See the License for the specific language governing permissions and |
016 | limitations under the License. |
017 +--------------------------------------------------------------------------*/
018 package edu.tum.cs.commons.assessment;
019
020 import java.io.Serializable;
021 import java.util.Collection;
022
023 import edu.tum.cs.commons.clone.IDeepCloneable;
024
025 /**
026 * This class stores an assessment. An assessment is a multiset of traffic light
027 * colors (i.e. a mapping from traffic light colors to non-negative integers).
028 *
029 * @author Benjamin Hummel
030 * @author $Author: hummelb $
031 * @version $Rev: 27816 $
032 * @levd.rating GREEN Hash: E1A8D09889F5CFB5BAD863BEB224644F
033 */
034 public class Assessment implements Cloneable, IDeepCloneable, Serializable {
035
036 /** The "multimap". */
037 private int[] mapping = new int[ETrafficLightColor.values().length];
038
039 /** The assessment message. */
040 private String message;
041
042 /**
043 * Creates an empty assessment (i.e. one with all entries set to 0).
044 */
045 public Assessment() {
046 // Do nothing, but keep it to have a default constructor.
047 }
048
049 /**
050 * Create an assessment with a single color entry.
051 *
052 * @param color
053 * the color included in this assessment.
054 */
055 public Assessment(ETrafficLightColor color) {
056 add(color);
057 }
058
059 /**
060 * Create an assessement with a assessment message.
061 */
062 public Assessment(ETrafficLightColor color, String message) {
063 add(color);
064 this.message = message;
065 }
066
067 /**
068 * Add a single entry of this color to this assessment.
069 *
070 * @param color
071 * the color added to this assessment.
072 */
073 public void add(ETrafficLightColor color) {
074 mapping[color.ordinal()]++;
075 }
076
077 /**
078 * Add a single entry of this color to this assessment.
079 *
080 * @param color
081 * the color added to this assessment.
082 * @param count
083 * how often to add this color to the assessment.
084 */
085 public void add(ETrafficLightColor color, int count) {
086 if (count < 0) {
087 throw new IllegalArgumentException("Count must be non-negative!");
088 }
089 mapping[color.ordinal()] += count;
090 }
091
092 /**
093 * Merge the provided assessment into this, i.e. increase all trafic light
094 * color counts by the values in the provided asseessment.
095 *
096 * @param a
097 * the assessment to merge in.
098 */
099 public void add(Assessment a) {
100 for (int i = 0; i < mapping.length; ++i) {
101 mapping[i] += a.mapping[i];
102 }
103 }
104
105 /**
106 * @param color
107 * the color whose frequency to read.
108 * @return the number of occurrences of the provided color in this
109 * assessment.
110 */
111 public int getColorFrequency(ETrafficLightColor color) {
112 return mapping[color.ordinal()];
113 }
114
115 /**
116 * @return the most dominant color. If there is a RED entry, RED is
117 * returned. Otherwise, if there is a YELLOW entry, YELLOW is
118 * returned. If there is neither a RED OR YELLOW entry, but a GREEN
119 * one, GREEN is returned. If no color is there, UNKNOWN is
120 * returned.
121 *
122 * This method relies on the fact that the entries in
123 * {@link ETrafficLightColor} are ordered according to their
124 * dominance.
125 */
126 public ETrafficLightColor getDominantColor() {
127 for (int i = 0; i < ETrafficLightColor.values().length; ++i) {
128 if (mapping[i] > 0) {
129 return ETrafficLightColor.values()[i];
130 }
131 }
132 return ETrafficLightColor.UNKNOWN;
133 }
134
135 /**
136 * @return the color that is most frequent in this assessment. If all
137 * frequencies are 0, UNKNOWN is returned. If there are ties, the
138 * more dominant (see {@link #getDominantColor()}) one is returned.
139 */
140 public ETrafficLightColor getMostFrequentColor() {
141 ETrafficLightColor result = null;
142 int resultCount = 0;
143
144 for (int i = 0; i < mapping.length; ++i) {
145 if (mapping[i] > resultCount) {
146 resultCount = mapping[i];
147 result = ETrafficLightColor.values()[i];
148 }
149 }
150
151 if (result != null) {
152 return result;
153 }
154 return ETrafficLightColor.UNKNOWN;
155 }
156
157 /** {@inheritDoc} */
158 @Override
159 public String toString() {
160 int sum = 0;
161 for (int i = 0; i < mapping.length; ++i) {
162 sum += mapping[i];
163 }
164
165 if (sum == 0) {
166 return "";
167 }
168
169 if (sum == 1) {
170 for (int i = 0; i < mapping.length; ++i) {
171 if (mapping[i] > 0) {
172 return ETrafficLightColor.values()[i].toString();
173 }
174 }
175 }
176
177 StringBuilder builder = new StringBuilder("[");
178 appendColor(builder, ETrafficLightColor.GREEN);
179 builder.append(", ");
180 appendColor(builder, ETrafficLightColor.YELLOW);
181 builder.append(", ");
182 appendColor(builder, ETrafficLightColor.RED);
183 builder.append("]");
184 return builder.toString();
185 }
186
187 /**
188 * append a string containing the color and its frequency to the given
189 * builder
190 *
191 * @param builder
192 * @param color
193 */
194 private void appendColor(StringBuilder builder, ETrafficLightColor color) {
195 builder.append(color.toString().substring(0, 1));
196 builder.append(": ");
197 builder.append(getColorFrequency(color));
198 }
199
200 /** {@inheritDoc} */
201 @Override
202 protected Object clone() throws CloneNotSupportedException {
203 Assessment a = (Assessment) super.clone();
204 a.mapping = a.mapping.clone();
205 return a;
206 }
207
208 /** {@inheritDoc} */
209 public Assessment deepClone() {
210 Assessment a = new Assessment();
211 a.add(this);
212 return a;
213 }
214
215 /** {@inheritDoc} */
216 @Override
217 public boolean equals(Object obj) {
218 if (!(obj instanceof Assessment)) {
219 return false;
220 }
221
222 Assessment a = (Assessment) obj;
223 for (int i = 0; i < mapping.length; ++i) {
224 if (mapping[i] != a.mapping[i]) {
225 return false;
226 }
227 }
228 return true;
229 }
230
231 /** {@inheritDoc} */
232 @Override
233 public int hashCode() {
234 int hash = 0;
235 for (int i = 0; i < mapping.length; ++i) {
236 /*
237 * primes taken from
238 * http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
239 */
240 hash *= 97;
241 hash += mapping[i];
242 hash %= 50331653;
243 }
244 return hash;
245 }
246
247 /**
248 * Get assessment message.
249 *
250 * @return the message or <code>null</code> if this assessment has no
251 * message.
252 */
253 public String getMessage() {
254 return message;
255 }
256
257 /**
258 * Set assessment message.
259 *
260 * @param message
261 * the message or <code>null</code> if this assessment has no
262 * message.
263 */
264 public void setMessage(String message) {
265 this.message = message;
266 }
267
268 /** Aggregate assessments. */
269 public static Assessment aggregate(Collection<Assessment> values) {
270 Assessment result = new Assessment();
271 for (Assessment a : values) {
272 result.add(a);
273 }
274 return result;
275 }
276
277 }