001 /** 002 * planningtool - A Planning Tool with Critiquing Support. 003 * 004 * Copyright (C) 2006 olale 005 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License 008 * as published by the Free Software Foundation; either version 2 009 * of the License, or (at your option) any later version. 010 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 020 * Contact information: 021 * E-mail: olale@ida.liu.se 022 * olale@lysator.liu.se 023 */ 024 package se.liu.ida.critiquer.constraints; 025 026 import java.awt.Color; 027 import java.awt.Graphics2D; 028 import java.rmi.NotBoundException; 029 import java.text.DateFormat; 030 import java.util.ArrayList; 031 import java.util.Calendar; 032 import java.util.GregorianCalendar; 033 import java.util.List; 034 035 import se.liu.ida.critiquer.activities.AbstractParamChangedListener; 036 import se.liu.ida.critiquer.activities.Activity; 037 import se.liu.ida.critiquer.activities.ActivityUtils; 038 import se.liu.ida.critiquer.activities.parameters.Parameter; 039 import se.liu.ida.critiquer.activities.parameters.TimeParameter; 040 import se.liu.ida.critiquer.gui.AbstractView; 041 import se.liu.ida.critiquer.gui.View; 042 043 /** 044 * Check/ensure that t2 doesn't come before t1 045 * 046 * This is a modifying constraint for now, but we let it implement the 047 * VisualConstraint interface just in case.. 048 */ 049 public class TimeParameterOrdering implements VisualConstraint { 050 051 /** 052 * 053 */ 054 private static final long serialVersionUID = 1L; 055 056 private AbstractParamChangedListener t1Listener; 057 058 private AbstractParamChangedListener t2Listener; 059 060 private TimeParameter t1; 061 062 private TimeParameter t2; 063 064 private ConstraintPolicy policy = ConstraintPolicy.MODIFY_VALUES; 065 066 private boolean slackAllowed = true; 067 068 /** 069 * Should the distance between the parameters be kept? 070 */ 071 private boolean endToStartConnector = false; 072 073 /** 074 * Custom slack between activities can be 10 minutes 075 */ 076 private int maxSlack = 10 * 60 * 1000; 077 078 private Color color; 079 080 private ArrayList<Class<? extends View>> applicableViews; 081 082 /* 083 * (non-Javadoc) 084 * 085 * @see java.lang.Object#toString() 086 */ 087 @Override 088 public String toString() { 089 return "[" + t1.getActivity() 090 + ":" 091 + t1.getName() 092 + "]" 093 + " < " 094 + "[" 095 + t2.getActivity() 096 + ":" 097 + t2.getName() 098 + "]"; 099 } 100 101 /* 102 * (non-Javadoc) 103 * 104 * @see java.lang.Object#equals(java.lang.Object) 105 */ 106 @Override 107 public boolean equals(Object obj) { 108 boolean eq = false; 109 if (obj instanceof TimeParameterOrdering) { 110 TimeParameterOrdering tp = (TimeParameterOrdering) obj; 111 eq = tp.toString().equals(toString()); 112 } 113 return eq; 114 } 115 116 private void modifyParameter(TimeParameter param, long timeInMillis) { 117 Calendar oldTime = param.getValue(); 118 Calendar newTime = new GregorianCalendar(); 119 newTime.setTimeInMillis(timeInMillis); 120 logModification(param, oldTime, newTime); 121 param.setValue(newTime); 122 } 123 124 /** 125 * Change the parameter which wasn't modified by the user, and possible 126 * parameters that are indirectly affected if this ParameterOrdering orders two consecutive activities to each other. 127 * 128 * @param modifiedParameter 129 */ 130 private void adjustWithRespectTo(TimeParameter modifiedParameter) { 131 try { 132 133 long currentSlack = t2.getValue().getTimeInMillis() - t1.getValue().getTimeInMillis(); 134 boolean tooMuchSlack = (!isSlackAllowed()) && currentSlack > maxSlack; 135 long time = 0; 136 TimeParameter indirectlyModifiedParameter = null; 137 boolean violation = isViolation(); 138 if (endToStartConnector && (violation || tooMuchSlack)) { 139 if (policy == ConstraintPolicy.MODIFY_VALUES) { 140 if (modifiedParameter == t1) { 141 /** 142 * Modified the end time of an activity, so update the end 143 * of the next activity so that the time span of it is 144 * preserved 145 */ 146 indirectlyModifiedParameter = ActivityUtils.getEndTimeParameter(t2.getActivity()); 147 time = t1.getValue().getTimeInMillis() + indirectlyModifiedParameter.getValue() 148 .getTimeInMillis() 149 - t2.getValue().getTimeInMillis(); 150 } else { 151 /** 152 * Modified the start time of an activity, so update the 153 * start time of the previous activity so that the time span 154 * is preserved. 155 */ 156 indirectlyModifiedParameter = ActivityUtils.getStartTimeParameter(t1.getActivity()); 157 time = t2.getValue().getTimeInMillis() - (t1.getValue().getTimeInMillis() - indirectlyModifiedParameter.getValue() 158 .getTimeInMillis()); 159 } 160 modifyParameter(indirectlyModifiedParameter, time); 161 162 } 163 } 164 if (violation || tooMuchSlack) { 165 166 if (policy == ConstraintPolicy.MODIFY_VALUES) { 167 if (modifiedParameter == t1) { 168 modifyParameter(t2, t1.getValue().getTimeInMillis()); 169 } else { 170 modifyParameter(t1, t2.getValue().getTimeInMillis()); 171 } 172 } 173 } 174 175 } catch (Exception e) { 176 e.printStackTrace(); 177 } 178 } 179 180 private void logModification(TimeParameter modifiedParameter, Calendar oldValue, Calendar newValue) { 181 DateFormat shortFormat = DateFormat.getTimeInstance(DateFormat.SHORT); 182 System.out.println(modifiedParameter.getFullName() + " changed from " 183 + shortFormat.format(oldValue.getTime()) 184 + " to " 185 + shortFormat.format(newValue.getTime())); 186 } 187 188 private boolean isViolation() throws NotBoundException { 189 return t1.getValue().after(t2.getValue()); 190 } 191 192 public void setPolicy(ConstraintPolicy policy) { 193 this.policy = policy; 194 // if (policy == ConstraintPolicy.MODIFY_VALUES) { 195 // t1.setEditable(false); 196 // t2.setEditable(false); 197 // } else { 198 // t1.setEditable(true); 199 // t2.setEditable(true); 200 // } 201 } 202 203 /** 204 * Remove the activity update listeners when this constraint is removed 205 * 206 */ 207 public void disableConstraint() { 208 t1.getActivity().removePrivateUpdateListener(t1Listener); 209 t2.getActivity().removePrivateUpdateListener(t2Listener); 210 } 211 212 public TimeParameterOrdering(TimeParameter t1Param, TimeParameter t2Param) { 213 214 this.t1 = t1Param; 215 this.t2 = t2Param; 216 endToStartConnector = t1.getActivity() != t2.getActivity() && ActivityUtils.getEndTimeParameter(t1.getActivity()) == t1 217 && ActivityUtils.getStartTimeParameter(t2.getActivity()) == t2; 218 /* 219 * When comparing the parameter that has changed, identity checks maybe 220 * not possible.. 221 */ 222 t1Listener = new AbstractParamChangedListener() { 223 224 /** 225 * 226 */ 227 private static final long serialVersionUID = 1L; 228 229 @Override 230 public <T> void paramChanged(Activity activity, Parameter<T> p) { 231 if (t1 == p) { 232 adjustWithRespectTo(t1); 233 } 234 } 235 236 }; 237 t1.getActivity().addPrivateUpdateListener(t1Listener); 238 t2Listener = new AbstractParamChangedListener() { 239 240 /** 241 * 242 */ 243 private static final long serialVersionUID = 1L; 244 245 @Override 246 public <T> void paramChanged(Activity activity, Parameter<T> p) { 247 if (t2 == p) { 248 adjustWithRespectTo(t2); 249 } 250 } 251 252 }; 253 t2.getActivity().addPrivateUpdateListener(t2Listener); 254 try { 255 if (isViolation()) { 256 /** 257 * Adjust the later parameter by default 258 */ 259 260 adjustWithRespectTo(t2); 261 } 262 } catch (NotBoundException e) { 263 /** 264 * Ignore this 265 */ 266 } 267 268 } 269 270 public void viewUpdated(View v, Graphics2D g2) { 271 if (policy == ConstraintPolicy.VISUALIZE) { 272 System.out.println(this.getClass() + ".viewUpdated not implemented yet"); 273 274 } 275 } 276 277 public void enableEditFirst() { 278 t1.setEditable(true); 279 t2.setEditable(false); 280 } 281 282 public Color getColor() { 283 return color; 284 } 285 286 public void setColor(Color c) { 287 color = c; 288 289 } 290 291 /** 292 * @return Returns the allowSlack. 293 */ 294 public boolean isSlackAllowed() { 295 return slackAllowed; 296 } 297 298 /** 299 * @param allowSlack 300 * The allowSlack to set. 301 */ 302 public void setSlackAllowed(boolean allowSlack) { 303 this.slackAllowed = allowSlack; 304 } 305 306 /** 307 * @return Returns the t1. 308 */ 309 public TimeParameter getT1() { 310 return t1; 311 } 312 313 /** 314 * @return Returns the t2. 315 */ 316 public TimeParameter getT2() { 317 return t2; 318 } 319 320 public List<Class<? extends View>> getApplicableViews() { 321 if (applicableViews == null) { 322 applicableViews = new ArrayList<Class<? extends View>>(); 323 applicableViews.add(AbstractView.class); 324 } 325 return applicableViews; 326 } 327 328 public boolean isApplicableFor(View view) { 329 return false; 330 } 331 332 public void setActive(boolean active) { 333 // TODO Auto-generated method stub 334 335 } 336 337 public boolean isActive() { 338 // TODO Auto-generated method stub 339 return false; 340 } 341 342 /* 343 * (non-Javadoc) 344 * 345 * @see se.liu.ida.critiquer.constraints.VisualConstraint#addStatusListener(se.liu.ida.critiquer.constraints.ConstraintStatusListener) 346 */ 347 public void addStatusListener(ConstraintStatusListener l) { 348 // TODO Auto-generated method stub 349 350 } 351 352 /* 353 * (non-Javadoc) 354 * 355 * @see se.liu.ida.critiquer.constraints.VisualConstraint#removeStatusListener(se.liu.ida.critiquer.constraints.ConstraintStatusListener) 356 */ 357 public void removeStatusListener(ConstraintStatusListener l) { 358 // TODO Auto-generated method stub 359 360 } 361 362 }