// Copyright (c) 2006 - 2008, Markus Strauch.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package net.sf.sdedit.editor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.LinkedList;
import net.sf.sdedit.config.Configuration;
import net.sf.sdedit.config.ConfigurationManager;
import net.sf.sdedit.diagram.Diagram;
import net.sf.sdedit.error.DiagramError;
import net.sf.sdedit.error.FatalError;
import net.sf.sdedit.error.SemanticError;
import net.sf.sdedit.text.TextHandler;
/**
* Utility class for drawing diagrams on a separate thread.
*
* @author Markus Strauch
*/
final class Engine extends Thread {
private static final String getFatalErrorDescription(Throwable ex) {
return "A FATAL ERROR has occured: " + ex.getClass().getSimpleName();
}
Engine(Editor editor) {
this.editor = editor;
setName("Diagram-Engine");
setDaemon(true);
stack = new LinkedList<Boolean>();
start();
}
private final Editor editor;
private final LinkedList<Boolean> stack;
/**
* (A)synchronously initiates a new drawing process and returns a reference
* to the diagram. When the processing is asynchronous and the diagram is
* finished, a notification to the calling editor will be sent via
* editor.getUI().setPaintDevice(...) with the paint device belonging to the
* diagram as a parameter.
*
* @param configuration
* the configuration of the diagram to be drawn
* @param syntaxCheckOnly
* @param synchronous
* flag denoting if the diagram generation is to take place
* (synchronously) on the current thread (true) or on a
* background thread (false)
*
* @return a reference to the (not yet finished) diagram
*/
final void render(final Configuration configuration,
final boolean syntaxCheckOnly, final boolean synchronous) {
if (synchronous) {
_render(syntaxCheckOnly);
} else {
synchronized (stack) {
stack.addLast(syntaxCheckOnly);
stack.notify();
}
}
}
private synchronized void _render(final boolean syntaxCheckOnly) {
editor.getUI().leaveFilterMode();
Diagram diagram = editor.getUI().renderDiagram();
// TODO
// This reference can be null, at least when this
// method is not synchronized. Why?
if (diagram == null) {
return;
}
DiagramError error = editor.getUI().getDiagramError();
if (error == null) {
editor.getUI().setErrorStatus(false, "", -1, -1);
if (diagram.getFragmentManager().openFragmentsExist()) {
editor
.getUI()
.setErrorStatus(
true,
"Warning: There are open comments. Use [c:<type> <text>]...[/c]",
-1, -1);
}
int noteNumber = diagram.getNextFreeNoteNumber();
if (noteNumber == 0) {
editor.getUI().setStatus("");
} else {
editor.getUI().setStatus(
"Next note number: " + diagram.getNextFreeNoteNumber());
}
} else {
editor.getUI().setStatus("");
if (error instanceof FatalError) {
FatalError fatal = (FatalError) error;
System.err
.println("********************************************************");
System.err
.println("* *");
System.err
.println("* A FATAL ERROR HAS OCCURED. *");
System.err
.println("* *");
System.err
.println("********************************************************");
error.getCause().printStackTrace();
// cautiously embedding this call into a try/catch-block
try {
handle(diagram, fatal.getCause());
} catch (Throwable t) {
t.printStackTrace();
}
} else {
TextHandler handler = (TextHandler) error.getProvider();
String prefix = "";
if (error instanceof SemanticError) {
prefix = diagram.isThreaded()
&& diagram.getCallerThread() != -1 ? "Thread "
+ diagram.getCallerThread() + ": " : "";
}
editor.getUI().setErrorStatus(false,
prefix + error.getMessage(),
handler.getLineBegin() - 1, handler.getLineEnd());
}
}
if (!syntaxCheckOnly) {
editor.getUI().redraw();
}
}
private void saveLog(File logFile, Throwable exception,
TextHandler textHandler) throws IOException {
FileOutputStream stream = new FileOutputStream(logFile);
try {
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(
stream, ConfigurationManager.getGlobalConfiguration()
.getFileEncoding()));
BufferedReader bufferedReader = new BufferedReader(
new StringReader(textHandler.getText()));
int error = textHandler.getLineNumber();
printWriter.println(exception.getClass().getSimpleName()
+ " has occurred in line " + error + "\n");
int i = 0;
for (;;) {
String line = bufferedReader.readLine();
if (line == null) {
bufferedReader.close();
break;
}
line = line.trim();
if (i == error - 1) {
line = ">>>>>>>>>>>>>> " + line;
}
printWriter.println(line);
i++;
}
printWriter.println("\n\n:::::::::::::::::::::::::::::\n\n");
exception.printStackTrace(printWriter);
printWriter.flush();
printWriter.close();
editor
.getUI()
.errorMessage(
getFatalErrorDescription(exception)
+ "\n\nAn error log file has been saved under \n"
+ logFile.getAbsolutePath()
+ "\n\n"
+ "Please send an e-mail with this file as an attachment to:\n"
+ "sdedit@users.sourceforge.net");
} finally {
stream.close();
}
}
private void handle(Diagram diagram, RuntimeException ex) {
String name = "sdedit-errorlog-" + System.currentTimeMillis();
File errorLogFile = new File(name);
try {
errorLogFile.createNewFile();
} catch (IOException e0) {
try {
errorLogFile = new File(System.getProperty("user.home"), name);
errorLogFile.createNewFile();
} catch (IOException e1) {
errorLogFile = new File(System.getProperty("java.io.tmpdir",
name));
}
}
try {
saveLog(errorLogFile, ex, (TextHandler) diagram.getDataProvider());
} catch (IOException e) {
editor.getUI().errorMessage(
getFatalErrorDescription(ex)
+ "\n\nAn error log file could not be saved.");
}
}
/**
* Draws diagrams submitted by calls to render(). When the diagram is
* finished, it notifies the user interface.
*/
@Override
public void run() {
while (true) {
boolean syntaxCheckOnly;
synchronized (stack) {
while (stack.isEmpty()) {
try {
stack.wait();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
syntaxCheckOnly = stack.removeLast();
stack.clear();
}
_render(syntaxCheckOnly);
}
}
}