/*
* @(#)Command.java
*
* This file is part of webCDwriter - Network CD/DVD Writing.
*
* Copyright (C) 1999-2006 Jörg P. M. Haeger
*
* webCDwriter is free software. See CDcreator.java for details.
*/
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
/**
* Execution of a command.
*
* @version 20060425
* @author Jörg P. M. Haeger
*/
public class Command implements Runnable {
final JButton cancelButton = new JButton(i18n("cancel"));
final Object[] cancelOption = { cancelButton };
final String okButtonText = i18n("OK");
final JButton okButton = new JButton(okButtonText);
final Object[] okOption = { okButton };
public static final int INIT = 0;
public static final int RUNNING = 1;
public static final int ERROR = 2;
public static final int SUCCESS = 3;
public int status = INIT;
private Window dialog;
private volatile boolean dialogIsVisible = false;
static Point location = new Point(-1, -1);
private Point locationAtOpen;
private String updateTitle = null;
protected boolean cancel = false, offline = false;
String infoStr;
String lastResult = "unknown";
int writer = -1;
private int openCommands = 0;
protected final ExecSemaphore execSemaphore = new ExecSemaphore();
public Command() {
cancelButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
Version.debug("show", "" + e);
cancelCommand();
}
});
okButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
Version.debug("show", "" + e);
closeDialog();
}
});
infoStr = new String();
}
void cancelCommand() {
if (cancel)
System.exit(0);
cancel = true;
JPanel vp = new JPanel();
vp.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40));
vp.setLayout(new BoxLayout(vp, BoxLayout.Y_AXIS));
vp.add(new JLabel(i18n("waitForCancel")));
vp.add(Box.createVerticalStrut(20));
JProgressBar bar = new JProgressBar();
bar.setIndeterminate(true);
vp.add(bar);
showCentered(vp, i18n("titleWaitForCancel"));
execSemaphore.setDone();
}
protected void closeDialog() {
Version.debug("show", "closeDialog()...");
invokeLater(new Runnable() {
public void run() {
if (dialogIsVisible) {
Point locationAtClose = dialog.getLocation();
if (!locationAtClose.equals(locationAtOpen))
location = locationAtClose;
dialog.setVisible(false);
dialogIsVisible = false;
}
Version.debug("show", "closeDialog()... done");
}
});
execSemaphore.setDone();
}
protected JComponent createFirstDialog() {
JPanel vp = new JPanel();
vp.setLayout(new BoxLayout(vp, BoxLayout.Y_AXIS));
vp.add(Box.createVerticalGlue());
vp.add(new JLabel("Please wait..."));
vp.add(Box.createVerticalGlue());
return vp;
}
public final void exec() throws CommandError {
if (!SwingUtilities.isEventDispatchThread())
Version.debug("FIXME", "Command.exec()");
if (CDcreator.frame == null)
dialog = new JDialog();
else
dialog = new JDialog(CDcreator.frame);
initWindowListener(dialog);
JDialog dialog = (JDialog) this.dialog;
dialog.getContentPane().add(createFirstDialog());
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setModal(true);
dialog.setSize(getPreferredSize());
if (location.x == -1 && location.y == -1)
MainWin.center(dialog);
else
dialog.setLocation(location);
locationAtOpen = dialog.getLocation();
Thread thread = new Thread(this);
thread.start();
int delay = getDialogDelay();
if (delay > 0) {
try {
thread.join(delay);
}
catch (InterruptedException e) {
}
}
if (thread.isAlive() || delay == 0) {
dialogIsVisible = true;
dialog.setVisible(true);
}
try {
thread.join();
}
catch (InterruptedException e) {
}
while (openCommands > 0) {
Version.debug("command", "close " + openCommands);
try {
readResult();
}
catch (java.io.IOException e) {
}
}
CDcreator.setTitle(-1);
if (offline) {
CDcreator.closeMainWin();
return;
}
showResult();
if (status == ERROR)
throw new CommandError();
if (cancel)
throw new CommandError();
}
public final void exec(RootPaneContainer pane) throws CommandError {
final JFrame frame = (JFrame) pane;
int closeOperation = frame.getDefaultCloseOperation();
invokeAndWait(new Runnable() {
public void run() {
}
});
try {
exec2(frame);
}
finally {
frame.setDefaultCloseOperation(closeOperation);
}
}
private void exec2(JFrame frame) throws CommandError {
dialog = frame;
frame.getContentPane().add(createFirstDialog());
frame.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
initWindowListener(frame);
run();
while (openCommands > 0) {
Version.debug("command", "close " + openCommands);
try {
readResult();
}
catch (java.io.IOException e) {
}
}
execSemaphore.setDone(false);
invokeLater(new Runnable() {
public void run() {
CDcreator.setTitle(-1);
if (offline) {
CDcreator.closeMainWin();
return;
}
showResult();
}
});
execSemaphore.untilDone();
if (status == ERROR)
throw new CommandError();
if (cancel)
throw new CommandError();
}
protected void execCommand(String cmdStr) {
try {
status = INIT;
writeCommand(cmdStr);
status = RUNNING;
readResult();
}
catch (java.io.IOException e) {
System.out.println("execCommand: IOException");
lastResult = "error " + e.getLocalizedMessage();
}
}
int getDialogDelay() {
return 200;
}
Dimension getPreferredSize() {
return new Dimension(600, 400);
}
protected void handleWindowEvent(WindowEvent e) {
cancelCommand();
}
String i18n(String key) {
if (key.startsWith("."))
key = getClass().getName() + key;
return CDcreator.i18n(key);
}
private void initWindowListener(Window window) {
WindowListener[] listeners = window.getWindowListeners();
for (int i = 0; i < listeners.length; i++)
window.removeWindowListener(listeners[i]);
window.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
Version.debug("show", "windowClosing");
handleWindowEvent(e);
}
});
}
static void invokeAndWait(Runnable runnable) {
if (SwingUtilities.isEventDispatchThread())
runnable.run();
else
try {
SwingUtilities.invokeAndWait(runnable);
}
catch (Exception e) {
}
}
static void invokeLater(Runnable runnable) {
if (SwingUtilities.isEventDispatchThread())
runnable.run();
else
SwingUtilities.invokeLater(runnable);
}
boolean lastResultOK() {
return status == SUCCESS;
}
protected void pack() {
if (dialog instanceof JDialog)
invokeAndWait(new Runnable() {
public void run() {
dialog.pack();
}
});
}
protected Integer parse(String str, String key) {
try {
StreamTokenizer tokenizer
= new StreamTokenizer(
new StringReader(str));
if (tokenizer.nextToken()
!= StreamTokenizer.TT_WORD)
return null;
if (!tokenizer.sval.equals(key))
return null;
if (tokenizer.nextToken()
!= StreamTokenizer.TT_NUMBER)
return null;
return new Integer((int)tokenizer.nval);
}
catch (IOException e) {
return null;
}
}
protected Integer parse(String str, String key, int arg) {
StringTokenizer t = new StringTokenizer(str, " \t");
try {
String str2 = t.nextToken();
if (!str2.equals(key))
return null;
for (int i = 0; i < arg; i++)
str2 = t.nextToken();
return new Integer(str2);
}
catch (Exception e) {
return null;
}
}
int parsePairStrInt(String lineStr) throws IOException {
StreamTokenizer tokenizer
= new StreamTokenizer(
new StringReader(lineStr));
tokenizer.nextToken();
if (tokenizer.nextToken() != StreamTokenizer.TT_NUMBER)
return 0;
return (int)tokenizer.nval;
}
protected void preloadHTMLViewer() {
Version.debug("show", "preloadHTMLViewer()...");
invokeAndWait(new Runnable() {
public void run() {
new JLabel("Preload HTML viewer");
Version.debug("show", "preloadHTMLViewer()... done");
}
});
}
protected void process(int code, String text) {
}
void processHint(String str) {
}
void processInfo(String str) {
if (infoStr.length() > 0)
infoStr += "\n";
infoStr = CDcreator.replaceAll(infoStr, "CD-R(W)", Mode.medium);
infoStr += str;
}
protected boolean processInternal(int code, String text) {
return false;
}
void processLog(String str) {
}
protected String readLine() throws java.io.IOException {
StringBuffer strBuf = new StringBuffer();
while (true) {
int b = CDcreator.inStream.read();
if (b < 0)
break;
if (b == 10)
break;
if (b < 32 && b != '\t')
strBuf.append("(" + String.valueOf(b) + ")");
else
strBuf.append((char) b);
}
String str = new String(strBuf);
Version.debug("inStream", "<- \"" + str + "\"");
return str;
}
void readResult() throws java.io.IOException {
synchronized(CDcreator.inStream) {
readResult2();
}
}
private void readResult2() throws java.io.IOException {
Version.debug("command", "readResult() " + openCommands);
openCommands--;
while (true) {
String line = readLine();
if (line.length() == 0)
break;
writer = -1;
if (line.startsWith("W")) {
int p = line.indexOf(" ");
if (p >= 2) {
try {
writer = Integer.parseInt(line.substring(1, p));
p++;
while (p < line.length() && line.charAt(p) <= 32)
p++;
line = line.substring(p);
}
catch (Exception e) {
}
}
}
String type = line, message = "";
int i = line.indexOf(' ');
if (i >= 0) {
type = line.substring(0, i);
i++;
while (i < line.length() && line.charAt(i) <= 32)
i++;
message = line.substring(i);
}
type = type.toUpperCase();
if (type.equals("ERR")) {
status = ERROR;
lastResult = i18n("error_"
+ message.replace(' ', '_'));
if (lastResult.startsWith("error_")) {
int p0 = message.indexOf("http://");
if (p0 < 0)
lastResult = "\"" + message + "\"";
else {
int p1 = p0 + 1;
while(p1 < message.length()
&& message.charAt(p1) != ' ')
p1++;
String link = message.substring(p0, p1);
lastResult = ""
+ message.substring(0, p0)
+ "
"
+ link + "
"
+ message.substring(p1) + "";
}
}
break;
}
else
if (type.equals("OK")) {
status = SUCCESS;
lastResult = i18n("success_"
+ message.replace(' ', '_'));
break;
}
else
if (type.equals("HINT") && this instanceof ListWriters)
processHint(message);
else
if (type.equals("HINT")) {
final String message0 = message;
invokeAndWait(new Runnable() {
public void run() {
processHint(message0);
}
});
}
else
if (type.equals("INFO"))
processInfo(message);
else
if (type.equals("CDRECORD")
|| type.equals("CDWVERIFY")
|| type.equals("MKISOFS")
|| type.equals("READCD")) {
toLog(message);
processLog(message);
}
else {
try {
final int code = Integer.parseInt(type);
final String text = message;
if (!processInternal(code, text))
invokeAndWait(new Runnable() {
public void run() {
process(code, text);
}
});
}
catch (Exception e) {
}
}
}
}
public static String removeEscChars(String str) {
char str2[] = new char[str.length()];
int i, j;
for (i = 0, j = 0; i < str.length(); j++) {
if (str.charAt(i) == '%') {
i++;
int digits = 2;
if (str.charAt(i) == 'u') {
digits = 4;
i++;
}
try {
str2[j] = (char) Integer.parseInt(
str.substring(i, i + digits), 16);
}
catch (NumberFormatException e) {
str2[j] = '_';
}
i += digits;
}
else
str2[j] = str.charAt(i++);
}
return new String(str2, 0, j);
}
protected void resetOpenCommands() {
openCommands = 0;
}
public void run() {
closeDialog();
}
protected void setTitle(String title) {
if (!(dialog instanceof JDialog))
return;
final JDialog dialog = (JDialog) this.dialog;
synchronized(dialog) {
if (updateTitle != null) {
updateTitle = title;
return;
}
updateTitle = title;
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
synchronized(dialog) {
dialog.setTitle(Version.product + " - " + updateTitle);
updateTitle = null;
}
}
});
}
protected void show(final JComponent component) {
show(component, "none");
}
protected void show(final JComponent component, final String title) {
Version.debug("show", title + "...");
if (component instanceof JOptionPane) {
JOptionPane pane = (JOptionPane) component;
Object[] options = pane.getOptions();
for (int i = 0; i < options.length; i++) {
if (options[i] instanceof JComponent)
continue;
Version.debug("show", "option: " + options[i]);
options[i] = new JButton(options[i].toString());
((JButton) options[i]).addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
Version.debug("show", "" + e);
closeDialog();
}
});
}
pane.setOptions(options);
}
final RootPaneContainer dialog2 = (RootPaneContainer) dialog;
invokeLater(new Runnable() {
public void run() {
if (!title.equals("none") && dialog instanceof JDialog) {
JDialog dialog3 = (JDialog) dialog;
dialog3.setTitle(Version.product + " - " + title);
}
dialog2.getContentPane().removeAll();
dialog2.getContentPane().add(component, BorderLayout.CENTER);
// dialog.pack();
dialog.validate();
if (dialog.isVisible())
dialog.repaint();
Dimension d = component.getMinimumSize();
Dimension d2 = dialog.getSize();
if (d.height > d2.height) {
d2.height = d.height;
// dialog.setSize(d2);
}
Version.debug("show", title + "... done");
}
});
}
protected void showCentered(JComponent component, String title) {
JPanel vp = new JPanel();
vp.setLayout(new BoxLayout(vp, BoxLayout.Y_AXIS));
vp.add(Box.createVerticalGlue());
vp.add(component);
vp.add(Box.createVerticalGlue());
show(vp, title);
}
public int showConfirmDialog(
Object message, String title, int optionType) {
return JOptionPane.showConfirmDialog(
dialog, message, title, optionType);
}
public int showOptionDialog(
Object message, String title, int optionType,
int messageType, Icon icon,
Object[] options, Object initialValue) {
return JOptionPane.showOptionDialog(
dialog, message, title, optionType,
messageType, icon,
options, initialValue);
}
void showResult() {
execSemaphore.setDone();
}
protected void sleep(int msec) {
try {
Thread.currentThread().sleep(msec);
} catch (InterruptedException e) {
}
}
/**
* replace 0x00 to 0x20 by %00 to %20
*/
public static String spaceToEsc(String str) {
char str2[] = new char[6 * str.length()];
int i, j;
for (i = 0, j = 0; i < str.length(); i++, j++) {
char code = str.charAt(i);
if (code > 0x20 && code < 0x7F && code != '%')
str2[j] = code;
else
if (code < 0x100) {
str2[j++] = '%';
String codeStr = Integer.toHexString(0x100 + code);
str2[j++] = codeStr.charAt(1);
str2[j] = codeStr.charAt(2);
}
else {
str2[j++] = '%';
str2[j++] = 'u';
String codeStr = Integer.toHexString(
0x10000 + (code & 0xFFFF));
str2[j++] = codeStr.charAt(1);
str2[j++] = codeStr.charAt(2);
str2[j++] = codeStr.charAt(3);
str2[j] = codeStr.charAt(4);
}
}
return new String(str2, 0, j);
}
protected void toLog(String str) {
if (writer < 0)
Log.put(str);
else
Log.put(writer + ": " + str);
}
protected void validate() {
invokeAndWait(new Runnable() {
public void run() {
dialog.validate();
dialog.repaint();
}
});
}
protected synchronized void writeCommand(String cmdStr)
throws java.io.IOException {
openCommands++;
cmdStr.replace('\n', ' ');
Version.debug("command", "-> \"" + cmdStr + "\""
+ " " + openCommands);
CDcreator.outStream.writeBytes(cmdStr + '\n');
}
protected void writeCommandIgnoreException(String cmdStr) {
try {
writeCommand(cmdStr);
}
catch (java.io.IOException e) {
}
}
protected void writeHint(String cmdStr) {
writeCommandIgnoreException(cmdStr);
if (openCommands > 0)
openCommands--;
}
class ExecSemaphore {
private boolean done = false;
synchronized void setDone() {
done = true;
notify();
}
synchronized void setDone(boolean done) {
this.done = done;
notify();
}
synchronized void untilDone() {
Version.debug("wait", "start");
while (!done)
try {
wait();
}
catch (InterruptedException e) {
}
Version.debug("wait", "done");
}
}
}