InputSession.java
package emissary.parser;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Detailed session information as it is parsed This class just records offsets and length of various things using lists
* of PositionRecord. If you want to actually produce the bytes of a session see SessionParser.decomposeSession and
* emissary.parser.DecomposedSession
* <p>
* No assertions are made about the order of the header, footer, and data sections within the original byte array, it
* can be constructed from position records in any order, possibly overlapping and repeated, We only assert that the
* sections asked for are not out of bounds with respect to the overall array boundaries.
* <p>
* The validation scheme allows the overall bounds to be set at either the beginning or at the end of session parsing
* without performance penalty either way.
*/
public class InputSession {
// Logger
private static final Logger logger = LoggerFactory.getLogger(InputSession.class);
// Overall start/length of the session
@Nullable
protected PositionRecord overall = null;
// ordered list of PositionRecord for the header within the session
protected List<PositionRecord> header = new ArrayList<>();
// ordered list of PositionRecord for the footer within the session
protected List<PositionRecord> footer = new ArrayList<>();
// ordered list of PositionRecord for the data within the session
protected List<PositionRecord> data = new ArrayList<>();
// unordered collected metadata during parsing
protected Map<String, Object> metaData = new HashMap<>();
// Indicator of validness
protected boolean valid = true;
/**
* Record details of an input session
*/
public InputSession() {}
/**
* Record details of an input session
*
* @param o the overall position record
*/
public InputSession(PositionRecord o) {
this.overall = o;
}
/**
* Record details of an input session
*
* @param o the overall position record
* @param d position records for the data
*/
public InputSession(PositionRecord o, List<PositionRecord> d) throws ParserException {
this(o);
addDataRecs(d);
}
/**
* Record details of an input session
*
* @param o the overall position record
* @param d position record for the data
*/
public InputSession(PositionRecord o, PositionRecord d) throws ParserException {
this(o);
addDataRec(d);
}
/**
* Record details of an input session
*
* @param o the overall position record
* @param h position records for the header
* @param f position records for the footer
* @param d position records for the data
*/
public InputSession(PositionRecord o, List<PositionRecord> h, List<PositionRecord> f, List<PositionRecord> d) throws ParserException {
this(o, d);
addHeaderRecs(h);
addFooterRecs(f);
}
/**
* Record details of an input session
*
* @param o the overall position record
* @param h position record for the header
* @param f position record for the footer
* @param d position record for the data
* @param m map of collected metadata
*/
public InputSession(PositionRecord o, List<PositionRecord> h, List<PositionRecord> f, List<PositionRecord> d, Map<String, Object> m)
throws ParserException {
this(o, h, f, d);
addMetaData(m);
}
/**
* Set the overall position record
*
* @param rec the PositionRecord for the overall session range
*/
public void setOverall(PositionRecord rec) throws ParserException {
this.overall = rec;
validateAll();
}
/**
* Set the overall position record from the data
*
* @param start for the position record
* @param length for the position record
*/
public void setOverall(int start, int length) throws ParserException {
setOverall(new PositionRecord(start, length));
}
/**
* Add a map of metadata
*
* @param m map of String key and String or PositionRecord values
*/
public void addMetaData(Map<String, Object> m) throws ParserException {
for (Map.Entry<String, Object> entry : m.entrySet()) {
String key = entry.getKey();
Object val = entry.getValue();
if (val instanceof PositionRecord) {
validateRecord((PositionRecord) val);
metaData.put(key, val);
} else if (val instanceof String) {
metaData.put(key, val);
} else {
logger.warn("Ignoring metadata record named {} with type of {} - it is not a PositionRecord or a String", key,
val.getClass().getName());
}
}
}
/**
* Set the session validity
*
* @param b true if session is valid
*/
public void setValid(boolean b) {
this.valid = b;
}
/**
* Get session validity
*
* @return true if session is valid
*/
public boolean isValid() {
return valid;
}
/**
* Get overall start position
*
* @return overall start
*/
public long getStart() {
if (overall != null) {
return overall.getPosition();
}
return 0;
}
/**
* Get overall length
*
* @return overall length
*/
public long getLength() {
if (overall != null) {
return overall.getLength();
}
return 0;
}
/**
* Get overall position record for the session
*
* @return overall position record
*/
public PositionRecord getOverall() {
return overall;
}
/**
* Add and validate a list of header records
*
* @param h list of PositionRecord
* @throws ParserException when a record is out of bounds
*/
public void addHeaderRecs(@Nullable List<PositionRecord> h) throws ParserException {
if (CollectionUtils.isNotEmpty(h)) {
validateList(h);
header.addAll(h);
}
}
/**
* Add and validate a list of data records
*
* @param d list of PositionRecord
* @throws ParserException when a record is out of bounds
*/
public void addDataRecs(@Nullable List<PositionRecord> d) throws ParserException {
if (CollectionUtils.isNotEmpty(d)) {
validateList(d);
data.addAll(d);
}
}
/**
* Add and validate a list of footer records
*
* @param f list of PositionRecord
* @throws ParserException when a record is out of bounds
*/
public void addFooterRecs(@Nullable List<PositionRecord> f) throws ParserException {
if (CollectionUtils.isNotEmpty(f)) {
validateList(f);
footer.addAll(f);
}
}
/**
* Add and validate a header record
*
* @param r the record to add
* @throws ParserException when a record is out of bounds
*/
public void addHeaderRec(PositionRecord r) throws ParserException {
validateRecord(r);
header.add(r);
}
/**
* Create a position record and add it to the header
*
* @param pos starting position
* @param len length of data
* @throws ParserException when out of bounds
*/
public void addHeaderRec(int pos, int len) throws ParserException {
addHeaderRec(new PositionRecord(pos, len));
}
/**
* Add and validate a footer record
*
* @param r the record to add
* @throws ParserException when a record is out of bounds
*/
public void addFooterRec(PositionRecord r) throws ParserException {
validateRecord(r);
footer.add(r);
}
/**
* Create a position record and add it to the footer
*
* @param pos starting position
* @param len length of data
* @throws ParserException when out of bounds
*/
public void addFooterRec(int pos, int len) throws ParserException {
addFooterRec(new PositionRecord(pos, len));
}
/**
* Add a validate a data record
*
* @param r the record to add
* @throws ParserException when a record is out of bounds
*/
public void addDataRec(PositionRecord r) throws ParserException {
validateRecord(r);
data.add(r);
}
/**
* Create a position record and add it to the data
*
* @param pos starting position
* @param len length of data
* @throws ParserException when out of bounds
*/
public void addDataRec(int pos, int len) throws ParserException {
addDataRec(new PositionRecord(pos, len));
}
/**
* Add a metadata position record
*
* @param name name of metadata item
* @param r position record of data
* @throws ParserException when out of bounds
*/
public void addMetaDataRec(String name, PositionRecord r) throws ParserException {
validateRecord(r);
metaData.put(name, r);
}
/**
* Add a metadata position record
*
* @param name name of metadata item
* @param rec string value of metadata item
*/
public void addMetaDataRec(String name, String rec) {
metaData.put(name, rec);
}
/**
* Count of data position records
*
* @return count
*/
public int getDataCount() {
return data.size();
}
/**
* Count of footer position records
*
* @return count
*/
public int getFooterCount() {
return footer.size();
}
/**
* Count of header position records
*
* @return count
*/
public int getHeaderCount() {
return header.size();
}
/**
* Count of metadata records
*
* @return count
*/
public int getMetaDataCount() {
return metaData.size();
}
/**
* Get footer position records
*
* @return list of PositionRecord
*/
public List<PositionRecord> getFooter() {
return footer;
}
/**
* List header records
*
* @return list of PositionRecord
*/
public List<PositionRecord> getHeader() {
return header;
}
/**
* Get data position records
*
* @return list of PositionRecord
*/
public List<PositionRecord> getData() {
return data;
}
/**
* Return a map of metadata information. Some values will be Strings, others will be PositionRecords
*
* @return metadata
*/
public Map<String, Object> getMetaData() {
return metaData;
}
/**
* Info pump for debugging output
*
* @return string representation
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Session is ").append((this.isValid() ? "" : "not")).append("valid").append("\n");
sb.append("Session overall ").append(this.getOverall()).append("\n");
sb.append("Header record ").append(this.getHeader()).append("\n");
sb.append("Data record ").append(this.getData()).append("\n");
sb.append("Footer record ").append(this.getFooter()).append("\n");
Map<String, Object> m = this.getMetaData();
sb.append("Metadata count ").append((m == null ? 0 : m.size())).append("\n");
return sb.toString();
}
/**
* Validation of one PositionRecord. Does nothing if overall bounds not yet set
*
* @param r the record to check
* @throws ParserException when out of bounds
*/
protected void validateRecord(PositionRecord r) throws ParserException {
if (overall != null && r != null && ((r.getPosition() < overall.getPosition()) || (r.getEnd() > overall.getEnd()))) {
throw new ParserException("Position record " + r + " is out of bounds for the data " + overall);
}
}
/**
* Validate a list of PositionRecord. Does nothing if overall bounds not yet set
*
* @param list the list of PositionRecord
* @throws ParserException when out of bounds
*/
protected void validateList(@Nullable List<PositionRecord> list) throws ParserException {
if (overall == null || list == null) {
return;
}
for (PositionRecord p : list) {
validateRecord(p);
}
}
/**
* Validate everything. Does nothing if overall bounds not yet set
*
* @throws ParserException when out of bounds
*/
protected void validateAll() throws ParserException {
if (overall == null) {
return;
}
validateList(header);
validateList(footer);
validateList(data);
validateMetaData();
}
/**
* Validate the PositionRecords in the metadata map Does nothing if overall bounds not yet set
*
* @throws ParserException when out of bounds
*/
protected void validateMetaData() throws ParserException {
if (overall == null || metaData.isEmpty()) {
return;
}
for (Object obj : metaData.values()) {
if (obj instanceof PositionRecord) {
validateRecord((PositionRecord) obj);
}
}
}
}