/*
* Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner,
* Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain,
* Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter,
* Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann,
* Samuel Zweifel
*
* This file is part of Jukefox.
*
* Jukefox 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 3 of the License, or any later version. Jukefox 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
* Jukefox. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.ethz.dcg.jukefox.data.log;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;
import java.util.Map;
import ch.ethz.dcg.jukefox.playmode.smartshuffle.agents.IAgent;
public abstract class AbstractLogEntry implements ILogEntry {
private final static int VERSION = 1;
private Date timestamp = null;
@Override
public Date getTimestamp() {
return timestamp;
}
protected void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
@Override
public String getPacked() {
return String.format("%d|%d|%s|%d|%s", // version|timestamp|type-ident|type-version|type-log (synchronize this with PackedLogEntry!)
VERSION, getTimestamp().getTime(), getTypeIdent(), getTypeVersion(), packYourStuff());
}
protected <K, V> String getPackedMap(Map<K, V> map, Formatter<K> keyFormatter, Formatter<V> valueFormatter) {
if (map == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<K, V> entry : map.entrySet()) {
sb.append(String.format(
"%s=%s;",
keyFormatter.format(entry.getKey()), valueFormatter.format(entry.getValue())));
}
sb.deleteCharAt(sb.length() - 1); // Remove last ';'
return sb.toString();
}
/**
* Returns the unique log type identifier. This helps the server side to know how to decode the package.
*
* @return The log type identifier
*/
protected abstract String getTypeIdent();
/**
* Returns the version of this log entry. Gets increased every time the output format of
* {@link ILogEntry#createPackage} changes.
*
* @return
*/
public abstract int getTypeVersion();
/**
* Write your values (and only yours!) into a string.
*
* @return The encoded values
*/
protected abstract String packYourStuff();
/**
* Builder class for an {@link AbstractLogEntry}. Create the instance with this builder to ensure, all required
* fields are set. When building is done call {@link #build()} to get the instance.<br/>
* Please ensure, that you call {@link #init(AbstractLogEntry, Builder)} in the constructor of your builder
* implementation.
*
* @param <L>
* The log-entry class
* @param <B>
* The builder class
*/
public static abstract class Builder<L extends AbstractLogEntry, B extends Builder<?, ?>> {
private L instance;
private B builderInstance;
protected void init(L instance, B builderInstance) {
this.instance = instance;
this.builderInstance = builderInstance;
instance.setTimestamp(new Date()); // Set it to now
}
protected L getInstance() {
return instance;
}
public B setTimestamp(Date timestamp) {
instance.setTimestamp(timestamp);
return builderInstance;
}
/**
* Checks if all the fields are set in {@link #instance} and returns it afterwards.<br/>
* Please ensure, that all fields have <code>protected</code> visibility and the name of fields which do not
* need to be set end with <code>Opt</code>.
*
* @return The instance
*/
public L build() {
Field[] fields = getInstance().getClass().getDeclaredFields();
for (Field field : fields) {
try {
if (field.getName().endsWith("Opt")) {
continue;
}
assert field.get(instance) != null : "You did not set the required field " + field.getName();
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
return instance;
}
}
protected interface Formatter<T> {
public String format(T value);
}
protected class FloatFormatter implements Formatter<Float> {
@Override
public String format(Float value) {
return String.format("%.2f", value);
}
}
protected class DoubleFormatter implements Formatter<Double> {
@Override
public String format(Double value) {
return String.format("%.2f", value);
}
}
protected class AgentFormatter implements Formatter<IAgent> {
@Override
public String format(IAgent value) {
return value.getIdentifier();
}
}
protected class ListFormatter<T> implements Formatter<List<T>> {
private final Formatter<T> itemFormatter;
public ListFormatter(Formatter<T> itemFormatter) {
this.itemFormatter = itemFormatter;
}
@Override
public String format(List<T> list) {
StringBuffer sb = new StringBuffer();
for (T item : list) {
sb.append(itemFormatter.format(item));
sb.append(':');
}
sb.deleteCharAt(sb.length() - 1); // remove last ':'
return sb.toString();
}
}
}