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 }