package org.yamcs.yarch;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.utils.TaiUtcConverter.DateTimeComponents;
/**
* Implements different schemes for partitioning by time.
* It gives back a partition start/end and a directory where data shall be stored.
*
* Currently the following are implemented:
* YYYY/DOY
* YYYY/MM
* @author nm
*
*/
public abstract class TimePartitionSchema {
/**
* returns the directory where this instant shall be written.
* This is likely to be expensive operation - since instant has to be converted into a calendar taking into accounts leap seconds and all the rest.
*
* @param instant
* @return
*/
public abstract PartitionInfo getPartitionInfo(long instant);
/**
* Parses a string of the shape "A/B/..." into a PartitionInfo.
* It is used by the storage engines to parse the partitions from disk at startup.
*
* Returns null if the given string does not match the expected directory.
* @return
*/
public abstract PartitionInfo parseDir(String dir);
private String name;
static public TimePartitionSchema getInstance(String schema) {
TimePartitionSchema tps;
if("YYYY/DOY".equalsIgnoreCase(schema)) {
tps = new YYYYDOY();
} else if ("YYYY/MM".equalsIgnoreCase(schema)) {
tps = new YYYYMM();
} else if ("YYYY".equalsIgnoreCase(schema)) {
tps = new YYYY();
} else {
throw new IllegalArgumentException("Invalid time partitioning schema '"+schema+"'. Supported schemas are: YYYY/DOY, YYYY/MM and YYYY");
}
tps.name = schema;
return tps;
}
public static class PartitionInfo {
@Override
public String toString() {
return "PartitionInfo [dir=" + dir + ", partitionStart="
+ partitionStart + ", partitionEnd=" + partitionEnd + "]";
}
public String dir;
public long partitionStart;
public long partitionEnd;
}
/**
* name for the partitioning schema (YYYY/DOY, YYYY/MM, etc)
* @return
*/
public String getName() {
return name;
}
static class YYYYDOY extends TimePartitionSchema {
Pattern p = Pattern.compile("(\\d{4})/(\\d{3})");
@Override
public PartitionInfo getPartitionInfo(long instant) {
DateTimeComponents dtc =TimeEncoding.toUtc(instant);
return getPartitionInfo(dtc.year, dtc.doy);
}
@Override
public PartitionInfo parseDir(String dir) {
Matcher m = p.matcher(dir);
if(m.matches()) {
int year = Integer.parseInt(m.group(1));
int doy = Integer.parseInt(m.group(2));
return getPartitionInfo(year, doy);
} else {
return null;
}
}
private PartitionInfo getPartitionInfo(int year, int doy) {
PartitionInfo pinfo=new PartitionInfo();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.clear();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.DAY_OF_YEAR, doy);
pinfo.partitionStart = TimeEncoding.fromCalendar(cal);
cal.add(Calendar.DAY_OF_YEAR, 1);
pinfo.partitionEnd = TimeEncoding.fromCalendar(cal);
pinfo.dir = String.format("%4d/%03d", year, doy);
return pinfo;
}
}
static class YYYYMM extends TimePartitionSchema {
Pattern p = Pattern.compile("(\\d{4})/(\\d{2})");
@Override
public PartitionInfo getPartitionInfo(long instant) {
DateTimeComponents dtc =TimeEncoding.toUtc(instant);
return getPartitionInfo(dtc.year, dtc.month);
}
@Override
public PartitionInfo parseDir(String dir) {
Matcher m = p.matcher(dir);
if(m.matches()) {
int year = Integer.parseInt(m.group(1));
int month = Integer.parseInt(m.group(2));
return getPartitionInfo(year, month);
} else {
return null;
}
}
private PartitionInfo getPartitionInfo(int year, int month) {
PartitionInfo pinfo=new PartitionInfo();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.clear();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month-1);
cal.set(Calendar.HOUR, 0);
pinfo.partitionStart = TimeEncoding.fromCalendar(cal);
cal.add(Calendar.MONTH, 1);
pinfo.partitionEnd = TimeEncoding.fromCalendar(cal);
pinfo.dir = String.format("%4d/%02d", year, month);
return pinfo;
}
}
static class YYYY extends TimePartitionSchema {
Pattern p = Pattern.compile("(\\d{4})");
@Override
public PartitionInfo getPartitionInfo(long instant) {
DateTimeComponents dtc =TimeEncoding.toUtc(instant);
return getPartitionInfo(dtc.year);
}
@Override
public PartitionInfo parseDir(String dir) {
Matcher m = p.matcher(dir);
if(m.matches()) {
int year = Integer.parseInt(m.group(1));
return getPartitionInfo(year);
} else {
return null;
}
}
private PartitionInfo getPartitionInfo(int year) {
PartitionInfo pinfo=new PartitionInfo();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.clear();
cal.set(Calendar.YEAR, year);
pinfo.partitionStart = TimeEncoding.fromCalendar(cal);
cal.add(Calendar.YEAR, 1);
pinfo.partitionEnd = TimeEncoding.fromCalendar(cal);
pinfo.dir = String.format("%4d", year);
return pinfo;
}
}
}