package javaflow.network.impl;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import javaflow.components.api.NewPacket;
import javaflow.components.api.OutputPort;
import javaflow.components.api.Packet;
class PacketImpl implements Packet, NewPacket {
private static final AtomicLong packetIdCounter = new AtomicLong();
//private final PacketListener listener;
private final long packetId;
private final Object content;
private Map<String, Object> metadata;
private PacketType type = PacketType.NORMAL;
AtomicBoolean dropped = new AtomicBoolean(false);
private final OutputPortImpl creatorPort;
private List<PacketImpl> chained;
private PacketImpl attachedTo;
private int packetSize = 1;
private AbstractRunnableComponent owner = null;
Set<PacketChainIterator> iterators = Collections.newSetFromMap(new WeakHashMap<PacketChainIterator, Boolean>());
public PacketImpl(Object content, OutputPortImpl creatorPort) {
this.content = content;
this.creatorPort = creatorPort;
//this.listener = listener;
this.packetId = packetIdCounter.incrementAndGet();
if (creatorPort != null) {
this.owner = creatorPort.owner();
}
}
@Override
public long getId() {
return packetId;
}
@Override
public Object getContent() {
return content;
}
@Override
public void drop() {
if (!dropped.compareAndSet(false, true)) {
throw new Error("Already dropped!");
}
detach();
dropRecursively();
}
private void dropRecursively() {
owner.packetDroppedByComponent(this);
if (chained != null) {
for (PacketImpl p : chained) {
p.dropRecursively();
}
}
owner = null;
}
@Override
public Object getContentAndDrop() {
Object o = getContent();
drop();
return o;
}
@Override
public Object getMetadata(String key) {
if (metadata == null) {
return null;
}
return metadata.get(key);
}
@Override
public PacketImpl setMetadata(String key, Object value) {
createMetadataIfNecessary();
metadata.put(key, value);
return this;
}
@Override
public Packet removeMetadata(String key) {
metadata.remove(key);
return this;
}
@Override
public Collection<String> getMetadataKeys() {
if (metadata == null) {
return Collections.emptyList();
}
return metadata.keySet();
}
@Override
public PacketType getType() {
return type;
}
@Override
public boolean isStartPacket() {
return PacketType.START.equals(type);
}
@Override
public boolean isEndPacket() {
return PacketType.END.equals(type);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Packet #" + packetId());
if (isStartPacket()) {
builder.append(" START ");
}
if (isEndPacket()) {
builder.append(" END ");
}
builder.append(" [");
if (null != getContent()) {
builder.append(getContent().toString());
} else {
builder.append(" no content ");
}
builder.append("]");
return builder.toString();
}
boolean isDropped() {
return dropped.get();
}
@Override
public PacketImpl copyMetadataFrom(Packet packet) {
final Map<String, Object> otherMetaData = ((PacketImpl) packet).metadata;
if (otherMetaData != null) {
createMetadataIfNecessary();
metadata.putAll(otherMetaData);
}
return this;
}
private void createMetadataIfNecessary() {
if (metadata == null) {
metadata = new HashMap<>();
}
}
@Override
public void sendTo(OutputPort out) {
out.send(this);
}
@Override
public PacketImpl beStart() {
type = PacketType.START;
return this;
}
@Override
public PacketImpl beEnd() {
type = PacketType.END;
return this;
}
@Override
public void send() {
creatorPort.send(this);
}
@Override
public PacketImpl attach(Packet packet) {
PacketImpl p = (PacketImpl) packet;
if (null == chained) {
chained = new ArrayList<>();
}
packet.detach();
chained.add(p);
p.attachedTo = this;
p.attachedTo.alterSize(packetSize);
for(PacketChainIterator iterator : iterators){
iterator.indexAdded(chained.size()-1);
}
return this;
}
@Override
public boolean isTree() {
return chained != null && chained.size() > 0;
}
@Override
public PacketImpl getSubchain(Object chainName) {
if (chained == null){
return null;
}
for (PacketImpl p : chained) {
if (chainName.equals(p.getContent())) {
return p;
}
}
return null;
}
@Override
public PacketImpl detach() {
if (attachedTo != null) {
int index = attachedTo.chained.indexOf(this);
attachedTo.chained.remove(index);
attachedTo.alterSize(-packetSize);
for(PacketChainIterator iterator : attachedTo.iterators){
iterator.indexRemoved(index);
}
attachedTo = null;
}
return this;
}
@Override
public Packet attachedTo() {
return attachedTo;
}
@Override
public Iterator<Packet> iterator() {
PacketChainIterator iterator = new PacketChainIterator();
iterators.add(iterator);
return iterator;
}
private void alterSize(int subsize) {
int oldSize = packetSize;
this.packetSize += subsize;
if (attachedTo != null) {
attachedTo.alterSize(packetSize - oldSize);
}
}
/**
* Size of the whole tree including this packet.
*
* @return
*/
protected int totalTreeSize() {
return packetSize;
}
@Override
public boolean isNormalPacket() {
return PacketType.NORMAL.equals(type);
}
public long packetId() {
return packetId;
}
void arrivedAtComponent(AbstractRunnableComponent component) {
this.owner = component;
component.packetArrived(this);
if (chained != null) {
for (PacketImpl p : chained) {
p.arrivedAtComponent(component);
}
}
}
void owner(AbstractRunnableComponent owner) {
this.owner = owner;
}
int ownerId() {
return owner.componentId();
}
void leftComponentTowards(InputPortImpl inputPort) {
owner.packetLeftComponent(this,inputPort.owner());
owner = null;
if (chained != null) {
for (PacketImpl p : chained) {
p.leftComponentTowards(inputPort);
}
}
}
@Override
public int chainSize() {
if (chained != null) {
return chained.size();
}
return 0;
}
private class PacketChainIterator implements Iterator<Packet> {
int nextIndex = 0;
boolean started = false;
@Override
public boolean hasNext() {
return chained != null && nextIndex < chained.size();
}
@Override
public Packet next() {
started = true;
Packet packet = chained.get(nextIndex);
nextIndex++;
return packet;
}
@Override
public void remove() {
if (!started){
throw new IllegalStateException("You must call next before removing anything!");
}
chained.get(nextIndex).detach();
}
public void indexRemoved(int i) {
if (i<= nextIndex){
nextIndex--;
}
if (nextIndex <0){
nextIndex = 0;
}
}
public void indexAdded(int i) {
if (i< nextIndex){
nextIndex++;
}
}
}
}