/* * JSampler - a java front-end for LinuxSampler * * Copyright (C) 2005-2008 Grigor Iliev * * This file is part of JSampler. * * JSampler is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * JSampler is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with JSampler; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package org.jsampler; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.util.LinkedList; import java.util.Vector; import java.util.logging.Level; import net.sf.juife.PDUtils; import net.sf.juife.event.GenericEvent; import net.sf.juife.event.GenericListener; import net.sf.juife.event.TaskEvent; import net.sf.juife.event.TaskListener; import org.jsampler.event.LSConsoleEvent; import org.jsampler.event.LSConsoleListener; import org.jsampler.task.LSConsoleConnect; import static org.jsampler.JSI18n.i18n; /** * This class provides default implementation of the LSConsoleModel interface. * @author Grigor Iliev */ public class DefaultLSConsoleModel implements LSConsoleModel { private Socket socket; private LscpOutputStream out; private final String[] cmdList = LscpUtils.getCommandList(); private String cmdLine = ""; /** * Contains the global command history, excluding blank lines and comments. */ private final LinkedList cmdHistory = new LinkedList(); private int cmdHistoryIdx = -1; private int commandHistorySize = 1000; private final LSConsoleThread lsConsoleThread = new LSConsoleThread(); /** * Contains the command history of the current * session, including blank lines and comments. */ private final Vector sessionHistory = new Vector(); /** Used to hold the current command when browsing through command history, etc. */ private String currentCmd = ""; private final Vector listeners = new Vector(); /** Creates a new instance of DefaultLSConsoleModel. */ public DefaultLSConsoleModel() { CC.addReconnectListener(new GenericListener() { public void jobDone(GenericEvent e) { reconnect(); } }); lsConsoleThread.start(); } private Socket getSocket() { return socket; } private void setSocket(Socket socket) { this.socket = socket; } private void reconnect() { final LSConsoleConnect cnt = new LSConsoleConnect(getSocket()); cnt.addTaskListener(new TaskListener() { public void taskPerformed(TaskEvent e) { changeSocket(cnt.getResult()); } }); CC.getTaskQueue().add(cnt); } public void disconnect() { try { if(getSocket() != null) getSocket().close(); } catch(Exception x) { CC.getLogger().info(HF.getErrorMessage(x)); } } private void changeSocket(Socket sock) { setSocket(sock); try { LscpInputStream in; in = sock == null ? null : new LscpInputStream(sock.getInputStream()); out = sock == null ? null : new LscpOutputStream(sock.getOutputStream()); lsConsoleThread.setInputStream(in); } catch(Exception x) { CC.getLogger().log(Level.INFO, HF.getErrorMessage(x), x); } } /** * Registers the specified listener for receiving event messages. * @param l The LSConsoleListener to register. */ @Override public void addLSConsoleListener(LSConsoleListener l) { listeners.add(l); } /** * Removes the specified listener. * @param l The LSConsoleListener to remove. */ @Override public void removeLSConsoleListener(LSConsoleListener l) { listeners.remove(l); } /** Executes the command specified in the command line. */ @Override public void execCommand() { String cmd = getCommandLineText(); sessionHistory.add(cmd); if(cmd.trim().length() > 0 && !cmd.startsWith("#")) addToCommandHistory(cmd); while(cmdHistory.size() > getCommandHistorySize()) cmdHistory.removeFirst(); setCommandLineText(""); currentCmd = ""; cmdHistoryIdx = -1; if( getSocket() == null || getSocket().isClosed() || !getSocket().isConnected() || getSocket().isOutputShutdown() || out == null ) { fireResponseReceived(i18n.getMessage("DefaultLSConsoleModel.notConnected")); } else { CC.getTaskQueue().add(new LSConsoleExecCommand(cmd)); } fireCommandExecuted(); } /** * Gets the last executed command. * @return The last command executed in the LS Console. */ @Override public String getLastExecutedCommand() { int size = sessionHistory.size(); return size == 0 ? "" : sessionHistory.get(size - 1); } /** * Sets the text in the command line. * @param cmdLine The new command line text. */ @Override public void setCommandLineText(String cmdLine) { if(this.cmdLine.equals(cmdLine)) return; String oldCmdLine = this.cmdLine; this.cmdLine = cmdLine; fireCommandLineTextChanged(oldCmdLine); } /** * Gets the text in the command line. * @return The command line's text. */ @Override public String getCommandLineText() { return cmdLine; } /** * Gets the command history of the current session, including blank lines and comments. * @return The command history of the current session, including blank lines and comments. */ @Override public String[] getSessionHistory() { return sessionHistory.toArray(new String[sessionHistory.size()]); } /** * Clears the session history. * @see #getSessionHistory */ @Override public void clearSessionHistory() { sessionHistory.removeAllElements(); } /** * Adds the specified command to command history. * @param command The command to be added to command history. */ @Override public void addToCommandHistory(String command) { cmdHistory.add(command); } /** * Gets the complete command history, excluding blank lines and comments. * @return The complete command history, excluding blank lines and comments. */ @Override public String[] getCommandHistory() { return cmdHistory.toArray(new String[cmdHistory.size()]); } /** * Clears the complete/multisession command history. * @see #getCommandHistory */ @Override public void clearCommandHistory() { cmdHistory.clear(); cmdHistoryIdx = -1; } /** * Determines the maximum number of lines to be kept in the command history. * @return The maximum number of lines to be kept in the command history. */ @Override public int getCommandHistorySize() { return commandHistorySize; } /** * Sets the maximum number of lines to be kept in the command history. * @param size Determines the maximum number of lines to be kept in the command history. */ @Override public void setCommandHistorySize(int size) { commandHistorySize = size; } /** * Gets a list of all LSCP commands. * @return A list of all LSCP commands. */ @Override public String[] getCommandList() { return cmdList; } /** Browses the command history one line up. */ @Override public void browseCommandHistoryUp() { if(cmdHistory.size() == 0) return; if(cmdHistoryIdx == -1) { currentCmd = getCommandLineText(); cmdHistoryIdx = cmdHistory.size() - 1; setCommandLineText(cmdHistory.get(cmdHistoryIdx)); return; } if(cmdHistoryIdx == 0) return; setCommandLineText(cmdHistory.get(--cmdHistoryIdx)); } /** Browses the command history one line down. */ @Override public void browseCommandHistoryDown() { if(cmdHistory.size() == 0 || cmdHistoryIdx == -1) return; if(cmdHistoryIdx == cmdHistory.size() - 1) { cmdHistoryIdx = -1; setCommandLineText(currentCmd); currentCmd = ""; return; } setCommandLineText(cmdHistory.get(++cmdHistoryIdx)); } /** Browses to the first line of the command history. */ @Override public void browseCommandHistoryFirst() { if(cmdHistory.size() == 0) return; cmdHistoryIdx = 0; setCommandLineText(cmdHistory.get(cmdHistoryIdx)); } /** Browses to the last line of the command history. */ @Override public void browseCommandHistoryLast() { if(cmdHistory.size() == 0) return; cmdHistoryIdx = cmdHistory.size() - 1; setCommandLineText(cmdHistory.get(cmdHistoryIdx)); } private Vector tmpVector = new Vector(); /** * Searches the command history for commands * containing the string returned by {@link #getCommandLineText}. * @return All commands that contains the string returned by {@link #getCommandLineText}. * @see #getCommandHistory */ @Override public String[] searchCommandHistory() { return searchCommandHistory(getCommandLineText()); } /** * Searches the command history for commands containing substring. * @param substring The substring to be used to perform the search. * @return All commands that contains substring. * @see #getCommandList */ @Override public String[] searchCommandHistory(String substring) { tmpVector.removeAllElements(); for(String s : cmdHistory) if(s.indexOf(substring) != -1) tmpVector.add(s); return tmpVector.toArray(new String[tmpVector.size()]); } /** * Searches the LSCP command list for commands * containing the string returned by {@link #getCommandLineText}. * @return All commands that contains the string returned by {@link #getCommandLineText}. * @see #getCommandList */ @Override public String[] searchCommandList() { return searchCommandList(getCommandLineText()); } /** * Searches the LSCP command list for commands containing substring. * @param substring The substring to be used to perform the search. * @return All commands that contains substring. * @see #getCommandList */ @Override public String[] searchCommandList(String substring) { tmpVector.removeAllElements(); for(String s : cmdList) if(s.indexOf(substring) != -1) tmpVector.add(s); return tmpVector.toArray(new String[tmpVector.size()]); } public void quit() { disconnect(); } /** Notifies listeners that the text in the command line has changed. */ private void fireCommandLineTextChanged(String oldCmdLine) { LSConsoleEvent e = new LSConsoleEvent(this, null, oldCmdLine); for(LSConsoleListener l : listeners) l.commandLineTextChanged(e); } /** Notifies listeners that the command in the command line has been executed. */ private void fireCommandExecuted() { LSConsoleEvent e = new LSConsoleEvent(this); for(LSConsoleListener l : listeners) l.commandExecuted(e); } /** * Notifies listeners that response is received from LinuxSampler. * @param response The response received from LinuxSampler. */ private void fireResponseReceived(final String response) { PDUtils.runOnUiThread(new Runnable() { public void run() { LSConsoleEvent e = new LSConsoleEvent(this, response); for(LSConsoleListener l : listeners) l.responseReceived(e); } }); } /** Executes LS Console command. */ private class LSConsoleExecCommand extends org.jsampler.task.EnhancedTask { private String cmd; /** Creates a new instance of LSConsoleExecCommand. */ public LSConsoleExecCommand(String cmd) { setTitle("LSConsoleExecCommand_task"); setDescription(i18n.getMessage("LSConsoleExecCommand.description")); this.cmd = cmd; } /** The entry point of the task. */ public void exec() throws Exception { out.writeLine(cmd); } } class LSConsoleThread extends Thread { LscpInputStream in; private boolean terminate = false; LSConsoleThread() {super("LS-Console-Thread"); } @Override public void run() { while(!mustTerminate()) { try { processInput(); } catch(Exception x) { CC.getLogger().log(Level.INFO, HF.getErrorMessage(x), x); } try { synchronized(this) { wait(100); } } catch(Exception x) { CC.getLogger().log(Level.INFO, HF.getErrorMessage(x), x); } } } private synchronized boolean mustTerminate() { return terminate; } public synchronized void terminate() { terminate = true; this.notifyAll(); } /** Processes the input sent by LinuxSampler */ private synchronized void processInput() throws IOException { while(in != null && in.available() > 0) { String response = in.readLine(); fireResponseReceived(response); } } public synchronized void setInputStream(LscpInputStream in) { this.in = in; } } class LscpInputStream { private InputStream in; private StringBuffer buf = new StringBuffer(); /** * Creates a new instance of LscpInputStream. * */ public LscpInputStream(InputStream in) { this.in = in; } /** * Reads a line. * This method is thread safe. * * @return A string containing the next line readed from the stream or * null if the end of the stream has been reached. * @throws IOException If an I/O error occurs. */ public synchronized String readLine() throws IOException { int i; buf.setLength(0); while((i = in.read()) != -1) { if(i == '\r') { checkLF(); break; } buf.append((char)i); } if(i == -1) { if(buf.length() > 0) throw new IOException("Unexpected end of line!"); return null; } return buf.toString(); } /** * Returns the number of bytes that can * be read from this input stream without blocking. * * @return The number of bytes that can * be read from this input stream without blocking. * @throws IOException If an I/O error occurs. */ public synchronized int available() throws IOException { return in.available(); } private void checkLF() throws IOException { int i = in.read(); if(i == -1) throw new IOException("Unexpected end of file!"); if(i != '\n') throw new IOException("Unexpected end of line!"); } } class LscpOutputStream { private OutputStream out; /** Creates a new instance of LscpOutputStream */ public LscpOutputStream(OutputStream out) { this.out = out; } /* * Writes a line. * @param line a string to be written. */ public void writeLine(String line) throws IOException { try { out.write(line.getBytes("US-ASCII")); out.write('\r'); out.write('\n'); out.flush(); } catch(UnsupportedEncodingException x) { CC.getLogger().log(Level.INFO, HF.getErrorMessage(x), x); } } } }