Coverage Report - org.homeunix.thecave.buddi.model.impl.BudgetCategoryImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
BudgetCategoryImpl
25%
33/127
16%
11/68
2.8
 
 1  
 /*
 2  
  * Created on Jul 29, 2007 by wyatt
 3  
  */
 4  
 package org.homeunix.thecave.buddi.model.impl;
 5  
 
 6  
 import java.util.Date;
 7  
 import java.util.HashMap;
 8  
 import java.util.LinkedList;
 9  
 import java.util.List;
 10  
 import java.util.Map;
 11  
 import java.util.logging.Logger;
 12  
 
 13  
 import org.homeunix.thecave.buddi.model.BudgetCategory;
 14  
 import org.homeunix.thecave.buddi.model.BudgetCategoryType;
 15  
 import org.homeunix.thecave.buddi.model.Document;
 16  
 import org.homeunix.thecave.buddi.model.ModelObject;
 17  
 import org.homeunix.thecave.buddi.plugin.api.exception.DataModelProblemException;
 18  
 import org.homeunix.thecave.buddi.plugin.api.exception.InvalidValueException;
 19  
 
 20  
 import ca.digitalcave.moss.collections.SortedArrayList;
 21  
 import ca.digitalcave.moss.common.DateUtil;
 22  
 
 23  
 /**
 24  
  * Default implementation of an BudgetCategory.  You should not create this object directly; 
 25  
  * instead, please use the ModelFactory to create it, as this will ensure that all
 26  
  * required fields are correctly set.
 27  
  * @author wyatt
 28  
  *
 29  
  */
 30  489930
 public class BudgetCategoryImpl extends SourceImpl implements BudgetCategory {
 31  
         private boolean income;
 32  
         private boolean expanded;
 33  
         private BudgetCategoryType periodType;
 34  
         private BudgetCategory parent;
 35  
         private Map<String, Long> amounts;
 36  
         private List<BudgetCategory> children;
 37  
         private List<BudgetCategory> allChildren;
 38  
         
 39  
         public Map<String, Long> getAmounts() {
 40  132670
                 if (amounts == null)
 41  30250
                         amounts = new HashMap<String, Long>();
 42  132670
                 return amounts;
 43  
         }
 44  
         public void setAmounts(Map<String, Long> amounts) {
 45  0
                 this.amounts = amounts;
 46  0
         }
 47  
         /**
 48  
          * Returns the budgeted amount associated with the given budget category, for 
 49  
          * the date in which the given period date exists.
 50  
          * @param periodDate
 51  
          * @return
 52  
          */
 53  
         public long getAmount(Date periodDate){
 54  132670
                 Long l = getAmounts().get(getPeriodKey(periodDate));
 55  132670
                 if (l == null)
 56  132670
                         return 0;
 57  0
                 return l;
 58  
         }
 59  
         
 60  
         @Override
 61  
         public void setDeleted(boolean deleted) throws InvalidValueException {
 62  0
                 if (getDocument() != null)
 63  0
                         getDocument().startBatchChange();
 64  
                 //We need to delete / undelete ancestors / descendents as needed.  The rule to follow is that
 65  
                 // we cannot have any account which is not deleted which has a parent which is deleted.
 66  
                 //We also cannot set any children deleted if the document is not set.  This should be fine
 67  
                 // for almost all operations - the only potential problem would be if you create a budget
 68  
                 // category, add some children to it, delete the parent, and then add the parent.  
 69  
                 // TODO Check for this condition
 70  0
                 if (getDocument() != null){
 71  0
                         if (deleted){
 72  
                                 //If we delete this one, we must also delete all children.
 73  0
                                 for (BudgetCategory bc : getChildren()) {
 74  0
                                         bc.setDeleted(deleted);
 75  
                                 }
 76  
                         }
 77  
                         else {
 78  
                                 //If we undelete this one, we must also undelete all ancestors.  
 79  0
                                 if (getParent() != null)
 80  0
                                         getParent().setDeleted(deleted);
 81  
                         }
 82  
                 }
 83  
                 
 84  0
                 setChanged();
 85  
                 
 86  0
                 super.setDeleted(deleted);
 87  
                 
 88  0
                 if (getDocument() != null)
 89  0
                         getDocument().finishBatchChange();
 90  0
         }
 91  
         
 92  
         public List<BudgetCategory> getChildren() {
 93  53872
                 if (children == null)
 94  30242
                         children = new FilteredLists.BudgetCategoryListFilteredByDeleted(getDocument(), new FilteredLists.BudgetCategoryListFilteredByParent(getDocument(), getDocument().getBudgetCategories(), this));
 95  53872
                 return children;
 96  
         }
 97  
         
 98  
         public List<BudgetCategory> getAllChildren() {
 99  0
                 if (allChildren == null)
 100  0
                         allChildren = new FilteredLists.BudgetCategoryListFilteredByParent(getDocument(), getDocument().getBudgetCategories(), this);
 101  0
                 return allChildren;
 102  
         }        
 103  
         
 104  
         public long getAmount(Date startDate, Date endDate){
 105  0
                 if (startDate.after(endDate))
 106  0
                         throw new RuntimeException("Start date cannot be before End Date!");
 107  
                 
 108  0
                 Logger.getLogger(this.getClass().getName()).info("Starting to calculate the budgeted amount for " + getFullName() + " between " + startDate + " and " + endDate + ".");
 109  
                 
 110  
                 //If Start and End are in the same budget period
 111  0
                 if (getBudgetPeriodType().getStartOfBudgetPeriod(startDate).equals(
 112  
                                 getBudgetPeriodType().getStartOfBudgetPeriod(endDate))){
 113  
 //                        Logger.getLogger().info("Start Date and End Date are in the same period.");
 114  0
                         long amount = getAmount(startDate);
 115  
 //                        Logger.getLogger().info("Amount = " + amount);
 116  0
                         long daysInPeriod = getBudgetPeriodType().getDaysInPeriod(startDate);
 117  
 //                        Logger.getLogger().info("Days in Period = " + daysInPeriod);
 118  0
                         long daysBetween = DateUtil.getDaysBetween(startDate, endDate, true);
 119  
 //                        Logger.getLogger().info("Days Between = " + daysBetween);
 120  
                 
 121  
 //                        Logger.getLogger().info("Returning " + (long) (((double) amount / (double) daysInPeriod) * daysBetween));
 122  
 //                        Logger.getLogger().info("Finished calculating the budget amount.\n\n");
 123  0
                         return (long) (((double) amount / (double) daysInPeriod) * daysBetween);
 124  
                 }
 125  
                  
 126  
                 //If the area between Start and End overlap at least two budget periods. 
 127  0
                 if (getBudgetPeriodType().getBudgetPeriodOffset(startDate, 1).equals(
 128  
                                 getBudgetPeriodType().getStartOfBudgetPeriod(endDate))
 129  
                                 || getBudgetPeriodType().getBudgetPeriodOffset(startDate, 1).before(
 130  
                                                 getBudgetPeriodType().getStartOfBudgetPeriod(endDate))){
 131  
 //                        Logger.getLogger().info("Start Date and End Date are in different budget periods.");
 132  0
                         long amountStartPeriod = getAmount(startDate);
 133  
 //                        Logger.getLogger().info("Amount Start Period = " + amountStartPeriod);
 134  0
                         long daysInStartPeriod = getBudgetPeriodType().getDaysInPeriod(startDate);
 135  
 //                        Logger.getLogger().info("Days in Start Period = " + daysInStartPeriod);
 136  0
                         long daysAfterStartDateInStartPeriod = DateUtil.getDaysBetween(startDate, getBudgetPeriodType().getEndOfBudgetPeriod(startDate), true);
 137  
 //                        Logger.getLogger().info("Days After Start Date in Start Period = " + daysAfterStartDateInStartPeriod);
 138  0
                         double totalStartPeriod = (((double) amountStartPeriod / (double) daysInStartPeriod) * daysAfterStartDateInStartPeriod);
 139  
 //                        Logger.getLogger().info("Total in Start Period = " + totalStartPeriod);
 140  
                         
 141  0
                         double totalInMiddle = 0;
 142  0
                         for (String periodKey : getBudgetPeriods(
 143  
                                         getBudgetPeriodType().getBudgetPeriodOffset(startDate, 1),
 144  
                                         getBudgetPeriodType().getBudgetPeriodOffset(endDate, -1))) {
 145  0
                                 totalInMiddle += getAmount(getPeriodDate(periodKey));
 146  0
                                 Logger.getLogger(this.getClass().getName()).info("Added " + getAmount(getPeriodDate(periodKey)) + " to total for one period in between; current value is " + totalInMiddle);
 147  
                         }
 148  
 //                        Logger.getLogger().info("Total in Middle = " + totalInMiddle);
 149  
                         
 150  0
                         long amountEndPeriod = getAmount(endDate);
 151  
 //                        Logger.getLogger().info("Amount End Period = " + amountEndPeriod);
 152  0
                         long daysInEndPeriod = getBudgetPeriodType().getDaysInPeriod(endDate);
 153  
 //                        Logger.getLogger().info("Days in End Period = " + daysInEndPeriod);
 154  0
                         long daysBeforeEndDateInEndPeriod = DateUtil.getDaysBetween(getBudgetPeriodType().getStartOfBudgetPeriod(endDate), endDate, true);
 155  
 //                        Logger.getLogger().info("Days before End Period = " + daysBeforeEndDateInEndPeriod);
 156  0
                         double totalEndPeriod = (long) (((double) amountEndPeriod / (double) daysInEndPeriod) * daysBeforeEndDateInEndPeriod); 
 157  
 //                        Logger.getLogger().info("Total in End Period = " + totalEndPeriod);
 158  
                         
 159  
 //                        Logger.getLogger().info("Sum of Start Period, Middle, and End Period = " + (totalStartPeriod + totalInMiddle + totalEndPeriod));
 160  
 //                        Logger.getLogger().info("Finished Calculating the Budget Amount\n\n");
 161  0
                         return (long) (totalStartPeriod + totalInMiddle + totalEndPeriod);
 162  
                 }
 163  
 
 164  0
                 throw new RuntimeException("You should not be here.  We have returned all legitimate numbers from getAmount(Date, Date) in BudgetCategoryImpl.  Please contact Wyatt Olson with details on how you got here (what steps did you perform in Buddi to get this error message).");
 165  
         }
 166  
         
 167  
         /**
 168  
          * Returns a list of BudgetPeriods, covering the entire range of periods
 169  
          * occupied by startDate to endDate.
 170  
          * @param startDate
 171  
          * @param endDate
 172  
          * @return
 173  
          */
 174  
         public List<String> getBudgetPeriods(Date startDate, Date endDate){
 175  0
                 List<String> budgetPeriodKeys = new LinkedList<String>();
 176  
 
 177  0
                 Date temp = getBudgetPeriodType().getStartOfBudgetPeriod(startDate);
 178  
 
 179  0
                 while (temp.before(getBudgetPeriodType().getEndOfBudgetPeriod(endDate))){
 180  0
                         budgetPeriodKeys.add(getPeriodKey(temp));
 181  0
                         temp = getBudgetPeriodType().getBudgetPeriodOffset(temp, 1);
 182  
                 }
 183  
 
 184  0
                 return budgetPeriodKeys;
 185  
         }
 186  
         
 187  
         /**
 188  
          * Sets the budgeted amount for the given time period.
 189  
          * @param periodDate
 190  
          * @param amount
 191  
          */
 192  
         public void setAmount(Date periodDate, long amount){
 193  0
                 if (getAmount(periodDate) != amount)
 194  0
                         setChanged();
 195  0
                 getAmounts().put(getPeriodKey(periodDate), amount);
 196  0
         }
 197  
         public BudgetCategoryType getPeriodType() {
 198  1943840
                 return periodType;
 199  
         }
 200  
         public void setPeriodType(BudgetCategoryType periodType) {
 201  30340
                 this.periodType = periodType;
 202  30340
                 setChanged();
 203  30340
         }
 204  
         public boolean isIncome() {
 205  1194192
                 return income;
 206  
         }
 207  
         public void setIncome(boolean income) {
 208  30340
                 this.income = income;
 209  30340
                 setChanged();
 210  30340
         }
 211  
         @Override
 212  
         public void setName(String name) throws InvalidValueException {
 213  
 //                if (getDocument() != null){
 214  
 //                        for (BudgetCategory bc : ((Document) getDocument()).getBudgetCategories()) {
 215  
 //                                if (bc.getName().equalsIgnoreCase(name)
 216  
 //                                                && !bc.equals(this)
 217  
 //                                                && ((bc.getParent() == null && this.getParent() == null)
 218  
 //                                                                || (bc.getParent() != null && this.getParent() != null && bc.getParent().equals(this.getParent()))))
 219  
 //                                        throw new InvalidValueException("The budget category name must be unique for nodes which share the same parent");
 220  
 //                        }
 221  
 //                }
 222  30340
                 super.setName(name);
 223  30340
         }
 224  
         public BudgetCategory getParent() {
 225  1722167
                 return parent;
 226  
         }
 227  
         public void setParent(BudgetCategory parent) {
 228  0
                 this.parent = parent;
 229  0
                 setChanged();
 230  0
         }
 231  
         public boolean isExpanded() {
 232  123100
                 return expanded;
 233  
         }
 234  
         public void setExpanded(boolean expanded) {
 235  0
                 this.expanded = expanded;
 236  0
         }
 237  
         /**
 238  
          * Returns the key which is associated with the date contained within the
 239  
          * current budget period.  The string is constructed as follows:
 240  
          * 
 241  
          * <code>String periodKey = getPeriodType() + ":" + getStartOfBudgetPeriod(periodDate).getTime();</code>
 242  
          * 
 243  
          * @param periodDate
 244  
          * @return
 245  
          */
 246  
         private String getPeriodKey(Date periodDate){
 247  132670
                 Date d = getBudgetPeriodType().getStartOfBudgetPeriod(periodDate); 
 248  132670
                 return getBudgetPeriodType().getName() + ":" + DateUtil.getYear(d) + ":" + DateUtil.getMonth(d) + ":" + DateUtil.getDay(d);
 249  
         }
 250  
         /**
 251  
          * Parses a periodKey to get the date 
 252  
          * @param periodKey
 253  
          * @return
 254  
          */
 255  
         private Date getPeriodDate(String periodKey){
 256  0
                 String[] splitKey = periodKey.split(":");
 257  0
                 if (splitKey.length == 4){
 258  0
                         int year = Integer.parseInt(splitKey[1]);
 259  0
                         int month = Integer.parseInt(splitKey[2]);
 260  0
                         int day = Integer.parseInt(splitKey[3]);
 261  0
                         return getBudgetPeriodType().getStartOfBudgetPeriod(DateUtil.getDate(year, month, day));
 262  
                 }
 263  
 
 264  0
                 throw new DataModelProblemException("Cannot parse date from key " + periodKey);
 265  
         }
 266  
         public String getFullName(){
 267  809799
                 if (getDocument() != null && getParent() != null && !getParent().equals(this)){
 268  0
                         for (BudgetCategory bc : getDocument().getBudgetCategories()) {
 269  0
                                 if (bc.getName().equals(this.getName())
 270  
                                                 && !bc.equals(this))
 271  0
                                         return this.getName() + " (" + this.getParent().getName() + ")";
 272  
                         }
 273  
                 }
 274  809799
                 return this.getName();
 275  
         }
 276  
         /**
 277  
          * Returns the Budget Period type.  One of the values in Enum BudgePeriodKeys.
 278  
          * @return
 279  
          */
 280  
         public BudgetCategoryType getBudgetPeriodType() {
 281  971920
                 if (getPeriodType() == null)
 282  0
                         setPeriodType(new BudgetCategoryTypeMonthly());
 283  971920
                 return getPeriodType();
 284  
         }
 285  
         @Override
 286  
         public int compareTo(ModelObject arg0) {
 287  459590
                 if (arg0 instanceof BudgetCategoryImpl){
 288  459590
                         BudgetCategoryImpl c = (BudgetCategoryImpl) arg0;
 289  459590
                         if (this.isIncome() != c.isIncome())
 290  61516
                                 return -1 * Boolean.valueOf(this.isIncome()).compareTo(Boolean.valueOf(c.isIncome()));
 291  398074
                         return this.getFullName().compareTo(c.getFullName());
 292  
                 }
 293  0
                 return super.compareTo(arg0);
 294  
         }
 295  
         
 296  
         public List<Date> getBudgetedDates() {
 297  0
                 List<Date> budgetedDates = new SortedArrayList<Date>();
 298  
                 
 299  0
                 Map<String, Long> amounts = getAmounts();
 300  0
                 for (String key : amounts.keySet()){
 301  0
                         if (amounts.get(key) != null && amounts.get(key) != 0)
 302  0
                                 budgetedDates.add(getPeriodDate(key));
 303  
                 }
 304  
                 
 305  0
                 return budgetedDates;
 306  
         }
 307  
         
 308  
         BudgetCategory clone(Map<ModelObject, ModelObject> originalToCloneMap) throws CloneNotSupportedException {
 309  
 
 310  0
                 if (originalToCloneMap.get(this) != null)
 311  0
                         return (BudgetCategory) originalToCloneMap.get(this);
 312  
                 
 313  0
                 BudgetCategoryImpl b = new BudgetCategoryImpl();
 314  0
                 originalToCloneMap.put(this, b);
 315  
 
 316  0
                 b.document = (Document) originalToCloneMap.get(document);
 317  0
                 b.expanded = expanded;
 318  0
                 b.income = income;
 319  0
                 b.periodType = periodType;
 320  0
                 b.deleted = isDeleted();
 321  0
                 b.modifiedTime = new Time(modifiedTime);
 322  0
                 b.name = name;
 323  0
                 b.notes = notes;
 324  0
                 if (parent != null)
 325  0
                         b.parent = (BudgetCategory) ((BudgetCategoryImpl) parent).clone(originalToCloneMap);
 326  0
                 b.amounts = new HashMap<String, Long>();
 327  0
                 if (amounts != null){
 328  0
                         for (String s : amounts.keySet()) {
 329  0
                                 b.amounts.put(s, amounts.get(s).longValue());
 330  
                         }
 331  
                 }
 332  
                 
 333  0
                 return b;
 334  
         }
 335  
 }