/*******************************************************************************
* Copyright (c) 2009 Vlad Dumitrescu and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available
* at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Vlad Dumitrescu
*******************************************************************************/
package org.erlide.runtime.internal;
import java.util.Collection;
import org.erlide.util.ErlLogger;
import org.erlide.util.erlang.Bindings;
import org.erlide.util.erlang.OtpErlang;
import org.erlide.util.erlang.TermParserException;
import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangException;
import com.ericsson.otp.erlang.OtpErlangExit;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangPid;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.ericsson.otp.erlang.OtpMbox;
/**
* An Erlang io_server.
*/
public class IOServer implements Runnable {
private final IOCallback callback;
private final OtpMbox mbox;
private final Thread thread;
private volatile boolean stopped = false;
public IOServer(final OtpMbox box, final IOCallback callback) {
this.callback = callback;
mbox = box;
thread = new Thread(this, "io_server");
thread.start();
}
public void stop() {
stopped = true;
}
@Override
public void run() {
boolean done = false;
do {
OtpErlangObject msg;
try {
msg = mbox.receive(3000);
if (msg != null) {
ErlLogger.debug("IOS " + Thread.currentThread().getName() + " : "
+ msg);
if (msg instanceof OtpErlangTuple) {
handleMessage(msg);
} else {
ErlLogger.debug("IOServer: unknown message " + msg);
}
}
} catch (final OtpErlangExit e) {
done = true;
} catch (final Exception e) {
ErlLogger.error(e);
}
} while (!stopped || !done || !Thread.interrupted());
if (stopped) {
mbox.close();
}
}
private void handleMessage(final OtpErlangObject msg) {
final OtpErlangTuple tuple = (OtpErlangTuple) msg;
final String tag = ((OtpErlangAtom) tuple.elementAt(0)).atomValue();
if ("io_request".equals(tag)) {
final OtpErlangPid from = (OtpErlangPid) tuple.elementAt(1);
final OtpErlangObject replyAs = tuple.elementAt(2);
final OtpErlangTuple request = (OtpErlangTuple) tuple.elementAt(3);
final OtpErlangObject reply = processRequest(from, request);
final OtpErlangTuple replyMsg = OtpErlang.mkTuple(new OtpErlangAtom(
"io_reply"), replyAs, reply);
mbox.send(from, replyMsg);
} else {
ErlLogger.warn("IOServer: unknown message " + msg);
}
}
private final OtpErlangObject error = OtpErlang.mkTuple(new OtpErlangAtom("error"),
new OtpErlangAtom("request"));
private OtpErlangObject processRequest(final OtpErlangPid from,
final OtpErlangObject arequest) {
if (callback == null) {
return error;
}
Bindings b;
try {
if (arequest instanceof OtpErlangTuple) {
final OtpErlangTuple request = (OtpErlangTuple) arequest;
final String tag = ((OtpErlangAtom) request.elementAt(0)).atomValue();
if ("put_chars".equals(tag)) {
b = OtpErlang.match("{put_chars, Chars}", request);
if (b != null) {
return callback.putChars(from, IOEncoding.latin1, b.get("Chars"));
}
b = OtpErlang.match("{put_chars, Enc:a, Chars}", request);
if (b != null) {
final String enc = b.getAtom("Enc");
return callback.putChars(from, IOEncoding.valueOf(enc),
b.get("Chars"));
}
b = OtpErlang.match("{put_chars, M:a, F:a, A}", request);
if (b != null) {
final String m = b.getAtom("M");
final String f = b.getAtom("F");
final Collection<OtpErlangObject> a = b.getList("A");
return callback.putChars(from, IOEncoding.latin1, m, f, a);
}
b = OtpErlang.match("{put_chars, Enc:a, M:a, F:a, A}", request);
if (b != null) {
final String enc = b.getAtom("Enc");
final String m = b.getAtom("M");
final String f = b.getAtom("F");
final Collection<OtpErlangObject> a = b.getList("A");
return callback.putChars(from, IOEncoding.valueOf(enc), m, f, a);
}
return error;
} else if ("get_until".equals(tag)) {
b = OtpErlang.match("{get_until, Prompt}", request);
if (b != null) {
return callback.getUntil(IOEncoding.latin1, b.get("Prompt"));
}
b = OtpErlang.match("{get_until, Prompt, N:i}", request);
if (b != null) {
final long n = b.getLong("N");
return callback.getUntil(IOEncoding.latin1, b.get("Prompt"), n);
}
b = OtpErlang.match("{get_until, Enc:a, Prompt}", request);
if (b != null) {
final String enc = b.getAtom("Enc");
return callback
.getUntil(IOEncoding.valueOf(enc), b.get("Prompt"));
}
b = OtpErlang.match("{get_until, Enc:a, Prompt, N:i}", request);
if (b != null) {
final String enc = b.getAtom("Enc");
final long n = b.getLong("N");
return callback.getUntil(IOEncoding.valueOf(enc),
b.get("Prompt"), n);
}
b = OtpErlang.match("{get_until, Prompt, M:a, F:a, A}", request);
if (b != null) {
final String m = b.getAtom("M");
final String f = b.getAtom("F");
final Collection<OtpErlangObject> a = b.getList("A");
return callback.getUntil(IOEncoding.latin1, b.get("Prompt"), m,
f, a);
}
b = OtpErlang.match("{get_until, Enc: a, Prompt, M:a, F:a, A}",
request);
if (b != null) {
final String enc = b.getAtom("Enc");
final String m = b.getAtom("M");
final String f = b.getAtom("F");
final Collection<OtpErlangObject> a = b.getList("A");
return callback.getUntil(IOEncoding.valueOf(enc),
b.get("Prompt"), m, f, a);
}
} else if ("requests".equals(tag)) {
b = OtpErlang.match("{requests, Reqs:lx}", request);
if (b != null) {
final Collection<OtpErlangObject> reqs = b.getList("Reqs");
OtpErlangObject val = null;
for (final OtpErlangObject r : reqs) {
val = processRequest(from, r);
if (val.equals(error)) {
return error;
}
}
return val == null ? error : val;
}
} else if ("setopts".equals(tag)) {
b = OtpErlang.match("{setopts, Opts:lx}", request);
if (b != null) {
final Collection<OtpErlangObject> opts = b.getList("Opts");
return callback.setOpts(opts);
}
} else if ("get_geometry".equals(tag)) {
return OtpErlang.mkTuple(new OtpErlangAtom("error"),
new OtpErlangAtom("enotsup"));
} else {
return error;
}
} else if (arequest instanceof OtpErlangAtom) {
final OtpErlangAtom tag = (OtpErlangAtom) arequest;
if ("getopts".equals(tag.atomValue())) {
return callback.getOpts();
}
return error;
} else {
return error;
}
} catch (final TermParserException e) {
ErlLogger.error(e);
} catch (final OtpErlangException e) {
ErlLogger.error(e);
}
return error;
}
}