package context.arch.widget;
import context.arch.storage.Attribute;
import context.arch.storage.AttributeNameValue;
import context.arch.storage.Attributes;
/**
* Widget that repeats attributes for multiple times depending on sequence length.
* @author Brian Y. Lim
*
*/
public abstract class SequenceWidget extends Widget {
public static final String SEQUENCE_LENGTH = "SEQUENCE_LENGTH"; // tag
protected int sequenceLength;
public static final String SEQUENCE_MARKER = "__T";
public SequenceWidget(int port, String id, String widgetClassName, int sequenceLength) {
super(port, id, widgetClassName);
this.sequenceLength = sequenceLength;
}
public SequenceWidget(int port, String id, String widgetClassName, boolean storageFlag, int sequenceLength) {
super(port, id, widgetClassName, storageFlag);
this.sequenceLength = sequenceLength;
}
public SequenceWidget(String id, String widgetClassName, int sequenceLength) {
super(id, widgetClassName);
this.sequenceLength = sequenceLength;
}
public SequenceWidget(String clientClass, String serverClass,
int serverPort, String encoderClass, String decoderClass,
boolean storageFlag, String id, String widgetClassName, int sequenceLength) {
super(clientClass, serverClass, serverPort, encoderClass, decoderClass,
storageFlag, id, widgetClassName);
this.sequenceLength = sequenceLength;
}
public SequenceWidget(String clientClass, String serverClass,
int serverPort, String encoderClass, String decoderClass,
String storageClass, String id, String widgetClassName, int sequenceLength) {
super(clientClass, serverClass, serverPort, encoderClass, decoderClass,
storageClass, id, widgetClassName);
this.sequenceLength = sequenceLength;
}
public int getSequenceLength() {
return sequenceLength;
}
/**
* Overridden to replicate attributes to sequence
*/
@Override
protected void initFull() {
init();
/*
* Attributes added in init(), so replicate them
*/
// already includes w/o prepended time step, to represent freshest data, so that attribute naming can match
nonConstantAttributes.putAll(replicateToSequence(nonConstantAttributes, sequenceLength));
// don't replicate constant attributes
/*
* Add other non-repetitive attributes after replication
*/
addAttribute(Attribute.instance(TIMESTAMP, Long.class));
// add sequence length as constant attribute
addAttribute(AttributeNameValue.instance(SEQUENCE_LENGTH, sequenceLength), true);
setCallbacks(initCallbacks());
// setServices(initServices());
setSubscribers();
getNewOffset();
}
/**
* Prepends sequence index to the names of each attribute.
* This creates new attributes because it's part of a replication process,
* so there needs to be seperate objects.
* @param atts
* @return
*/
protected static Attributes prependSequenceIndex(Attributes atts, int index) {
Attributes atts2 = new Attributes();
for (Attribute<?> att : atts.values()) {
Attribute<?> att2; // new version with prepended name
if (att instanceof AttributeNameValue<?>) {
att2 = att.cloneWithNewName(
getTPrepend(index) + att.getName() // NAME -> __T#_NAME
);
}
else {
att2 = att.cloneWithNewName(
getTPrepend(index) + att.getName() // NAME -> __T#_NAME
);
}
atts2.add(att2);
}
return atts2;
}
public static String getTPrepend(int index) {
return SEQUENCE_MARKER + index + '_';
}
/**
* Replicates attributes to sequence length.
* @param atts
* @param sequenceLength
* @return
*/
protected static Attributes replicateToSequence(Attributes atts, int sequenceLength) {
Attributes sequenceAtts = new Attributes();
for (int i = 0; i < sequenceLength; i++) {
sequenceAtts.putAll(prependSequenceIndex(atts, i));
}
return sequenceAtts;
}
protected static void stepBackNames(Attributes atts) {
/*
* create a new collection so that it does not get
* corrupted with removals of atts with the same names
*/
Attributes newAtts = new Attributes();
for (Attribute<?> att : atts.values()) {
String name = att.getName();
int markerPos = name.indexOf(SEQUENCE_MARKER);
// SEQUENCE_MARKER not found => not a sequence feature (e.g. timestamp)
if (markerPos == -1) { continue; } // ignore
int step = -1;
try {
String substr = name.substring(markerPos + SEQUENCE_MARKER.length()); // skip past "__T"
int splitIndex = substr.indexOf("_");
String origName = substr.substring(splitIndex + 1);
substr = substr.substring(0, splitIndex); // stop before next "_"
step = Integer.parseInt(substr);
// the new step: 0 if it should be removed, -1 if it didn't have a step count; otherwise the new step index
if (step == 0) { continue; } // just ignore
// replace with new name where step count is decremented
String newName = getTPrepend(--step) + origName;
att = att.cloneWithNewName(newName);
// System.out.println("name = " + name + ", newName = " + newName + ", att = " + att);
// System.out.println("atts = " + atts);
// System.out.println("att.class = " + att.getClass());
/*
* Add to attributes if not stepped out of bounds
*/
newAtts.add(att);
} catch (NumberFormatException e) {
// this should not happen, but may if there is a bug
e.printStackTrace();
}
}
/*
* Clear original attributes and add new
*/
//atts.clear(); // don't clear the original since some attributes are needed to check canHandle (e.g. w/o sequence steps); just replace
atts.putAll(newAtts);
}
/**
* Overridden to take attributes data as an incremental input to the widget. It then removes the oldest sequence step of data.
* Effectively doing a FIFO operation.
*/
@Override
protected void notify(String event, Attributes attrs) {
// System.out.println("SequenceWidget.notify attrs 1 = " + attrs);
if (attrs == null) { return; }
/*
* Shift all previous sequence steps one step back, deleting oldest step
*/
stepBackNames(nonConstantAttributes);
// System.out.println("SequenceWidget.notify nonConstantAttributes = " + nonConstantAttributes);
/*
* Set attrs for the newest sequence step
*/
attrs = prependSequenceIndex(attrs, sequenceLength - 1);
// System.out.println("SequenceWidget.notify attrs 2 = " + attrs);
/*
* Add the new attrs
*/
nonConstantAttributes.putAll(attrs);
// System.out.println("notify.nonConstantAttributes = " + nonConstantAttributes);
super.notify(event, nonConstantAttributes);
}
/*
* Should just use a normal widget with no memory of history
*/
// public abstract static class SequenceWidgetData extends WidgetData {
//
// protected int sequenceLength;
//
// public SequenceWidgetData(long timestamp, int sequenceLength) {
// super(timestamp);
// this.sequenceLength = sequenceLength;
// }
//
// @Override
// @Deprecated
// public Attributes toAttributes() {
// Attributes atts = super.toAttributes();
//
// // add this parameter as attribute
// atts.addAttributeNameValue(SEQUENCE_LENGTH, sequenceLength, Attribute.INT);
//
// for (int t = 0; t < sequenceLength; t++) {
// String seqIndexMarker = getSequenceIndexMarker(t);
// Attributes seqAtts = toSequenceAttributes(t, seqIndexMarker);
// atts.addAll(seqAtts);
// }
//
// return atts;
// }
//
// /**
// * To be used by subclasses instead of toAttributes()
// * @param t time step
// * @param seqIndexMarker to prepend attribute name with
// * @return
// */
// public abstract Attributes toSequenceAttributes(int t, String seqIndexMarker);
//
// }
}