/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.project;
import java.awt.event.ActionEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javolution37.javolution.xml.ObjectReader;
import javolution37.javolution.xml.ObjectWriter;
import javolution37.javolution.xml.XmlFormat;
import org.apache.log4j.Logger;
import com.jstatcom.component.StdMessages;
import com.jstatcom.component.TopFrame;
import com.jstatcom.component.TopFrameReference;
import com.jstatcom.io.FileSupport;
/**
* Manages storing and retrieving project data and project states for all
* modules. This class is a Singleton. All modules taking part in the project
* management system must register with this object with the method
* addProjectStateHandler at program start. On deserialization of
* projects, the data is restored and the modules states are set.
*
* @author Markus Kraetzig
*/
public final class ProjectManager {
private static final Logger log = Logger.getLogger(ProjectManager.class);
private final Map addedPackagePrefsMap = new HashMap();
private static final ProjectManager INSTANCE = new ProjectManager();
private final Map handlerMap = new HashMap();
private final Map projectStatesMap = new HashMap();
private AbstractAction serializationAction = null;
private AbstractAction deSerializationAction = null;
private TopFrame topFrame = null;
/**
* Default constructor.
*/
private ProjectManager() {
// not to be invoked
}
/**
* Gets singleton instance of this class.
*
* @return project manager
*/
public static ProjectManager getInstance() {
return INSTANCE;
}
/**
* Gets an abstract action instance that can be reused by components to
* invoke project serialization. For example, it could be used by "Save
* Project" button.
*
* @return abstract action
*/
public AbstractAction getSerializationAction() {
if (serializationAction == null) {
URL url = getClass().getResource("/images/saveprojectas.gif");
Icon icon = null;
if (url != null)
icon = new ImageIcon(url);
serializationAction = new AbstractAction("Save Project As", icon) {
public void actionPerformed(ActionEvent evt) {
ProjectDialog pDialog = ProjectDialog.getInstance();
pDialog.setSaveDialog(true);
pDialog.setVisible(true);
if (pDialog.isCancelled())
return;
ProjectTreeNode node = pDialog.getProjectNode();
if (node == null)
throw new IllegalStateException(
"Project node from dialog was null.");
try {
ProjectManager.getInstance().serializeProject(
node.getProjectFile(), node.getName(),
node.getDescription());
} catch (Throwable ex) {
String msg = "There was an error saving a project to file:\n\""
+ node.getProjectFile().getAbsolutePath()
+ "\".";
log.error(msg, ex);
StdMessages.error(msg + "\nThe error message was:\n\""
+ ex.getMessage()
+ "\"\nPlease see the log for more details.",
"Project Save Error");
}
}
};
serializationAction.putValue(Action.ACCELERATOR_KEY, KeyStroke
.getKeyStroke("control S"));
}
return serializationAction;
}
/**
* Gets an abstract action instance that can be reused by components to
* invoke project deserialization. For example, it could be used by "Open
* Project" button.
*
* @return abstract action
*/
public AbstractAction getDeSerializationAction() {
if (deSerializationAction == null) {
URL url = getClass().getResource("/images/openproject.gif");
Icon icon = null;
if (url != null)
icon = new ImageIcon(url);
deSerializationAction = new AbstractAction("Open Project", icon) {
public void actionPerformed(ActionEvent evt) {
File file = FileSupport.getInstance().selectProjectFile(
TopFrameReference.getTopFrameRef());
if (file == null)
return;
try {
ProjectManager.getInstance().deSerializeProject(file);
} catch (Throwable ex) {
String msg = "There was an error loading a project from file:\n\""
+ file.getAbsolutePath() + "\".";
log.error(msg, ex);
StdMessages.error(msg + "\nThe error message was:\n\""
+ ex.getMessage()
+ "\"\nPlease see the log for more details.",
"Project Load Error");
}
}
};
deSerializationAction.putValue(Action.ACCELERATOR_KEY, KeyStroke
.getKeyStroke("control O"));
}
return deSerializationAction;
}
/**
* Serializes the fully specified project to the file
* currentProjectFile.
*
* @param currentProjectFile
* file to use for project serialization
* @param project
* fully specified Project instance
* @throws IllegalArgumentException
* if (currentProjectFile == null || project == null)
*/
public void setProjectToFile(File currentProjectFile, Project project) {
if (currentProjectFile == null || project == null)
throw new IllegalArgumentException("Argument was null.");
if (topFrame != null)
project.setCurrentModuleFrame(topFrame.getTopModuleByName());
try {
BufferedOutputStream fout = new BufferedOutputStream(
new FileOutputStream(currentProjectFile));
OutputStream out = new GZIPOutputStream(fout);
ObjectWriter objectWriter = new ObjectWriter();
objectWriter.setPackagePrefix("", "com.jstatcom.model");
objectWriter.setPackagePrefix("jstatcom", "com.jstatcom");
for (String nam : addedPackagePrefsMap.keySet())
objectWriter.setPackagePrefix(nam, addedPackagePrefsMap
.get(nam));
objectWriter.write(project, out); // Uses UTF-8
fout.close();
} catch (IOException ivjExc) {
throw new RuntimeException(ivjExc);
}
}
/**
* Adds a package prefix. This can be used to reduce the size of the created
* XML file. For example, in the XML header it will appear as
* xmlns:jstatcom="java:com.jstatcom", where "jstatcom" is
* the name and "com.jstatcom" the package name. The package name will be
* replaced by the name in all classes of that package in the XML file.
*
* The package name "jstatcom" and "com.jstatcom" is reserved and must not
* be used.
*
* @param name
* @param packageName
* @throws IllegalArgumentException
* if null or empty argument or if
* name of packageName has already
* been used.
*/
public void addPackagePrefix(String name, String packageName) {
if (name == null || packageName == null)
throw new IllegalArgumentException("Argument was null.");
if (name.length() == 0 || packageName.length() == 0)
throw new IllegalArgumentException("Argument was empty.");
if (name.equalsIgnoreCase("jstatcom")
|| packageName.equalsIgnoreCase("com.jstatcom"))
throw new IllegalArgumentException(
"Tried to overwrite default package prefix.");
if (addedPackagePrefsMap.containsKey(name))
throw new IllegalArgumentException("Package prefix \"" + name
+ "\" already set.");
if (addedPackagePrefsMap.containsValue(packageName))
throw new IllegalArgumentException("Package prefix for \""
+ packageName + "\" already set.");
addedPackagePrefsMap.put(name, packageName);
}
/**
* Stores the current state as a project to currentProjectFile.
*
* @param currentProjectFile
* the file to store the project to
* @param name
* the name of the project
* @param description
* the project description
* @throws IllegalArgumentException
* if (currentProjectFile == null)
*/
public void serializeProject(File currentProjectFile, String name,
String description) {
if (currentProjectFile == null)
throw new IllegalArgumentException("Argument was null.");
Project project = new Project(name, description);
if (topFrame != null)
project.setCurrentModuleFrame(topFrame.getTopModuleByName());
ListModel listModel = OutputList.getInstance().getModel();
for (int i = 0; i < listModel.getSize(); i++)
project.addOutputData((OutputData) listModel.getElementAt(i));
// data types
ProjectData[] pData = ProjectDataManager.getInstance().getProjectData();
for (int i = 0; i < pData.length; i++)
project.addProjectData(pData[i]);
// project states
for (Iterator iter = handlerMap.values().iterator(); iter.hasNext();) {
ProjectStateHandler element = (ProjectStateHandler) iter.next();
project.addProjectState(element.getState());
}
setProjectToFile(currentProjectFile, project);
ProjectTreeNode node = new ProjectTreeNode(project, currentProjectFile,
true);
ProjectTree.getInstance().addProjectNode(node);
}
/**
* Gets project from currentProjectFile.
*
* @param currentProjectFile
* the file to read the project from
* @return deserialized project
* @throws IllegalArgumentException
* if (currentProjectFile == null)
*/
public Project getProjectFromFile(File currentProjectFile) {
if (currentProjectFile == null)
throw new IllegalArgumentException("Project file was null.");
if (!currentProjectFile.isFile() || !currentProjectFile.canRead())
throw new IllegalArgumentException("Project file "
+ currentProjectFile.getAbsolutePath()
+ " does not exist or is not readable.");
// deserialize project
ObjectReader constructor = new ObjectReader();
Project project = null;
try {
try { // GZIPped is default case, but might fail
InputStream fin = new BufferedInputStream(new FileInputStream(
currentProjectFile));
InputStream in = new GZIPInputStream(fin);
project = (Project) constructor.read(in);
in.close();
} catch (Throwable t) {
// probably no gzip
project = null;
log.warn("GZIP import failed for file \"" + currentProjectFile
+ "\": " + t.getMessage());
}
InputStream fin = new BufferedInputStream(new FileInputStream(
currentProjectFile));
if (project == null) // read uncompressed
project = (Project) constructor.read(fin);
fin.close();
} catch (Throwable t) {
log.error("XML deserialization failed", t);
}
return project;
}
/**
* Restores project from currentProjectFile.
*
* @param currentProjectFile
* the file to read the project from
* @return reference to the newly created project tree node
* @throws IllegalArgumentException
* if (currentProjectFile == null)
*/
public ProjectTreeNode deSerializeProject(File currentProjectFile) {
Project project = getProjectFromFile(currentProjectFile);
ProjectTreeNode node = new ProjectTreeNode(project, currentProjectFile,
true);
ProjectTree.getInstance().addProjectNode(node);
// set data
for (Iterator iter = project.getProjectDataIterator(); iter.hasNext();) {
ProjectData element = (ProjectData) iter.next();
ProjectDataManager.getInstance().setProjectData(element);
}
// set output
OutputList.getInstance().removeAll();
for (Iterator iter = project.getOutputDataIterator(); iter.hasNext();) {
OutputData element = (OutputData) iter.next();
OutputList.getInstance().addOutput(element);
}
// set handler states from retrieved states
for (Iterator iter = project.getProjectStatesIterator(); iter.hasNext();) {
ProjectState element = (ProjectState) iter.next();
ProjectStateHandler handler = handlerMap
.get(element.getHandlerID());
if (handler != null)
try {
handler.setState(element);
} catch (Throwable t) {
log.error("Failed to set state for element: " + element, t);
}
else
projectStatesMap.put(element.getHandlerID(), element);
}
if (topFrame != null) {
String top = topFrame.getTopModuleByName();
String newCurrent = project.getCurrentModuleFrame();
if (!newCurrent.equals(top))
topFrame.setTopModuleByName(newCurrent);
}
return node;
}
/**
* Adds a project state handler, typically a modelling module, to the list
* of all handlers. The state will be stored as part of the project and it
* is restored when the project is deserialized.
*
* This method should be called at program start when all handlers are being
* initialized.
*
* @param handler
* element of a project that hanldles project states, usually a
* ModelFrame
* @throws IllegalArgumentException
* if (handler == null)
*/
public void addProjectStateHandler(ProjectStateHandler handler) {
if (handler == null)
throw new IllegalArgumentException("Argument was null.");
handlerMap.put(handler.getID(), handler);
if (projectStatesMap.containsKey(handler.getID()))
try {
handler.setState(projectStatesMap.get(handler.getID()));
} catch (Throwable t) {
log.error("Failed to set state for handler ID:"
+ handler.getID(), t);
}
}
/**
* Sets the reference to the TopFrame component that can be
* used for (de)serializing projects. It is not strictly required to use
* this setting.
*
* @param topFrame
* reference to the TopFrame instance that is used
* as the main application frame
*/
public void setTopFrame(TopFrame topFrame) {
this.topFrame = topFrame;
}
/**
* Gets the reference to the TopFrame component that has been
* set earlier.
*
* @return reference to the currently set TopFrame instance
* or null if none has been set before
*/
public TopFrame getTopFrame() {
return topFrame;
}
/**
* Adds a classname transformation. This is useful to load serialized XML project files
* after a classname was changed.
*
* @param oldClassName full classname before renaming
* @param newClassName full classname after renaming
*/
public void addClassTransformation(String oldClassName, String newClassName) {
XmlFormat.addClassTransformation(oldClassName, newClassName);
}
}
Ads help maintain this website.