Skip to content

Instantly share code, notes, and snippets.

@holyjak
Created May 4, 2013 17:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save holyjak/5518196 to your computer and use it in GitHub Desktop.
Save holyjak/5518196 to your computer and use it in GitHub Desktop.
Simple vs. Easy: Writing A Generic Code To Avoid Duplication - replacing a number of anemic data structures with a generic one - IMPROVED. See http://wondersofcode.wordpress.com/2013/05/04/simple-vs-easy…id-duplication/
public interface LogEntry {
// same as before
}
// The generic LogEntry implementation
// JavaDoc removed for the sake of brevity
public class MapLogEntry implements LogEntry {
private static final char FIELD_SEPARATOR = '\t';
private Map<String, String> fields = new HashMap<String, String>();
private final String[][] columns;
private final StringBuilder tabString = new StringBuilder();
private final Date timestamp;
private String key;
public MapLogEntry(Date timestamp, String[][] columns) {
this.timestamp = checkNotNull(timestamp, "timestamp");
this.columns = checkNotNull(columns, "columns");
}
@Override
public String toTabDelimitedString() {
return tabString.toString();
}
public MapLogEntry addInOrder(String column, String value) {
checkAndStoreColumnValue(column, value);
appendToTabString(value);
return this;
}
public MapLogEntry validated() throws IllegalStateException {
if (fields.size() != columns.length) {
throw new IllegalStateException("This entry doesn't contain values for all the columns " +
"expected (" + columns.length + "). Actual values (" + fields.size() + "): " + toTabDelimitedString());
}
return this;
}
private void checkAndStoreColumnValue(String column, String value) {
final int addedColumnIndex = fields.size();
checkElementIndex(addedColumnIndex, columns.length, "Cannot add more values, all " + columns.length +
" columns already provided; column being added: " + column);
String expectedColumn = columns[addedColumnIndex][0];
if (!column.equals(expectedColumn)) {
throw new IllegalArgumentException("Cannot store value for the column '" +
column + "', the column expected at the current position " + addedColumnIndex +
" is '" + expectedColumn + "'");
}
fields.put(column, value);
}
private void appendToTabString(String value) {
if (tabString.length() > 0) {
tabString.append(FIELD_SEPARATOR);
}
tabString.append(valOrNull(replaceFildSeparators(value)));
}
/** Encode value for outputting into a tab-delimited dump. */
Object valOrNull(Object columnValue) {
if (columnValue == null) {
return HiveConstants.NULL_MARKER;
}
return columnValue;
}
@Override
public Date getTimestamp() {
return timestamp;
}
@Override
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public MapLogEntry withKey(String key) {
setKey(key);
return this;
}
/**Utility method to simplify testing. */
public Map<String, String> asFieldsMap() {
return fields;
}
...
}
// Called from a data import job to process individual log lines.
// Some time later, the job will call toTabDelimitedString on it.
public class PlayerEventsParser ... {
@Override
public LogEntry parse(String logLine) throws LogParseException {
... // tokenizing, processing etc. of the data ...
return new MapLogEntry(timestamp, getColumns())
.addInOrder("userid", userId)
.addInOrder("contentid", contentId)
.addInOrder("sessionid", sessionId)
...
.validated();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment