package org.korsakow.ide.ui.controller;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Set;
import javax.swing.BoxLayout;
import javax.swing.JDialog;
import javax.swing.JProgressBar;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.apache.log4j.Logger;
import org.dsrg.soenea.domain.MapperException;
import org.dsrg.soenea.domain.command.CommandException;
import org.dsrg.soenea.uow.UoW;
import org.korsakow.domain.CommandExecutor;
import org.korsakow.domain.command.FindMissingFilesCommand;
import org.korsakow.domain.command.LoadProjectCommand;
import org.korsakow.domain.command.NewProjectCommand;
import org.korsakow.domain.command.Request;
import org.korsakow.domain.command.Response;
import org.korsakow.domain.interf.IMedia;
import org.korsakow.domain.interf.IProject;
import org.korsakow.domain.mapper.input.ProjectInputMapper;
import org.korsakow.domain.mapper.input.ResourceInputMapper;
import org.korsakow.ide.Application;
import org.korsakow.ide.DataRegistry;
import org.korsakow.ide.lang.LanguageBundle;
import org.korsakow.ide.task.AbstractTask;
import org.korsakow.ide.task.TaskException;
import org.korsakow.ide.task.UIWorker;
import org.korsakow.ide.ui.components.tree.FolderNode;
import org.korsakow.ide.ui.components.tree.KNode;
import org.korsakow.ide.ui.components.tree.ResourceNode;
import org.korsakow.ide.ui.controller.action.AbstractAction;
import org.korsakow.ide.ui.controller.action.helper.ProgressDialogStatusListener;
import org.korsakow.ide.ui.dialogs.MissingMediaDialog;
import org.korsakow.ide.ui.resourceexplorer.ResourceTreeTableModel;
import org.korsakow.ide.util.MultiMap;
import org.korsakow.ide.util.StrongReference;
import org.korsakow.ide.util.UIUtil;
import org.korsakow.ide.util.Util;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class ProjectLoader
{
public static void newProject()
{
Logger.getLogger(ProjectLoader.class).info("NewProject");
try {
newProjectImpl();
} catch (Exception e) {
Application.getInstance().showUnhandledErrorDialog("Error", "An unrecoverable error has occurred.", e);
// there's really very little we can do if we cant even reset the system.
System.exit(1);
}
}
public static void loadProject(final File file) throws SAXException, ParserConfigurationException, IOException, SQLException, XPathExpressionException, Throwable
{
loadProject(file, true);
}
public static void loadProject(final File file, final boolean setSaveFile) throws SAXException, ParserConfigurationException, IOException, SQLException, XPathExpressionException, Throwable
{
Logger.getLogger(ProjectLoader.class).info("loadProject:" + file!=null?file.getAbsolutePath():"");
if (!file.exists())
throw new FileNotFoundException(file.getAbsolutePath());
final Application app = Application.getInstance();
try {
app.stopCommonTasks(); // this is necessary because we have these ui components updating all the time and we offload most of the work to another thread
app.clearRegistry();
app.getProjectExplorer().getResourceBrowser().getResourceTreeTable().getTreeTableModel().beginBatchUpdate();
final JDialog progressDialog = new JDialog(Application.getInstance().getProjectExplorer());
progressDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
final JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setIndeterminate(true);
progressDialog.setLayout(new BoxLayout(progressDialog.getContentPane(), BoxLayout.Y_AXIS));
progressDialog.add(progressBar);
progressDialog.setTitle("Loading...");
progressDialog.pack();
progressDialog.setSize(640, progressDialog.getSize().height);
UIUtil.centerOnFrame(progressDialog, app.getProjectExplorer());
progressDialog.setModal(true);
final StrongReference<IProject> projectRef = new StrongReference<IProject>();
final StrongReference<Collection<IMedia>> missingMediaRef = new StrongReference<Collection<IMedia>>();
UIWorker worker = new UIWorker(new AbstractTask() {
@Override
public void runTask() throws TaskException, InterruptedException {
Request request = new Request();
request.set("filename", file.getPath());
Response response;
try {
response = CommandExecutor.executeCommand(LoadProjectCommand.class, request);
} catch (CommandException e) {
throw new TaskException(e);
}
if (response.has("warnings"))
Application.getInstance().showAlertDialog("There were warnings", Util.join((Collection<String>)response.get("warnings"), "\n"));
IProject project = (IProject)response.get("project");
projectRef.set(project);
if (response.has("missingMedia"))
missingMediaRef.set((Collection<IMedia>)response.get("missingMedia"));
final StrongReference<Exception> throwableRef = new StrongReference<Exception>();
UIUtil.runUITaskNow(new Runnable() {
public void run() {
UoW.newCurrent();
Application.getInstance().beginBusyOperation();
try {
Element root = DataRegistry.getHelper().xpathAsElement("/korsakow/resources/Folder[@name=?]", "/");
if (root != null)
importResourceTree(app.getProjectExplorer().getResourceBrowser().getResourceTreeTable().getTreeTableModel(), root);
} catch (XPathExpressionException e) {
throwableRef.set(e);
} finally {
Application.getInstance().endBusyOperation();
}
}
});
if (!throwableRef.isNull())
throw new TaskException(throwableRef.get());
}
});
worker.addPropertyChangeListener(new ProgressDialogStatusListener(progressDialog) {
@Override
public void onDone() {
UoW.newCurrent();
app.notifyKeywordsChanged(); // cause context panes to update
try {
app.notifyProjectLoaded(ProjectInputMapper.find());
} catch (MapperException e) {
Application.getInstance().showUnhandledErrorDialog(e);
}
app.getProjectExplorer();
if (setSaveFile)
app.setSaveFile(file, DataRegistry.getHeadVersion());
if (!missingMediaRef.isNull() && !missingMediaRef.get().isEmpty()) {
Collection<IMedia> missingMedia = missingMediaRef.get();
MissingMediaDialog missingMediaDialog = app.showMissingMediaDialog();
missingMediaDialog.setMessage(LanguageBundle.getString("missingmediadialog.somemediacouldnotbelocated.label"));
for (IMedia medium : missingMedia) {
missingMediaDialog.addMissingItem(medium);
}
missingMediaDialog.setFindMissingAction(new FindMissingAction(projectRef.get().getId(), missingMediaDialog));
missingMediaDialog.expandAll();
missingMediaDialog.setVisible(true);
}
}
@Override
protected void handleException(Throwable e) {
newProject();
}
});
worker.execute();
progressDialog.setVisible(true);
if (worker.getException() != null) {
Throwable e = worker.getException();
if (e instanceof CommandException && e.getCause() != null)
e = e.getCause();
throw e;
}
} finally {
app.startCommonTasks();
app.getProjectExplorer().getResourceBrowser().getResourceTreeTable().getTreeTableModel().endBatchUpdate();
}
}
/**
* This method is not really expected to ever throw. If it does its due to internal failure.
* @throws ParserConfigurationException
* @throws SAXException
* @throws XPathExpressionException
* @throws SQLException
* @throws IOException
* @throws InterruptedException
*/
private static void newProjectImpl() throws ParserConfigurationException, SAXException, XPathExpressionException, SQLException, IOException, InterruptedException
{
Application app = Application.getInstance();
app.stopCommonTasks();
try {
UoW.newCurrent();
app.clearRegistry();
Response response = CommandExecutor.executeCommand(NewProjectCommand.class, new Request());
IProject project = (IProject)response.get("project");
app.notifyKeywordsChanged(); // cause context panes to update
// force snupanel to update now that resources are loaded (since Applications isXXReferenced methods currently incorrectly dont use a command)
app.getProjectExplorer().getResourceBrowser().getResourceTreeTable().getTreeTableModel().fireChanged();
app.getProjectExplorer();
app.setSaveFile(null, DataRegistry.getHeadVersion());
Element root = DataRegistry.getHelper().xpathAsElement("/korsakow/resources/Folder[@name=?]", "/");
if (root != null)
importResourceTree(app.getProjectExplorer().getResourceBrowser().getResourceTreeTable().getTreeTableModel(), root);
} catch (CommandException e) {
Application.getInstance().showUnhandledErrorDialog("Unexpected error", e);
} finally {
app.startCommonTasks();
}
}
private static void importResourceTreeNode(ResourceTreeTableModel model, Element domParent, KNode treeParent)
{
NodeList childList = domParent.getChildNodes();
int childLength = childList.getLength();
for (int i = 0; i < childLength; ++i)
{
if (childList.item(i) instanceof Element == false)
continue;
Element domChild = (Element)childList.item(i);
if (domChild.getTagName().equals("Folder")) {
FolderNode folderNode = new FolderNode(domChild.getAttribute("name"));
model.insertNodeInto(folderNode, treeParent, treeParent.getChildCount());
// recurse
importResourceTreeNode(model, domChild, folderNode);
} else
if (domChild.getTagName().equals("Resource")) {
Long id = null;
try {
id = Long.parseLong(domChild.getAttribute("id"));
} catch (NumberFormatException e) {
Logger.getLogger(Application.class).error("", e);
continue; // dunno what else to do
}
KNode resourceNode = model.findResource(id);
if (resourceNode != null) {
Logger.getLogger(Application.class).error("resource already exist: " + id + "," + domChild.getAttribute("class"), new Exception());
continue;
}
try {
resourceNode = ResourceNode.create( ResourceInputMapper.map( id ) );
} catch (MapperException e) {
Application.getInstance().showUnhandledErrorDialog(e);
continue;
}
model.insertNodeInto(resourceNode, treeParent, treeParent.getChildCount()); // reparent the node at the new location
}
}
}
protected static void importResourceTree(ResourceTreeTableModel model, Element root)
{
importResourceTreeNode(model, root, model.getRoot());
}
private static final class ProgressListener extends ProgressDialogStatusListener
{
public ProgressListener(JDialog progressDialog)
{
super(progressDialog);
}
@Override
protected void onDone() {
}
}
private static class FindMissingAction extends AbstractAction
{
private final long projectId;
private final MissingMediaDialog missingMediaDialog;
public FindMissingAction(long projectId, MissingMediaDialog missingMediaialog)
{
this.projectId = projectId;
missingMediaDialog = missingMediaialog;
}
@Override
public void performAction()
{
Application app = Application.getInstance();
File basePath = app.showDirOpenDialog(missingMediaDialog, null);
if (basePath == null)
return;
Request request = new Request();
request.set("id", projectId);
request.set("basePath", basePath.getAbsolutePath());
request.set("updateUniqueMatches", true);
Response response = new Response();
try {
CommandExecutor.executeCommand(FindMissingFilesCommand.class, request, response);
} catch (CommandException e) {
e.printStackTrace();
}
MultiMap<String, File, Set<File>> possibleMatches = (MultiMap<String, File, Set<File>>)response.get("possibleMatches");
Collection<IMedia> updatedMedia = (Collection<IMedia>)response.get("updatedMedia");
Collection<IMedia> missingMedia = missingMediaDialog.getMissingMedia();
for (IMedia medium : missingMedia) {
String filename = new File(medium.getFilename()).getName();
if (possibleMatches.containsKey(filename))
missingMediaDialog.setPossibleMatches(medium, possibleMatches.get(filename));
}
for (IMedia medium : updatedMedia) {
missingMediaDialog.removeMissingItem(medium);
}
missingMediaDialog.expandAll();
app.showAlertDialog(LanguageBundle.getString("findmissingfiles.xoutofy.title"), LanguageBundle.getString("findmissingfiles.xoutofy.message", updatedMedia.size(), missingMedia.size()));
if (updatedMedia.size() == missingMedia.size()) {
missingMediaDialog.dispose();
}
}
}
}