001: /*
002: * Gruntspud
003: *
004: * Copyright (C) 2002 Brett Smith.
005: *
006: * Written by: Brett Smith <t_magicthize@users.sourceforge.net>
007: *
008: * This program is free software; you can redistribute it and/or modify it under
009: * the terms of the GNU Library General Public License as published by the Free
010: * Software Foundation; either version 2 of the License, or (at your option) any
011: * later version. This program is distributed in the hope that it will be
012: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
014: * General Public License for more details.
015: *
016: * You should have received a copy of the GNU Library General Public License
017: * along with this program; if not, write to the Free Software Foundation, Inc.,
018: * 675 Mass Ave, Cambridge, MA 02139, USA.
019: */
020: package gruntspud;
021:
022: import gruntspud.connection.ConnectionProfileModel;
023: import gruntspud.file.FileTypeMapping;
024: import gruntspud.file.FileTypeMappingModel;
025: import gruntspud.filter.CVSFileFilterModel;
026: import gruntspud.project.ProjectListModel;
027: import gruntspud.style.TextStyle;
028: import gruntspud.style.TextStyleModel;
029: import gruntspud.ui.BrowserLauncher;
030: import gruntspud.ui.view.ViewEvent;
031: import gruntspud.ui.view.ViewListener;
032: import gruntspud.ui.view.ViewManager;
033: import java.awt.Color;
034: import java.awt.event.ActionEvent;
035: import java.awt.event.ActionListener;
036: import java.io.File;
037: import java.io.FileInputStream;
038: import java.io.FileOutputStream;
039: import java.io.IOException;
040: import java.io.InputStream;
041: import java.net.MalformedURLException;
042: import java.net.URL;
043: import java.util.Iterator;
044: import java.util.Vector;
045: import org.netbeans.lib.cvsclient.file.FileStatus;
046: import org.netbeans.lib.cvsclient.util.SimpleStringPattern;
047: import plugspud.PluginException;
048: import plugspud.PluginHostContext;
049: import plugspud.PluginManager;
050: import plugspud.PluginVersion;
051:
052: /**
053: * DOCUMENT ME!
054: *
055: * @author $author$
056: */
057: public class DefaultGruntspudContext implements GruntspudContext,
058: ViewListener {
059: /*
060: * Turn on debuggin if working from HEAD or the system property grunts.debug
061: * is set to true. This may also be enable by the user
062: */
063: private static boolean debugToConsole;
064:
065: private static boolean debug = Gruntspud.APPLICATION_VERSION
066: .equals("HEAD")
067: || "true".equals(System.getProperty("gruntspud.debug"));
068:
069: private static File PLUGIN_DIR = new File(System
070: .getProperty("user.home")
071: + File.separator + ".gruntspud" + File.separator + "plugin");
072:
073: private ConnectionProfileModel connectionProfileModel;
074:
075: private ViewManager viewManager;
076:
077: private GruntspudHost host;
078:
079: private CVSFileFilterModel filterModel;
080:
081: private FileTypeMappingModel fileTypeMappingModel;
082:
083: private PluginManager pluginManager;
084:
085: private Vector optionsTabs;
086:
087: private Encrypter encrypter;
088:
089: private ProjectListModel projectListModel;
090:
091: private File projectsFile;
092:
093: private TextStyleModel textStyleModel;
094:
095: /**
096: * Creates a new DefaultGruntspudContext object.
097: *
098: * @param host DOCUMENT ME!
099: */
100: public DefaultGruntspudContext(GruntspudHost host) {
101: this .host = host;
102: textStyleModel = new TextStyleModel(this );
103: textStyleModel.addStyle(new TextStyle(
104: Constants.OPTIONS_STYLE_GRUNTSPUD,
105: "General console output", Color.blue, true, true));
106: textStyleModel.addStyle(new TextStyle(
107: Constants.OPTIONS_STYLE_ERRORS, "Console errors",
108: Color.red, true, true));
109: textStyleModel.addStyle(new TextStyle(
110: Constants.OPTIONS_STYLE_CVS, "CVS output", Color.red
111: .darker(), true, true));
112: textStyleModel.addStyle(new TextStyle(
113: Constants.OPTIONS_STYLE_CONFLICT_LOCAL,
114: "Conflict (Local)", null, Color.red.darker(), false,
115: true));
116: textStyleModel.addStyle(new TextStyle(
117: Constants.OPTIONS_STYLE_CONFLICT_REMOTE,
118: "Conflict (Remote)", null, Color.green.darker(), false,
119: true));
120: textStyleModel.addStyle(new TextStyle(
121: Constants.OPTIONS_STYLE_CONFLICT_SEPARATOR,
122: "Conflict (Separator)", null, Color.blue.darker(),
123: false, true));
124: // Add the change status styles
125: for (Iterator i = CVSChangeStatus.changeStatus(); i.hasNext();) {
126: CVSChangeStatus cs = (CVSChangeStatus) i.next();
127: textStyleModel.addStyle(cs.getDefaultStyle());
128: }
129: textStyleModel.addStyle(new TextStyle("file.Unreadable",
130: "File is unreadable", Color.red, null, true, false));
131: textStyleModel.addStyle(new TextStyle("folder.Plain", "Folder",
132: null, null, true, false));
133: textStyleModel.addStyle(new TextStyle("folder.Project",
134: "Folder is project", null, null, true, false));
135: textStyleModel.addStyle(new TextStyle("folder.Module",
136: "Folder is module", null, null, true, false));
137: textStyleModel.addStyle(new TextStyle(
138: "folder.RequiresAttention",
139: "Folder requires attention", null, null, true, false));
140: // Add the local file status styles
141: textStyleModel.addStyle(new TextStyle("status."
142: + FileStatus.ADDED.toString(), "Status (Added)", null,
143: null, false, false));
144: textStyleModel.addStyle(new TextStyle("status."
145: + FileStatus.HAS_CONFLICTS.toString(),
146: "Status (Conflicts)", Color.white, Color.red, true,
147: false));
148: textStyleModel.addStyle(new TextStyle("status."
149: + FileStatus.MODIFIED.toString(), "Status (Modified)",
150: null, null, false, false));
151: textStyleModel.addStyle(new TextStyle("status."
152: + FileStatus.NEEDS_CHECKOUT.toString(),
153: "Status (Needs Checkout)", null, null, false, false));
154: textStyleModel.addStyle(new TextStyle("status."
155: + FileStatus.NEEDS_MERGE.toString(),
156: "Status (Needs Merge)", null, null, false, false));
157: textStyleModel.addStyle(new TextStyle("status."
158: + FileStatus.NEEDS_PATCH.toString(),
159: "Status (Needs Patch)", null, null, false, false));
160: textStyleModel.addStyle(new TextStyle("status."
161: + FileStatus.REMOVED.toString(), "Status (Removed)",
162: null, null, false, false));
163: textStyleModel.addStyle(new TextStyle("status."
164: + FileStatus.UNKNOWN.toString(), "Status (Unknown)",
165: null, null, false, false));
166: textStyleModel.addStyle(new TextStyle("status."
167: + FileStatus.UP_TO_DATE.toString(),
168: "Status (Up-To-Date)", null, null, false, false));
169: textStyleModel.addStyle(new TextStyle("status.Erased",
170: "Status (Erased)", Color.white, Color.orange.darker(),
171: true, false));
172: // Options tabs
173: optionsTabs = new Vector();
174: // Filter model
175: filterModel = new CVSFileFilterModel(this );
176: // Connection profile
177: connectionProfileModel = new ConnectionProfileModel(this );
178: // Projects
179: projectListModel = new ProjectListModel(this );
180: projectsFile = GruntspudUtil.getPreferenceFile("projects.xml",
181: false);
182: if (projectsFile.exists()) {
183: InputStream in = null;
184: try {
185: in = new FileInputStream(projectsFile);
186: projectListModel.load(in);
187: } catch (Exception e) {
188: Constants.SYSTEM_LOG.error("Could not load projects.",
189: e);
190: } finally {
191: GruntspudUtil.closeStream(in);
192: }
193: } else {
194: FileOutputStream out = null;
195: try {
196: out = new FileOutputStream(projectsFile);
197: projectListModel.save(out);
198: } catch (IOException ioe) {
199: Constants.SYSTEM_LOG.error("Could not create default.",
200: ioe);
201: } finally {
202: GruntspudUtil.closeStream(out);
203: }
204: }
205: // View manager
206: viewManager = new ViewManager();
207: viewManager.addViewListener(this );
208: // File type mapping model
209: fileTypeMappingModel = new FileTypeMappingModel(this );
210: // Create the plugin manager
211: try {
212: pluginManager = new PluginManager();
213: pluginManager.init(this );
214: } catch (PluginException pe) {
215: Constants.PLUGIN_LOG.error(
216: "Could not initialise plugin manager", pe);
217: }
218: // Debug mode
219: if (!debug
220: && host.getBooleanProperty(
221: Constants.OPTIONS_SYSTEM_DEBUG, false)) {
222: debug = true;
223: }
224: debugToConsole = host.getBooleanProperty(
225: Constants.OPTIONS_SYSTEM_DEBUG_TO_CONSOLE, false);
226: //
227: registerOptionsTab(gruntspud.ui.preferences.ConnectionOptionTab.class);
228: registerOptionsTab(gruntspud.ui.preferences.GlobalOptionsTab.class);
229: registerOptionsTab(gruntspud.ui.preferences.PasswordManagerOptionsTab.class);
230: registerOptionsTab(gruntspud.ui.preferences.MappingOptionsTab.class);
231: registerOptionsTab(gruntspud.style.TextStyleOptionsTab.class);
232: registerOptionsTab(gruntspud.ui.preferences.DisplayOptionsTab.class);
233: registerOptionsTab(gruntspud.ui.preferences.ToolBarOptionsTab.class);
234: registerOptionsTab(gruntspud.ui.preferences.SystemOptionsTab.class);
235: registerOptionsTab(gruntspud.ui.preferences.FilterOptionsTab.class);
236: registerOptionsTab(gruntspud.plugin.PluginManagerOptionsTab.class);
237: }
238:
239: /*
240: * Plugspud stuff
241: */
242: public File getPluginDirectory() {
243: return PLUGIN_DIR;
244: }
245:
246: public String getPluginHostName() {
247: return host.getName();
248: }
249:
250: public PluginVersion getPluginHostVersion() {
251: String v = VersionInfo.getVersion();
252: if (v.equals("HEAD")) {
253: return new PluginVersion(999, 999, 999, "head");
254: } else {
255: return new PluginVersion(v.substring(1));
256: }
257: }
258:
259: public URL getStandardPluginsResource() {
260: return getClass().getClassLoader().getResource(
261: "resources/plugins.properties");
262: }
263:
264: public URL getPluginUpdatesResource() {
265: try {
266: return new URL(host.getProperty(
267: Constants.OPTIONS_SYSTEM_PLUGIN_UPDATES_URL,
268: "http://gruntspud.sourceforge.net/plugins"));
269: } catch (MalformedURLException murle) {
270: return null;
271: }
272: }
273:
274: public void openURL(URL url) throws IOException {
275: host.viewHTML(url);
276: }
277:
278: public void log(int d, String mesg) {
279: switch (d) {
280: case PluginHostContext.LOG_ERROR:
281: Constants.PLUGIN_LOG.error(mesg);
282: break;
283: case PluginHostContext.LOG_INFORMATION:
284: Constants.PLUGIN_LOG.info(mesg);
285: break;
286: case PluginHostContext.LOG_WARNING:
287: Constants.PLUGIN_LOG.warn(mesg);
288: break;
289: default:
290: Constants.PLUGIN_LOG.debug(mesg);
291: break;
292: }
293: }
294:
295: public void log(int d, String mesg, Throwable throwable) {
296: switch (d) {
297: case PluginHostContext.LOG_ERROR:
298: Constants.PLUGIN_LOG.error(mesg, throwable);
299: break;
300: case PluginHostContext.LOG_INFORMATION:
301: Constants.PLUGIN_LOG.info(mesg, throwable);
302: break;
303: case PluginHostContext.LOG_WARNING:
304: Constants.PLUGIN_LOG.warn(mesg, throwable);
305: break;
306: default:
307: Constants.PLUGIN_LOG.debug(mesg, throwable);
308: break;
309: }
310: }
311:
312: public void log(int d, Throwable throwable) {
313: switch (d) {
314: case PluginHostContext.LOG_ERROR:
315: Constants.PLUGIN_LOG.error(throwable);
316: break;
317: case PluginHostContext.LOG_INFORMATION:
318: Constants.PLUGIN_LOG.info(throwable);
319: break;
320: case PluginHostContext.LOG_WARNING:
321: Constants.PLUGIN_LOG.warn(throwable);
322: break;
323: default:
324: Constants.PLUGIN_LOG.debug(throwable);
325: break;
326: }
327: }
328:
329: public void putPreference(String key, String val) {
330: host.setProperty(key, val);
331: }
332:
333: public String getPreference(String key, String def) {
334: return host.getProperty(key, def);
335: }
336:
337: /**
338: * Implement to register an <code>Encrypter</code> implementation. The
339: * password manager may use this to encrypt the password list. Only one
340: * implementation can be registered, subsequent attempts will thrown an an
341: * exception.
342: *
343: * @param encrypter the encrypter implementation
344: */
345: public void setEncrypter(Encrypter encrypter) throws Exception {
346: if (encrypter == null) {
347: throw new Exception("Encrypter cannot be null");
348: }
349: if (this .encrypter != null) {
350: throw new Exception("Encrypter " + this .encrypter.getName()
351: + " already registered");
352: }
353: this .encrypter = encrypter;
354: Constants.SYSTEM_LOG.info("Encrypter '" + encrypter.getName()
355: + "' registered");
356: encrypter.init(this );
357: }
358:
359: /**
360: * Implement to return the current <code>Encrypter</code>, or
361: * <code>null</code> if none is registered.
362: *
363: * @return the encrypter
364: */
365: public Encrypter getEncrypter() {
366: return encrypter;
367: }
368:
369: /**
370: * Register option tab. Plugins may use this to place a tab in the options
371: * pane. The class must extend <code>AbstractOptionsTab</code> and have an
372: * empty constructor to be able to be registered. An new instance of the class
373: * is created whenever the options dialog is opened.
374: *
375: * @param optionsTabClass class of options tab.
376: */
377: public void registerOptionsTab(Class optionsTabClass) {
378: optionsTabs.addElement(optionsTabClass);
379: }
380:
381: /**
382: * Return an iterator of Class of all registered options tab classes
383: *
384: * @return registered options tab classes
385: */
386: public Iterator optionsTabs() {
387: return optionsTabs.iterator();
388: }
389:
390: /**
391: * Return the plugin manager
392: *
393: */
394: public PluginManager getPluginManager() {
395: return pluginManager;
396: }
397:
398: /**
399: * Return if debugging is turned on
400: *
401: */
402: public boolean isDebugEnabled() {
403: return debug;
404: }
405:
406: /**
407: * DOCUMENT ME!
408: */
409: public void cleanUp() {
410: viewManager.removeViewListener(this );
411: viewManager.cleanUp();
412: filterModel.cleanUp();
413: FileOutputStream out = null;
414: try {
415: out = new FileOutputStream(projectsFile);
416: projectListModel.save(out);
417: } catch (IOException ioe) {
418: Constants.SYSTEM_LOG.error("Could not save projects.", ioe);
419: } finally {
420: GruntspudUtil.closeStream(out);
421: }
422: fileTypeMappingModel.cleanUp();
423: textStyleModel.saveStyles();
424: pluginManager.stop();
425: }
426:
427: /**
428: * DOCUMENT ME!
429: *
430: * @param evt DOCUMENT ME!
431: */
432: public void viewEvent(ViewEvent evt) {
433: }
434:
435: /**
436: * DOCUMENT ME!
437: *
438: * @return DOCUMENT ME!
439: */
440: public CVSFileFilterModel getFilterModel() {
441: return filterModel;
442: }
443:
444: /**
445: * DOCUMENT ME!
446: *
447: * @return DOCUMENT ME!
448: */
449: public FileTypeMappingModel getFileTypeMappingModel() {
450: return fileTypeMappingModel;
451: }
452:
453: /**
454: * DOCUMENT ME!
455: *
456: * @param node DOCUMENT ME!
457: * @param action DOCUMENT ME!
458: */
459: public void openNode(final CVSFileNode node, int action) {
460: if (node.getFile().exists()) {
461: FileTypeMapping mapping = null;
462: if (action == FileTypeMapping.OPEN_USING_DEFAULT) {
463: // How do we open the file?
464: for (int i = 0; (i < fileTypeMappingModel.getRowCount())
465: && (mapping == null); i++) {
466: FileTypeMapping f = fileTypeMappingModel
467: .getMappingAt(i);
468: for (int j = 0; (j < f.getPatternCount())
469: && (mapping == null); j++) {
470: SimpleStringPattern p = f.getPatternAt(j);
471: if (p.doesMatch(node.getFile().getName())) {
472: mapping = f;
473: }
474: }
475: }
476: action = (mapping == null) ? FileTypeMapping.DISABLE
477: : mapping.getAction();
478: }
479: if (action != FileTypeMapping.DISABLE) {
480: // If there is no mapping or the mapping specified open with host
481: try {
482: if (action == FileTypeMapping.OPEN_USING_HOST) {
483: getHost().openNode(node);
484: } else if (action == FileTypeMapping.OPEN_USING_HTML_VIEWER) {
485: host.viewHTML(node.getFile().toURL());
486: } else if (action == FileTypeMapping.OPEN_USING_OS) {
487: BrowserLauncher.openURL(node.getFile().toURL()
488: .toExternalForm());
489: } else if ((action == FileTypeMapping.OPEN_USING_APPLICATION)
490: && (mapping != null)) {
491: runApplicationForFile(mapping.getApplication(),
492: node.getFile());
493: }
494: } catch (Exception e) {
495: Constants.SYSTEM_LOG.error(
496: "Could not execute command", e);
497: GruntspudUtil.showErrorMessage(null, "Error", e);
498: }
499: }
500: }
501: }
502:
503: /**
504: * DOCUMENT ME!
505: *
506: * @param application DOCUMENT ME!
507: * @param file DOCUMENT ME!
508: *
509: * @throws IOException DOCUMENT ME!
510: */
511: public void runApplicationForFile(String application,
512: final File file) throws IOException {
513: Process p = null;
514: try {
515: String[] c = StringUtil.splitString(application, ' ', '"',
516: (char) -1);
517: String[] cmd = new String[c.length + 1];
518: System.arraycopy(c, 0, cmd, 0, c.length);
519: File dir = null;
520: File f = new File(cmd[0]);
521: if (f.isAbsolute()) {
522: dir = f.getParentFile();
523: } else {
524: dir = new File(System.getProperty("user.dir"));
525: }
526: cmd[c.length] = file.getAbsolutePath();
527: host.writeToConsole(getTextStyleModel().getStyle(
528: Constants.OPTIONS_STYLE_GRUNTSPUD), "Executing "
529: + StringUtil.stringArrayToString(cmd, ' ') + " ["
530: + dir.getAbsolutePath() + "]");
531: p = Runtime.getRuntime().exec(cmd, null, dir);
532: StreamSink s1 = new StreamSink(p.getInputStream(), false,
533: new ActionListener() {
534: public void actionPerformed(ActionEvent evt) {
535: Constants.SYSTEM_LOG
536: .info("Command finished, updating file "
537: + file.getAbsolutePath());
538: getViewManager().externalFileUpdate(file);
539: }
540: });
541: StreamSink s2 = new StreamSink(p.getErrorStream(), true);
542: s1.start();
543: s2.start();
544: } catch (IOException e2) {
545: Constants.SYSTEM_LOG.error(e2);
546: if (p != null) {
547: p.destroy();
548: }
549: throw e2;
550: }
551: }
552:
553: /**
554: *
555: */
556: public void openFile(File file, int action) {
557: }
558:
559: /**
560: * DOCUMENT ME!
561: *
562: * @return DOCUMENT ME!
563: */
564: public ViewManager getViewManager() {
565: return viewManager;
566: }
567:
568: /**
569: * DOCUMENT ME!
570: *
571: * @return DOCUMENT ME!
572: */
573: public GruntspudHost getHost() {
574: return host;
575: }
576:
577: /**
578: * DOCUMENT ME!
579: *
580: * @return DOCUMENT ME!
581: */
582: public ConnectionProfileModel getConnectionProfileModel() {
583: return connectionProfileModel;
584: }
585:
586: public ProjectListModel getProjectListModel() {
587: return projectListModel;
588: }
589:
590: /**
591: * Implement to return the text style model.
592: *
593: * @return text style mode
594: */
595: public TextStyleModel getTextStyleModel() {
596: return textStyleModel;
597: }
598: }
|