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    }