gt:                 SessionSummary summary = getSummary(date, comp);
context: /*
   * jMemorize - Learning made easy (and fun) - A Leitner flashcards tool
   * Copyright(C) 2004-2008 Riad Djemili and contributors
   * 
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 1, or (at your option)
   * any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  package jmemorize.core.learn;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Comparator;
  import java.util.Date;
  import java.util.LinkedList;
  import java.util.List;
  
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.transform.OutputKeys;
  import javax.xml.transform.Transformer;
  import javax.xml.transform.TransformerFactory;
  import javax.xml.transform.dom.DOMSource;
  import javax.xml.transform.stream.StreamResult;
  
  import jmemorize.core.Main;
  import jmemorize.core.io.XmlBuilder;
  import jmemorize.gui.Localization;
  
  import org.w3c.dom.Document;
  
  /**
   * Stores the history of learn sessions and provides statistics.
   * 
   * @author djemili
   */
  public class LearnHistory
  {
      public class SessionSummary implements Cloneable
      {
          private final Date  m_start;
          private final Date  m_end;
          private final int   m_duration;
          
          private final float m_passed;
          private final float m_failed;
          private final float m_skipped;
          private final float m_relearned;
          
  
          private SessionSummary(Date start, Date end, float passed, float failed, 
              float skipped, float relearned)
          {
              this(start, end, 
                  (int)((end.getTime() - start.getTime()) / (1000*60)),
                  passed, failed, skipped, relearned);
          }
          
          private SessionSummary(Date start)
          {
              this(start, start, 0.0f, 0.0f, 0.0f, 0.0f);
          }
          
          private SessionSummary(Date start, Date end, int duration, 
              float passed, float failed, float skipped, float relearned)
          {
              m_start = start;
              m_end = end;
              m_duration = duration;
              
              m_passed = passed;
              m_failed = failed;
              m_skipped = skipped;
              m_relearned = relearned;
          }
  
          public Date getStart()
          {
              return (Date)m_start.clone();
          }
  
          public Date getEnd()
          {
              return (Date)m_end.clone();
          }
          
          public int getDuration()
          {
              return m_duration;
          }
  
          public float getPassed()
          {
              return m_passed;
          }
  
          public float getFailed()
          {
              return m_failed;
          }
  
          public float getSkipped()
          {
              return m_skipped;
          }
  
          public float getRelearned()
          {
              return m_relearned;
          }
          
          /* (non-Javadoc)
           * @see java.lang.Object#clone()
           */
          public Object clone() throws CloneNotSupportedException
          {
              return super.clone();
          }
          
          /* (non-Javadoc)
           * @see java.lang.Object#equals(java.lang.Object)
           */
          public boolean equals(Object obj)
          {
              if (!(obj instanceof SessionSummary))
              {
                  return false;
              }
              
              SessionSummary other = (SessionSummary)obj;
              
              return m_passed == other.m_passed && m_failed == other.m_failed &&
                  m_skipped == other.m_skipped && m_relearned == other.m_relearned;
          }
          
          /* (non-Javadoc)
           * @see java.lang.Object#hashCode()
           */
          public int hashCode() 
          {
              return m_start.hashCode();
          }
          
          /* (non-Javadoc)
           * @see java.lang.Object#toString()
           */
          public String toString()
          {
              return "summary("+m_start+", "+m_passed+"/"+m_failed+")";
          }
      }
      
      public abstract static class CalendarComparator implements Comparator<SessionSummary>
      {
          /* (non-Javadoc)
           * @see java.util.Comparator
           */
          public int compare(SessionSummary s1, SessionSummary s2)
          {
              Calendar c1 = Calendar.getInstance();
              c1.setTime(s1.getStart());
              
              Calendar c2 = Calendar.getInstance();
              c2.setTime(s2.getStart());
              
              long v1 = toValue(c1);
              long v2 = toValue(c2);
              
              return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
          }
          
          public abstract long toValue(Calendar c);
          public abstract DateFormat getFormat();
          public abstract boolean showRotated();
          public abstract void decCalendarValue(Calendar c);
          
      }
      
      private static class SimpleComparator extends CalendarComparator
      {
          public long toValue(Calendar c)
          {
              return c.getTimeInMillis();
          }
  
          public DateFormat getFormat()
          {
              return Localization.SHORT_DATE_FORMATER;
          }
  
          public boolean showRotated()
          {
              return true;
          }
  
          @Override
          public void decCalendarValue(Calendar c)
          {
              throw new UnsupportedOperationException();
          }
      }
      
      private static class DateComparator extends CalendarComparator
      {
          public long toValue(Calendar c)
          {
              return c.get(Calendar.DAY_OF_YEAR)+ 1000 * c.get(Calendar.YEAR);
          }
  
          public DateFormat getFormat()
          {
              return DateFormat.getDateInstance(DateFormat.SHORT);
          }
  
          public boolean showRotated()
          {
              return true;
          }
  
          @Override
          public void decCalendarValue(Calendar c)
          {
              c.add(Calendar.DAY_OF_YEAR, -1);
          }
      }
      
      private static class WeekComparator extends CalendarComparator
      {
          public long toValue(Calendar c)
          {
              return c.get(Calendar.WEEK_OF_YEAR) + 1000 * c.get(Calendar.YEAR);
          }
  
          public DateFormat getFormat()
          {
              return new SimpleDateFormat("w/yyyy");
          }
          
          public boolean showRotated()
          {
              return true;
          }
  
          @Override
          public void decCalendarValue(Calendar c)
          {
              c.add(Calendar.WEEK_OF_YEAR, -1);
          }
      }
      
      private static class MonthComparator extends CalendarComparator
      {
          public long toValue(Calendar c)
          {
              return c.get(Calendar.MONTH) + 1000 * c.get(Calendar.YEAR);
          }
  
          public DateFormat getFormat()
          {
              return new SimpleDateFormat("M/yyyy");
          }
          
          public boolean showRotated()
          {
              return true;
          }
  
          @Override
          public void decCalendarValue(Calendar c)
          {
              c.add(Calendar.MONTH, -1);
          }
      }
      
      private static class YearComparator extends CalendarComparator
      {
          public long toValue(Calendar c)
          {
              return c.get(Calendar.YEAR);
          }
  
          public DateFormat getFormat()
          {
              return new SimpleDateFormat("yyyy");
          }
          
          public boolean showRotated()
          {
              return false;
          }
  
          @Override
          public void decCalendarValue(Calendar c)
          {
              c.add(Calendar.YEAR, -1);
          }
      }
      
      public static final CalendarComparator SIMPLE_COMP = new SimpleComparator();
      public static final CalendarComparator DATE_COMP   = new DateComparator();
      public static final CalendarComparator WEEK_COMP   = new WeekComparator();
      public static final CalendarComparator MONTH_COMP  = new MonthComparator();
      public static final CalendarComparator YEAR_COMP   = new YearComparator();
      
      // TODO enforce that m_summaries is always sorted in descending date order
      private List<SessionSummary>    m_summaries = new ArrayList<SessionSummary>();
      
      private File                    m_file;
      private boolean                 m_isLoaded; // false, if created from scratch
      
      public LearnHistory()
      {
          this(null);
      }
      
      public LearnHistory(File file)
      {
          try
          {
              m_file = file;
              
              if (m_file != null)
                  load(m_file);
          } 
          catch (Exception e)
          {
              
              Main.logThrowable("Could not load learn history.", e);
          } 
      }
      
      public void addSummary(Date start, Date end, int passed, int failed, 
          int skipped, int relearned)
      {
          SessionSummary sessionSummary = new SessionSummary(
              start, end, passed, failed, skipped, relearned);
          
          m_summaries.add(sessionSummary);
      }
      
      public void setIsLoaded(boolean loaded)
      {
          m_isLoaded = loaded;
      }
      
      public boolean isLoaded()
      {
          return m_isLoaded;
      }
  
      public SessionSummary getLastSummary()
      {
          if (m_summaries.size() == 0)
              return null;
          
          return (SessionSummary)m_summaries.get(m_summaries.size() - 1);
      }
  
      public List<SessionSummary> getSummaries()
      {
          return m_summaries;
      }
      
      public List<SessionSummary> getSummaries(int limit)
      {
          int n = Math.min(limit, m_summaries.size());
          return m_summaries.subList(m_summaries.size() - n, m_summaries.size());
      }
      
      public List<SessionSummary> getSummaries(CalendarComparator comp)
      {
          List<SessionSummary> list = new LinkedList<SessionSummary>();
          
          SessionSummary lastSummary = null;
          SessionSummary aggregatedSummary = null;
          
          // TODO refactor and use getSummary(date, comp)
          for (SessionSummary summary : m_summaries)
          {
              if (lastSummary == null || comp.compare(summary, lastSummary) != 0)
              {
                  if (aggregatedSummary != null)
                      list.add(aggregatedSummary);
                  
                  try
                  {
                      aggregatedSummary = (SessionSummary)summary.clone();
                  }
                  catch (CloneNotSupportedException e)
                  {
                      assert false;
                  }
              }
              else
              {
                  aggregatedSummary = new SessionSummary(
                      aggregatedSummary.m_start, summary.m_end,
                      aggregatedSummary.m_duration + summary.m_duration,
                      aggregatedSummary.m_passed + summary.m_passed,
                      aggregatedSummary.m_failed + summary.m_failed,
                      aggregatedSummary.m_skipped + summary.m_skipped,
                      aggregatedSummary.m_relearned + summary.m_relearned
                  );
              }
              
              lastSummary = summary;
          }
          
          if (aggregatedSummary != null)
              list.add(aggregatedSummary);
          
          return list;
      }
      
      public List<SessionSummary> getSummaries(CalendarComparator comp, int limit,
          boolean showEmpty)
      {
          if (showEmpty && comp != SIMPLE_COMP)
          {
              List<SessionSummary> summaries = new ArrayList<SessionSummary>(limit);
              Calendar c = Calendar.getInstance();
              Date date = c.getTime();
              
              int lastEntry = 0;
              for (int i=0; i<limit; i++)
              {
  