1    /*
2     * Copyright 2009 :torweg free software group
3     *
4     * This program is free software: you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     * 
9     * This program is distributed in the hope that it will be useful,
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    * GNU General Public License for more details.
13    * 
14    * You should have received a copy of the GNU General Public License
15    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16    *
17    */
18   package org.torweg.pulse.component.statistics.model.aggregation;
19   
20   import javax.persistence.Basic;
21   import javax.persistence.Entity;
22   import javax.xml.bind.annotation.XmlAccessOrder;
23   import javax.xml.bind.annotation.XmlAccessType;
24   import javax.xml.bind.annotation.XmlAccessorOrder;
25   import javax.xml.bind.annotation.XmlAccessorType;
26   import javax.xml.bind.annotation.XmlElement;
27   import javax.xml.bind.annotation.XmlRootElement;
28   
29   import org.hibernate.Session;
30   import org.torweg.pulse.component.statistics.model.StatisticsServer;
31   import org.torweg.pulse.component.statistics.model.Visit;
32   import org.torweg.pulse.util.time.Duration;
33   
34   /**
35    * Aggregates {@code Visit}s and stores information about the average time spent
36    * on a "per {@code Visit}" basis for the set {@code Duration}.
37    * 
38    * @author Daniel Dietz
39    * @version $Revision: 1579 $
40    */
41   @XmlRootElement(name = "average-time-per-visit-aggregation")
42   @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
43   @XmlAccessorType(XmlAccessType.FIELD)
44   @Entity
45   public class AverageTimePerVisitAggregation extends AbstractAggregation
46           implements ICount {
47   
48       /**
49        * The serialVersionUID.
50        */
51       private static final long serialVersionUID = -8593340599059224392L;
52   
53       /**
54        * The {@code Visit}-counter.
55        */
56       @XmlElement(name = "counter")
57       @Basic
58       private int counter = 0;
59   
60       /**
61        * The average time per {@code Visit} in milliseconds.
62        */
63       @XmlElement(name = "time")
64       @Basic
65       private double time = 0;
66   
67       /**
68        * Default constructor for Hibernate<sup>TM</sup> and JAXB.
69        */
70       @Deprecated
71       protected AverageTimePerVisitAggregation() {
72           super();
73       }
74   
75       /**
76        * Creates a new {@code AverageTimePerVisitAggregation} with the given
77        * {@code StatisticsServer} and the given {@code Duration}.
78        * 
79        * @param server
80        *            the {@code StatisticsServer}
81        * @param duration
82        *            the {@code Duration}
83        */
84       public AverageTimePerVisitAggregation(final StatisticsServer server,
85               final Duration duration) {
86           super();
87           setStatisticsServer(server);
88           setDuration(duration);
89       }
90   
91       /**
92        * Returns the average time in milliseconds per {@code Visit}.
93        * 
94        * @return the <tt>time</tt>
95        */
96       public final double getTime() {
97           return this.time;
98       }
99   
100      /**
101       * Returns the {@code Visit}-count.
102       * 
103       * @return the counter
104       */
105      public final int getCount() {
106          return this.counter;
107      }
108  
109      /**
110       * Adds 1 to the internal counter counter.
111       */
112      protected final void increase() {
113          this.counter++;
114      }
115  
116      /**
117       * Adds the given integer to the internal counter.
118       * 
119       * @param i
120       *            an integer
121       * 
122       * @throws IllegalArgumentException
123       *             if the given i is less than zero
124       */
125      protected final void increaseBy(final int i) {
126          if (i < 0) {
127              throw new IllegalArgumentException("Given i must not be < 0.");
128          }
129          this.counter += i;
130      }
131  
132      /**
133       * Test if the given {@code Visit} is aggregate-able by this {@code
134       * CountryPerVisitAggregation}.
135       * 
136       * @param visit
137       *            the {@code Visit}
138       * 
139       * @return {@code true} if and only if this {@code AbstractAggregation} can
140       *         process the given {@code Visit}, {@code false} otherwise
141       * 
142       * @see org.torweg.pulse.component.statistics.model.aggregation.AbstractAggregation
143       *      #isAggregateable(org.torweg.pulse.component.statistics.model.Visit)
144       */
145      @Override
146      public final boolean isAggregateable(final Visit visit) {
147          if (visit == null
148                  || !getStatisticsServer().equals(
149                          visit.getLastRecord().getStatisticsServer())) {
150              return false;
151          }
152          return true;
153      }
154  
155      /**
156       * Aggregates the given {@code Visit}.
157       * 
158       * @param visit
159       *            the {@code Visit}
160       * @param s
161       *            the {@code Session} UNUSED
162       * 
163       * @return {@code true} if and only if the {@code Visit} has been processed,
164       *         {@code false} otherwise
165       * 
166       * @see org.torweg.pulse.component.statistics.model.aggregation.AbstractAggregation
167       *      #aggregate(org.torweg.pulse.component.statistics.model.Visit)
168       */
169      @Override
170      public final boolean aggregate(final Visit visit, final Session s) {
171  
172          if (!isAggregateable(visit)) {
173              LOGGER.warn("{} CANNOT AGGREGATE{}", this, visit);
174              return false;
175          }
176  
177          // calculate average time per visit
178          this.time = ((this.counter * this.time) + visit.getTimeSpan(true)
179                  .getMilliseconds())
180                  / (this.counter + 1);
181  
182          // increase internal visit-counter
183          increase();
184  
185          return true;
186      }
187  
188      /**
189       * Adds the values of the given {@code AverageTimePerVisitAggregation} if
190       * its {@code Duration} lies within the {@code Duration} of the current
191       * {@code AverageTimePerVisitAggregation}.
192       * 
193       * @param aggr
194       *            the {@code AverageTimePerVisitAggregation}
195       * @throws NullPointerException
196       *             if the given {@code AverageTimePerVisitAggregation} is
197       *             {@code null}
198       * @throws IllegalArgumentException
199       *             if the {@code Duration} of the given {@code
200       *             AverageTimePerVisitAggregation} does not lie within the
201       *             {@code Duration} of the current {@code
202       *             AverageTimePerVisitAggregation}
203       */
204      public final void accumulate(final AverageTimePerVisitAggregation aggr) {
205          if (aggr == null) {
206              throw new NullPointerException(
207                      "The given AverageTimePerVisitAggregation aggr must not be null.");
208          }
209          if (!getDuration().contains(aggr.getDuration())) {
210              throw new IllegalArgumentException(
211                      "The duration ["
212                              + aggr.getDuration()
213                              + "] of the given AverageTimePerVisitAggregation does not lie within ["
214                              + getDuration() + "].");
215          }
216          // re-calculate average time per visit
217          this.time = ((this.counter * this.time) + (aggr.getTime() * aggr
218                  .getCount()))
219                  / (this.counter + aggr.getCount());
220          increaseBy(aggr.getCount());
221      }
222  
223  }
224