001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.metrics.persistent; 005 006 import java.sql.SQLException; 007 import java.text.MessageFormat; 008 import java.util.ArrayList; 009 import java.util.Collection; 010 import java.util.Collections; 011 import java.util.Comparator; 012 import java.util.Date; 013 import java.util.HashMap; 014 import java.util.Iterator; 015 import java.util.List; 016 import java.util.Map; 017 018 import org.w3c.dom.Element; 019 020 import biz.hammurapi.sql.SQLRuntimeException; 021 import biz.hammurapi.util.Visitable; 022 import biz.hammurapi.util.Visitor; 023 import biz.hammurapi.xml.dom.DomSerializable; 024 025 026 /** 027 * @author Pavel Vlasov 028 * @revision $Revision$ 029 */ 030 public abstract class AbstractPeriod implements DomSerializable, Visitable { 031 032 protected long timeFrom; 033 protected long timeTo; 034 protected Slice[] slices; 035 protected PeriodFactory factory; 036 private double maxValue; 037 private double minValue; 038 private double deviation; 039 private double total; 040 private double measurements; 041 042 /** 043 * @return Returns the deviation. 044 */ 045 public double getDeviation() { 046 return measurements==0 ? 0 : deviation/measurements; 047 } 048 049 /** 050 * @return Returns the from. 051 */ 052 public long getTimeFrom() { 053 return timeFrom; 054 } 055 056 /** 057 * @return Returns the maxValue. 058 */ 059 public double getMaxValue() { 060 return maxValue; 061 } 062 063 /** 064 * @return Returns the measurements. 065 */ 066 public double getMeasurements() { 067 return measurements; 068 } 069 070 /** 071 * @return Returns the minValue. 072 */ 073 public double getMinValue() { 074 return minValue; 075 } 076 077 /** 078 * @return Returns the to. 079 */ 080 public long getTimeTo() { 081 return timeTo; 082 } 083 084 /** 085 * @return Returns the total. 086 */ 087 public double getTotal() { 088 return total; 089 } 090 091 /** 092 * @return Returns the slices. 093 */ 094 public Slice[] getSlices() { 095 return slices; 096 } 097 098 private List subCategories; 099 100 public Collection getSubCategories() throws SQLException { 101 if (subCategories==null) { 102 subCategories=new ArrayList(); 103 loadSubCategories(subCategories); 104 Collections.sort(subCategories, new Comparator() { 105 106 public int compare(Object period0, Object period1) { 107 double total0 = ((AbstractPeriod) period0).getTotal(); 108 double total1 = ((AbstractPeriod) period1).getTotal(); 109 return total0==total1 ? 0 : total0>total1 ? -1 : 1; 110 } 111 112 }); 113 114 Iterator it=subCategories.iterator(); 115 for (int i=1; it.hasNext(); i++) { 116 Period p=(Period) it.next(); 117 labelMap.put(new Integer(p.getId()), p.getName().length()>15 ? "Category "+i : p.getName()); 118 } 119 120 } 121 122 return subCategories; 123 } 124 125 /** 126 * This method should add subcategories (of type Period) to this category. 127 * @throws SQLException 128 */ 129 protected abstract void loadSubCategories(List subCategories) throws SQLException; 130 131 public void toDom(Element holder) { 132 setAttributes(holder); 133 for (int i=0; i<slices.length; i++) { 134 Element se=holder.getOwnerDocument().createElement("slice"); 135 holder.appendChild(se); 136 slices[i].toDom(se); 137 se.setAttribute("avg", String.valueOf(slices[i].getMeasurements()==0 ? 0 : slices[i].getTotal()/slices[i].getMeasurements())); 138 se.setAttribute("length", String.valueOf(slices[i].getTimeTo()-slices[i].getTimeFrom())); 139 } 140 141 try { 142 Iterator it=getSubCategories().iterator(); 143 while (it.hasNext()) { 144 Element e=holder.getOwnerDocument().createElement("category"); 145 holder.appendChild(e); 146 AbstractPeriod period = (AbstractPeriod) it.next(); 147 period.setAttributes(e); 148 e.setAttribute("pct", MessageFormat.format("{0,number,#.##}", new Object[] {new Double(100*period.getTotal()/getTotal())})); 149 } 150 } catch (SQLException e) { 151 throw new SQLRuntimeException(e); 152 } 153 } 154 155 private Map labelMap=new HashMap(); 156 157 /** 158 * @param childId 159 * @return 160 */ 161 public String getLabel(int childId) { 162 return (String) labelMap.get(new Integer(childId)); 163 } 164 165 /** 166 * @param holder 167 */ 168 protected void setAttributes(Element holder) { 169 holder.setAttribute("from", new Date(timeFrom).toString()); 170 holder.setAttribute("from-msec", String.valueOf(timeFrom)); 171 holder.setAttribute("to", new Date(timeTo).toString()); 172 holder.setAttribute("to-msec", String.valueOf(timeTo)); 173 holder.setAttribute("max", String.valueOf(maxValue)); 174 holder.setAttribute("min", String.valueOf(minValue)); 175 holder.setAttribute("deviation", String.valueOf(getDeviation())); 176 holder.setAttribute("total", String.valueOf(total)); 177 holder.setAttribute("measurements", String.valueOf(measurements)); 178 holder.setAttribute("avg", String.valueOf(total/measurements)); 179 holder.setAttribute("length", String.valueOf(getLength())); 180 holder.setAttribute("name", name); 181 } 182 183 /** 184 * @param id 185 * @param slices 186 */ 187 protected void createSlices(int slices) { 188 this.slices=new Slice[slices]; 189 sliceInitialized=new boolean[slices]; 190 for (int i=0; i<slices; i++) { 191 this.slices[i]=new SliceImpl(); 192 this.slices[i].setTimeFrom((long) ((((double) this.timeFrom)*(slices-i)+((double) this.timeTo)*i)/slices)); 193 if (i==slices-1) { 194 this.slices[i].setTimeTo(this.timeTo); 195 } 196 197 if (i>0) { 198 this.slices[i-1].setTimeTo(this.slices[i].getTimeFrom()-1); 199 } 200 201 this.slices[i].setId(i); 202 } 203 } 204 205 private boolean initialized; 206 private boolean[] sliceInitialized; 207 208 /** 209 * @param slice 210 */ 211 protected void aggregate(Slice slice) { 212 long interval=Math.min(slice.getTimeTo(), this.timeTo)-Math.max(slice.getTimeFrom(), this.timeFrom); 213 if (interval>=0) { 214 if (!initialized) { 215 minValue=slice.getMinValue(); 216 maxValue=slice.getMaxValue(); 217 initialized=true; 218 } else { 219 minValue=Math.min(minValue, slice.getMinValue()); 220 maxValue=Math.max(maxValue, slice.getMaxValue()); 221 } 222 223 double coeff = interval==slice.getTimeTo()-slice.getTimeFrom() ? 1 : ((double) interval)/(double)(slice.getTimeTo()-slice.getTimeFrom()); 224 deviation+=slice.getDeviation()*slice.getMeasurements()*coeff; 225 total+=slice.getTotal()*coeff; 226 measurements+=slice.getMeasurements()*coeff; 227 } 228 229 for (int i=0; i<this.slices.length; i++) { 230 Slice curSlice = this.slices[i]; 231 long sinterval=Math.min(slice.getTimeTo(), curSlice.getTimeTo())-Math.max(slice.getTimeFrom(), curSlice.getTimeFrom()); 232 if (sinterval>=0) { 233 if (sliceInitialized[i]) { 234 curSlice.setMinValue(Math.min(curSlice.getMinValue(), slice.getMinValue())); 235 curSlice.setMaxValue(Math.max(curSlice.getMaxValue(), slice.getMaxValue())); 236 } else { 237 curSlice.setMinValue(slice.getMinValue()); 238 curSlice.setMaxValue(slice.getMaxValue()); 239 sliceInitialized[i]=true; 240 } 241 242 double scoeff = sinterval==slice.getTimeTo()-slice.getTimeFrom() ? 1 : ((double) sinterval)/(double)(slice.getTimeTo()-slice.getTimeFrom()); 243 curSlice.setDeviation(curSlice.getDeviation()+slice.getDeviation()*slice.getMeasurements()*scoeff); 244 curSlice.setTotal(curSlice.getTotal()+slice.getTotal()*scoeff); 245 curSlice.setMeasurements(curSlice.getMeasurements()+slice.getMeasurements()*scoeff); 246 } 247 } 248 } 249 250 /** 251 * Invoke after all aggregations. 252 */ 253 protected void normalizeSliceDeviations() { 254 for (int i=0; i<this.slices.length; i++) { 255 Slice curSlice = this.slices[i]; 256 curSlice.setDeviation(curSlice.getMeasurements()==0 ? 0 : curSlice.getDeviation()/curSlice.getMeasurements()); 257 } 258 } 259 260 public long getLength() { 261 return timeTo-timeFrom; 262 } 263 264 public boolean accept(Visitor visitor) { 265 if (visitor.visit(this)) { 266 try { 267 Iterator it=getSubCategories().iterator(); 268 while (it.hasNext()) { 269 ((Visitable) it.next()).accept(visitor); 270 } 271 } catch (SQLException e) { 272 throw new SQLRuntimeException(e); 273 } 274 return true; 275 } 276 277 return false; 278 } 279 280 /** 281 * @return Returns the name. 282 */ 283 public String getName() { 284 return name; 285 } 286 287 protected String name; 288 289 }