/plugins/ProjectViewer/tags/pv_2_9_1/projectviewer/persist/ProjectPersistenceManager.java

# · Java · 341 lines · 222 code · 48 blank · 71 comment · 31 complexity · 060cc9b5b9e257c0dba916ac0b4848dc MD5 · raw file

  1. /*
  2. * :tabSize=4:indentSize=4:noTabs=false:
  3. * :folding=explicit:collapseFolds=1:
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version 2
  8. * of the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19. package projectviewer.persist;
  20. //{{{ Imports
  21. import java.io.BufferedWriter;
  22. import java.io.File;
  23. import java.io.Writer;
  24. import java.io.InputStream;
  25. import java.io.OutputStream;
  26. import java.io.InputStreamReader;
  27. import java.io.OutputStreamWriter;
  28. import java.io.IOException;
  29. import java.io.Writer;
  30. import java.util.Collection;
  31. import java.util.Enumeration;
  32. import java.util.HashMap;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.Stack;
  36. import javax.swing.SwingUtilities;
  37. import org.xml.sax.Attributes;
  38. import org.xml.sax.InputSource;
  39. import org.xml.sax.XMLReader;
  40. import org.xml.sax.helpers.DefaultHandler;
  41. import org.gjt.sp.jedit.jEdit;
  42. import org.gjt.sp.jedit.GUIUtilities;
  43. import org.gjt.sp.jedit.PluginJAR;
  44. import org.gjt.sp.util.Log;
  45. import common.io.AtomicOutputStream;
  46. import common.threads.WorkerThreadPool;
  47. import common.threads.WorkRequest;
  48. import projectviewer.ProjectPlugin;
  49. import projectviewer.ProjectManager;
  50. import projectviewer.PVActions;
  51. import projectviewer.vpt.VPTNode;
  52. import projectviewer.vpt.VPTProject;
  53. import projectviewer.config.ExtensionManager;
  54. import projectviewer.config.ProjectViewerConfig;
  55. //}}}
  56. /**
  57. * This class takes care of each projects properties. Each project has its
  58. * own config file, that is loaded when the project is activated.
  59. *
  60. * @author Marcelo Vanzin
  61. * @version $Id: ProjectPersistenceManager.java 14824 2009-03-22 04:06:41Z vanza $
  62. */
  63. public final class ProjectPersistenceManager {
  64. private final static String CONFIG_DIR = "projects" + File.separator;
  65. /** Private constructor. No instances! */
  66. private ProjectPersistenceManager() { }
  67. /** The map of handlers based on nome names. */
  68. private static final Map<String,NodeHandler> handlerNames;
  69. /** the map of handlers based on classes. */
  70. private static final HashMap<Class,NodeHandler> handlerClasses;
  71. /** the node handler for projects (cannot be changed). */
  72. private static final NodeHandler projHandler = new ProjectNodeHandler();
  73. private static final ExtensionManager.ManagedService service;
  74. /** static initializer, registers the default handlers. */
  75. static {
  76. handlerNames = new HashMap<String,NodeHandler>();
  77. handlerClasses = new HashMap<Class,NodeHandler>();
  78. service = new MService();
  79. ExtensionManager.getInstance().register(service);
  80. }
  81. //}}}
  82. //{{{ +_registerHandler(NodeHandler)_ : void
  83. /**
  84. * Registers a node handler. The same instance will be used at all times
  85. * to process the data, so make sure this is not a problem with the
  86. * handler's implementation.
  87. */
  88. public static void registerHandler(NodeHandler nh) {
  89. handlerNames.put(nh.getNodeName(), nh);
  90. handlerClasses.put(nh.getNodeClass(), nh);
  91. } //}}}
  92. //{{{ +_load(VPTProject, String)_ : VPTProject
  93. /** Loads a project from the given file name. */
  94. public static VPTProject load(VPTProject p, String file) {
  95. InputStream in = ProjectPlugin.getResourceAsStream(CONFIG_DIR + file);
  96. if (in == null) {
  97. Log.log(Log.WARNING, ProjectPersistenceManager.class, "Cannot read config file " + file);
  98. return null;
  99. }
  100. // OK, let's parse the config file
  101. try {
  102. XMLReader parser = PVActions.newXMLReader(new ProjectHandler(p));
  103. parser.parse(new InputSource(new InputStreamReader(in, "UTF-8")));
  104. } catch (Exception e) {
  105. Log.log(Log.ERROR, ProjectPersistenceManager.class.getName(), e);
  106. return null;
  107. }
  108. p.sortChildren();
  109. return p;
  110. } //}}}
  111. /**
  112. * Creates a runnable task that will save the project's data to the
  113. * given config file.
  114. *
  115. * @param p The project to save.
  116. * @param filename The project config file name.
  117. *
  118. * @return A runnable that can be used to execute the task.
  119. *
  120. * @since PV 3.0.0
  121. */
  122. public static Runnable createSaveTask(VPTProject p, String filename) {
  123. return new IORequest(p, filename);
  124. }
  125. //{{{ -_saveNode(VPTNode, Writer)_ : void
  126. /** recursive method for saving nodes and their children. */
  127. private static void saveNode(VPTNode node, Writer out) throws IOException {
  128. if (node.isProject()) {
  129. projHandler.saveNode(node, out);
  130. for (Enumeration e = node.children(); e.hasMoreElements(); ) {
  131. saveNode((VPTNode)e.nextElement(), out);
  132. }
  133. out.write("</" + projHandler.getNodeName() + ">\n");
  134. } else {
  135. NodeHandler handler = handlerClasses.get(node.getClass());
  136. if (handler != null) {
  137. handler.saveNode(node, out);
  138. if (node.getAllowsChildren() && node.persistChildren()) {
  139. out.write(">\n");
  140. for (Enumeration e = node.children(); e.hasMoreElements(); ) {
  141. saveNode((VPTNode)e.nextElement(), out);
  142. }
  143. out.write("</" + handler.getNodeName() + ">\n");
  144. } else {
  145. out.write(" />\n");
  146. }
  147. } else {
  148. Log.log(Log.WARNING, ProjectPersistenceManager.class,
  149. "No handler found to save node of type: " + node.getClass().getName());
  150. }
  151. }
  152. } //}}}
  153. //{{{ +class _ProjectHandler_
  154. /** Handler to read project configuration files. */
  155. public static final class ProjectHandler extends DefaultHandler {
  156. //{{{ Instance variables
  157. private VPTProject proj;
  158. private VPTNode currNode;
  159. private Stack<String> openNodes;
  160. //}}}
  161. //{{{ +ProjectHandler(VPTProject) : <init>
  162. public ProjectHandler(VPTProject proj) {
  163. this.proj = proj;
  164. this.currNode = proj;
  165. this.openNodes = new Stack<String>();
  166. } //}}}
  167. //{{{ +startElement(String) : void
  168. /** takes care of identifying nodes read from the file. */
  169. public void startElement(String uri, String localName,
  170. String qName, Attributes attrs)
  171. {
  172. if (qName.equals(ProjectNodeHandler.NODE_NAME)) {
  173. projHandler.createNode(attrs, proj);
  174. } else {
  175. NodeHandler nh = handlerNames.get(qName);
  176. if (nh == null) {
  177. Log.log(Log.WARNING,this, "Unknown node: " + qName);
  178. } else {
  179. try {
  180. VPTNode node = nh.createNode(attrs, proj);
  181. if (node != null) {
  182. if (nh.isChild()) {
  183. currNode.add(node);
  184. }
  185. if (nh.hasChildren()) {
  186. currNode = node;
  187. openNodes.push(qName);
  188. }
  189. }
  190. } catch (Exception e) {
  191. Log.log(Log.WARNING, this, "Error loading project node, error follows.");
  192. Log.log(Log.ERROR, this, e);
  193. } catch (NoClassDefFoundError ncde) {
  194. Log.log(Log.WARNING, this, "Error loading project node, error follows.");
  195. Log.log(Log.ERROR, this, ncde);
  196. } catch (ExceptionInInitializerError eiie) {
  197. Log.log(Log.WARNING, this, "Error loading project node, error follows.");
  198. Log.log(Log.ERROR, this, eiie);
  199. }
  200. }
  201. }
  202. } //}}}
  203. //{{{ +endElement(String) : void
  204. /** Handles the closing of a directory element. */
  205. public void endElement(String uri, String localName, String qName) {
  206. if (!openNodes.isEmpty() && qName.equals(openNodes.peek())) {
  207. currNode.sortChildren();
  208. currNode = (VPTNode) currNode.getParent();
  209. openNodes.pop();
  210. }
  211. } //}}}
  212. } //}}}
  213. //{{{ IORequest class
  214. private static class IORequest implements Runnable {
  215. private VPTProject p;
  216. private String fname;
  217. private boolean notify;
  218. private Exception error;
  219. public IORequest(VPTProject p, String fname) {
  220. this.p = p;
  221. this.fname = fname;
  222. this.notify = false;
  223. }
  224. public void run() {
  225. if (!notify) {
  226. doSave();
  227. } else {
  228. doNotify();
  229. }
  230. }
  231. private void doSave() {
  232. AtomicOutputStream aout = null;
  233. try {
  234. synchronized (p) {
  235. aout = new AtomicOutputStream(ProjectPlugin.getResourcePath(CONFIG_DIR + fname));
  236. Writer out = new BufferedWriter(new OutputStreamWriter(aout, "UTF-8"));
  237. ProjectManager.writeXMLHeader("UTF-8", out);
  238. saveNode(p, out);
  239. out.flush();
  240. out.close();
  241. }
  242. } catch (IOException ioe) {
  243. Log.log(Log.ERROR, p, ioe);
  244. notify = true;
  245. error = ioe;
  246. SwingUtilities.invokeLater(this);
  247. } finally {
  248. if (aout != null) {
  249. aout.rollback();
  250. }
  251. }
  252. }
  253. private void doNotify() {
  254. String msg = jEdit.getProperty("projectviewer.error.project_str")
  255. + " '" + p.getName() + "'";
  256. GUIUtilities.error(jEdit.getActiveView(), "projectviewer.error.save",
  257. new Object[] { msg, error.getMessage() });
  258. }
  259. } //}}}
  260. private static class MService implements ExtensionManager.ManagedService
  261. {
  262. public Class getServiceClass()
  263. {
  264. return NodeHandler.class;
  265. }
  266. public String getServiceName()
  267. {
  268. return jEdit.getProperty("projectviewer.extensions.custom_nodes");
  269. }
  270. public void updateExtensions(List<Object> l)
  271. {
  272. handlerClasses.clear();
  273. handlerNames.clear();
  274. registerHandler(new FileNodeHandler());
  275. registerHandler(new FilterNodeHandler());
  276. registerHandler(new DirectoryNodeHandler());
  277. registerHandler(new PropertyNodeHandler());
  278. registerHandler(new OpenFileNodeHandler());
  279. /*
  280. * To maintain compatibility with config files that have
  281. * old VFSFile entries, we add this to the name map, but
  282. * not to the class map, since they'll be translated to
  283. * plain file entries when saving the project data.
  284. */
  285. handlerNames.put("vfsfile", handlerNames.get("file"));
  286. if (l != null && l.size() > 0) {
  287. for (Object o : l) {
  288. registerHandler((NodeHandler)o);
  289. }
  290. }
  291. }
  292. }
  293. }