package nbtool.io;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.channels.UnresolvedAddressException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.swing.SwingUtilities;
import nbtool.data.SExpr;
import nbtool.data.log.Log;
import nbtool.util.Center;
import nbtool.util.Center.ToolEvent;
import nbtool.util.Events.EventListener;
import nbtool.util.SharedConstants;
import nbtool.util.Debug;
import nbtool.util.ToolSettings;
import nbtool.util.Utility;
public class CommonIO {
/*
* Can't use (host, port) constructor because it doesn't allow setting of timeout.
* */
public static Socket setupNetSocket(String addr, int port) throws IOException {
Socket newsock = new Socket();
InetSocketAddress address = new InetSocketAddress(addr, port);
if (address.isUnresolved()) {
Debug.error( "CommonIO.setupNetSocket(): ERROR: Could not resolve address: " + addr);
throw new UnresolvedAddressException();
}
try {
newsock.connect(new InetSocketAddress(addr, port), SharedConstants.REMOTE_HOST_TIMEOUT() / 1000);
} catch (SocketTimeoutException ste) {
Debug.error( "CommonIO.setupNetSocket() could not connect to socket within timeout!");
throw ste;
}
return newsock;
}
public static enum IOState {
STARTING,
RUNNING,
ENDING,
FINISHED,
}
public static abstract class IOInstance implements Runnable {
protected IOFirstResponder ifr;
protected volatile IOState state = IOState.STARTING;
protected IOState state() { return state; }
public boolean running() {return state == IOState.RUNNING;}
public boolean finished() {return state == IOState.FINISHED;}
protected Socket socket = null;
protected String host;
protected int port;
public void kill() {
if (state == IOState.FINISHED)
return;
synchronized(this) {
state = IOState.ENDING;
if (socket != null) {
try {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
} catch (IOException e) {
Debug.info( "Exception generated by IOInstance.kill(): " + e.getMessage());
}
}
}
}
protected void finish() {
synchronized(this) {
state = IOState.FINISHED;
if (socket != null) {
try {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
} catch (IOException e) {
Debug.info( "Exception generated by IOInstance.finish(): " + e.getMessage());
}
socket = null;
}
}
}
public String host() {
return host;
}
public int port() {
return port;
}
protected void move(IOState from, IOState to) {
synchronized(this) {
assert(state == from);
state = to;
}
}
public abstract String name();
@Override
public String toString() {
return name();
}
}
protected static class SequenceErrorException extends Exception {
private static final long serialVersionUID = 1L;
public int expected, was;
SequenceErrorException(int e, int w) {
super();
expected = e; was = w;
}
@Override
public String toString() {
return String.format("SequenceError: wanted %d, got %d.", expected, was);
}
}
public static interface IOFirstResponder {
public void ioFinished(IOInstance instance);
public void ioReceived(IOInstance inst, int ret, Log ... out);
/* !CALLED ASYNCHRONOUSLY! */
public boolean ioMayRespondOnCenterThread(IOInstance inst);
}
public static final class GIOFirstResponder {
public static void generateReceived(IOInstance instance, IOFirstResponder responder, int ret, Log ... logs) {
if (instance == null || responder == null)
return;
IOReceived ior = new IOReceived();
ior.instance = instance;
ior.responder = responder;
ior.ret = ret;
ior.logs = logs;
Center.addEvent(ior);
}
public static void generateFinished(IOInstance instance, IOFirstResponder responder) {
if (instance == null || responder == null)
return;
IOFinished iof = new IOFinished();
iof.instance = instance;
iof.responder = responder;
Center.addEvent(iof);
}
}
protected static class IOReceived extends Center.ToolEvent {
protected IOInstance instance;
protected IOFirstResponder responder;
protected int ret;
protected Log[] logs;
protected IOReceived() {}
@Override
protected boolean canCombine() { return false; }
@Override
protected void combine(LinkedList<ToolEvent> others) {}
@Override
protected Class<? extends EventListener> listenerClass() { return null; }
@Override
protected void execute(ArrayList<EventListener> guiList,
ArrayList<EventListener> centerList) {
assert(guiList == null && centerList == null);
if (responder.ioMayRespondOnCenterThread(instance)) {
responder.ioReceived(instance, ret, logs);
} else {
final IOInstance inst = instance;
final IOFirstResponder resp = responder;
final int r = ret;
final Log[] out = logs;
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
resp.ioReceived(inst, r, out);
}
});
}
}
}
protected static class IOFinished extends Center.ToolEvent {
protected IOInstance instance;
protected IOFirstResponder responder;
protected IOFinished() {}
@Override
protected boolean canCombine() { return false; }
@Override
protected void combine(LinkedList<ToolEvent> others) {}
@Override
protected Class<? extends EventListener> listenerClass() { return null; }
@Override
protected void execute(ArrayList<EventListener> guiList,
ArrayList<EventListener> centerList) {
assert(guiList == null && centerList == null);
if (responder.ioMayRespondOnCenterThread(instance)) {
responder.ioFinished(instance);
} else {
final IOInstance inst = instance;
final IOFirstResponder resp = responder;
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
resp.ioFinished(inst);
}
});
}
}
}
}