package javaflow.network.impl;
import java.util.concurrent.atomic.AtomicBoolean;
import javaflow.components.api.OutputPort;
import javaflow.components.api.Packet;
import javaflow.components.api.PortIsClosed;
import javaflow.network.api.NetworkComponent;
import javaflow.network.api.PortReference;
class OutputPortImpl implements OutputPort, Port {
private InputPortImpl in;
private final PortReference portDef;
private AbstractRunnableComponent owner;
private boolean isConnected = false;
/**
* Whether the output port is waiting for room to be able to finish sending operation.
*/
final AtomicBoolean waitingForSend = new AtomicBoolean();
OutputPortImpl(PortReference portDef) {
this.portDef = portDef;
}
@Override
public void send(Packet p) {
if (p == null) {
return;
}
PacketImpl packet = (PacketImpl) p;
if (packet.isDropped()) {
throw new Error("Dropped package can not be sent!");
};
sendMessageOrThrowErrorIfNoConnection(packet);
}
void setWriteTo(InputPortImpl newIn) {
if (isConnected()) {
throw new Error("Already connected: " + toString() + "<->" + this.in.toString() + ". Tried to connect to " + newIn);
}
newIn.addConnection(this);
this.in = newIn;
isConnected = true;
}
@Override
public String toString() {
return portDef.toString();
}
@Override
public String toString(int indentation) {
return Utils.indent(indentation, toString());
}
@Override
public void close() {
if (isConnected()) {
isConnected = false;
sendWaitEnded();
in.signalOutputClosed(this);
}
}
@Override
public int getOpenPortCount() {
return this.in == null ? 0 : 1;
}
@Override
public boolean hasPacket() {
return false;
}
@Override
public void setOwner(AbstractRunnableComponent owner) {
this.owner = owner;
}
private boolean isConnected() {
return isConnected;
}
private void sendMessageOrThrowErrorIfNoConnection(PacketImpl packet) {
if (isConnected()) {
packet.leftComponentTowards(in);
in.send(this, packet);
} else {
throw new PortIsClosed("Tried to send packet through closed output port (" + this + ")");
}
}
@Override
public PacketImpl createPacket(Object content) {
PacketImpl packet = new PacketImpl(content, this);
owner.packetCreatedByComponent(packet);
return packet;
}
int ownerId() {
return owner.componentId();
}
/**
* Signals owner that this output port is waiting for send operation.
*/
void waitForSend() {
if (waitingForSend.compareAndSet(false, true)) {
owner.waitForSend();
} else {
// ComponentStateListenerImpl.printStatus("Sending waiting but already at wait!");
}
}
/**
* Signals owner that this output port has finished waiting for send operation.
*/
void sendWaitEnded() {
if (waitingForSend.compareAndSet(true, false)) {
owner.sendReturned();
} else {
// ComponentStateListenerImpl.printStatus("Send finished but not waiting!");
}
}
@Override
public boolean isOpen() {
return isConnected() && !in.hasBeenClosed();
}
@Override
public NetworkComponent getOwner() {
return owner;
}
public AbstractRunnableComponent owner() {
return owner;
}
public PortReference portReference() {
return portDef;
}
public void reopen() {
if (this.in != null) {
isConnected = true;
in.signalOutputReconnect();
}
}
}