package org.gaecounter.data;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import org.joda.time.DateMidnight;
import org.joda.time.DateTimeConstants;
import org.joda.time.DateTimeFieldType;
import org.joda.time.Days;
import org.joda.time.Partial;
import org.joda.time.Period;
@PersistenceCapable
public class Counter
{
public Counter(Type mType, String mDate, String mFile)
{
this.mKey = getKey(mFile, mDate);
this.mType = mType;
this.mDate = mDate;
this.mFile = mFile;
this.mCount = 1;
}
public static String getKey(String xiFile, String xiDate)
{
return xiFile + "_" + xiDate;
}
@PrimaryKey
@Persistent
public String mKey;
public enum Type
{
YEAR("yyyy", Period.years(1))
{
@Override
public Partial getPartial(int y, int m, int d)
{
return new Partial(new DateTimeFieldType[]
{DateTimeFieldType.year()},
new int[] {y});
}
},
MONTH("yyyy/MM", Period.months(1))
{
@Override
public Partial getPartial(int y, int m, int d)
{
return new Partial(new DateTimeFieldType[]
{DateTimeFieldType.year(),
DateTimeFieldType.monthOfYear()},
new int[] {y, m});
}
},
WEEK("yyyy/MM/dd", Period.days(7))
{
@Override
public Partial getPartial(int y, int m, int d)
{
return new Partial(new DateTimeFieldType[]
{DateTimeFieldType.year(),
DateTimeFieldType.monthOfYear(),
DateTimeFieldType.dayOfMonth()},
new int[] {y, m, d});
}
},
DAY("yyyy/MM/dd", Period.days(1))
{
@Override
public Partial getPartial(int y, int m, int d)
{
return new Partial(new DateTimeFieldType[]
{DateTimeFieldType.year(),
DateTimeFieldType.monthOfYear(),
DateTimeFieldType.dayOfMonth()},
new int[] {y, m, d});
}
};
private final String mFormat;
public final Period mPeriod;
private Type (String xiF, Period xiP) {mFormat = xiF; mPeriod = xiP;}
public Partial getPartial(int y, int m, int d) {return null;}
public String getPartialStr(int y, int m, int d)
{
return getPartial(y, m, d).toString(mFormat);
}
}
@Persistent
public Type mType;
@Persistent
public String mDate;
@Persistent
public String mFile;
@Persistent
public Integer mCount;
public void incrementCount()
{
mCount++;
}
@SuppressWarnings("unchecked")
public static List<Counter> getAllByType(Type xiType)
{
List<Counter> detachedList = null, list = null;
PersistenceManager pm = PMF.get().getPersistenceManager();
try
{
Query lQuery = pm.newQuery(Counter.class);;
lQuery.setFilter("mType == mTypeParam");
lQuery.declareParameters("String mTypeParam");
list = (List<Counter>)lQuery.execute(xiType);
detachedList = new ArrayList<Counter>();
for (Counter obj : list)
{
detachedList.add(pm.detachCopy(obj));
}
}
finally
{
pm.close();
}
return detachedList;
}
private static final Pattern sVerPattern = Pattern.compile("(.+)_([0-9]+(\\.[0-9]+)*)(\\..+)");
public static Map<String,Map<String,Integer>> getPerFilePerDateMap(List<Counter> xiList,
boolean xiCollapseVers)
{
Map<String,Map<String,Integer>> lFileMap = new HashMap<String, Map<String,Integer>>();
for (Counter lCounter : xiList)
{
String lFile = lCounter.mFile;
String lDateStr = lCounter.mDate;
Integer lCount = lCounter.mCount;
if (xiCollapseVers)
{
Matcher matcher = sVerPattern.matcher(lFile);
if (matcher.matches())
{
lFile = matcher.group(1) + "*" + matcher.group(4);
}
}
Map<String, Integer> lDateMap = lFileMap.get(lFile);
if (lDateMap == null)
{
lDateMap = new HashMap<String, Integer>();
lFileMap.put(lFile, lDateMap);
}
if (xiCollapseVers)
{
Integer lAllVersCount = lDateMap.get(lDateStr);
if (lAllVersCount == null)
{
lAllVersCount = 0;
}
lAllVersCount += lCount;
lDateMap.put(lDateStr, lAllVersCount);
}
else
{
lDateMap.put(lDateStr, lCount);
}
}
return lFileMap;
}
public static List<String> getDateStrs(Type xiType,
Partial xiStart,
Set<String> xiDataDateStrs)
{
List<String> lDateStrs = new ArrayList<String>();
if (xiDataDateStrs.size() > 0)
{
if (xiType == Type.WEEK)
{
DateMidnight foundMonday = new DateMidnight(xiStart.get(DateTimeFieldType.year()),
xiStart.get(DateTimeFieldType.monthOfYear()),
xiStart.get(DateTimeFieldType.dayOfMonth()));
while (foundMonday.getDayOfWeek() != DateTimeConstants.MONDAY)
{
foundMonday = foundMonday.plus(Days.ONE);
}
xiStart = new Partial(new DateTimeFieldType[]
{DateTimeFieldType.year(),
DateTimeFieldType.monthOfYear(),
DateTimeFieldType.dayOfMonth()},
new int[] {foundMonday.getYear(),
foundMonday.getMonthOfYear(),
foundMonday.getDayOfMonth()});
}
List<String> lInDataStrsList = new ArrayList<String>(xiDataDateStrs);
Collections.sort(lInDataStrsList);
String lOldestDate = lInDataStrsList.get(0);
Partial lDate = xiStart;
String lDateStr = lDate.toString(xiType.mFormat);
while (lDateStr.compareTo(lOldestDate) >= 0)
{
lDateStrs.add(lDateStr);
lDate = lDate.minus(xiType.mPeriod);
lDateStr = lDate.toString(xiType.mFormat);
}
lDateStrs.add(lDateStr);
Collections.reverse(lDateStrs);
}
return lDateStrs;
}
public static Map<String,Map<String,Integer>> getFilePerWeekMap(Map<String,Map<String,Integer>> xiPerFilePerDayData)
{
// Filename -> DateStr -> Count
Map<String,Map<String,Integer>> lRet = new HashMap<String, Map<String,Integer>>();
for (Entry<String,Map<String,Integer>> fileEntry : xiPerFilePerDayData.entrySet())
{
String file = fileEntry.getKey();
Map<String,Integer> perWeekData = new HashMap<String, Integer>();
lRet.put(file, perWeekData);
// DateStr -> Count
Map<String, Integer> perDateData = fileEntry.getValue();
List<String> dateStrs = new ArrayList<String>(perDateData.keySet());
Collections.sort(dateStrs);
DateMidnight nextPeriod = null;
int weekCount = 0;
boolean unrecordedItem = false;
// Iterate through the dates from oldest to newest
for (String datestr : dateStrs)
{
DateMidnight date = new DateMidnight(datestr.replace("/", "-"));
if (nextPeriod != null)
{
// We know when the next period starts so check whether this date
// is after the start of the next period
if (date.isEqual(nextPeriod) || date.isAfter(nextPeriod))
{
DateMidnight prevPeriod = nextPeriod.minusDays(7);
perWeekData.put(Type.DAY.getPartialStr(prevPeriod.getYear(),
prevPeriod.getMonthOfYear(),
prevPeriod.getDayOfMonth()),
weekCount);
weekCount = 0;
unrecordedItem = false;
}
}
// Add the count to the current period
Integer count = perDateData.get(datestr);
weekCount += count;
if (!unrecordedItem)
{
// If this is the first item in the new period - work out when the period ends
nextPeriod = date.plusDays(1);
while (nextPeriod.getDayOfWeek() != DateTimeConstants.MONDAY)
{
nextPeriod = nextPeriod.plusDays(1);
}
}
unrecordedItem = true;
}
if (unrecordedItem)
{
// Store the details of the final period
DateMidnight prevPeriod = nextPeriod.minusDays(7);
perWeekData.put(Type.DAY.getPartialStr(prevPeriod.getYear(),
prevPeriod.getMonthOfYear(),
prevPeriod.getDayOfMonth()),
weekCount);
}
}
return lRet;
}
}