View Javadoc

1   /*
2    * $Id: ProjectView.java,v 1.48 2008/03/05 22:13:43 edankert Exp $
3    *
4    * The contents of this file are subject to the Mozilla Public License 
5    * Version 1.1 (the "License"); you may not use this file except in 
6    * compliance with the License. You may obtain a copy of the License at 
7    * http://www.mozilla.org/MPL/ 
8    *
9    * Software distributed under the License is distributed on an "AS IS" basis, 
10   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
11   * for the specific language governing rights and limitations under the License.
12   *
13   * The Original Code is XML Hammer code. (org.xmlhammer.*)
14   *
15   * The Initial Developer of the Original Code is Edwin Dankert. Portions created 
16   * by the Initial Developer are Copyright (C) 2005 - 2006 the Initial Developer. 
17   * All Rights Reserved.
18   *
19   * Contributor(s): Edwin Dankert <edankert@gmail.com>
20   */
21  
22  package org.xmlhammer.gui;
23  
24  import java.awt.BorderLayout;
25  import java.awt.FlowLayout;
26  import java.awt.Font;
27  import java.awt.event.ActionEvent;
28  import java.awt.event.ActionListener;
29  import java.io.File;
30  import java.net.URI;
31  import java.util.ArrayList;
32  import java.util.Enumeration;
33  import java.util.List;
34  
35  import javax.swing.AbstractButton;
36  import javax.swing.ButtonGroup;
37  import javax.swing.Icon;
38  import javax.swing.ImageIcon;
39  import javax.swing.JButton;
40  import javax.swing.JOptionPane;
41  import javax.swing.JPanel;
42  import javax.swing.JToggleButton;
43  import javax.swing.SwingUtilities;
44  import javax.swing.border.EmptyBorder;
45  import javax.swing.event.AncestorEvent;
46  import javax.swing.event.AncestorListener;
47  import javax.swing.event.ChangeEvent;
48  import javax.swing.event.ChangeListener;
49  import javax.xml.bind.JAXBException;
50  import javax.xml.transform.TransformerException;
51  
52  import org.apache.log4j.Level;
53  import org.apache.log4j.Logger;
54  import org.apache.log4j.PatternLayout;
55  import org.apache.log4j.WriterAppender;
56  import org.bounce.CardPanel;
57  import org.bounce.image.ImageLoader;
58  import org.bounce.util.URIUtils;
59  import org.xmlhammer.Module;
60  import org.xmlhammer.ModuleThread;
61  import org.xmlhammer.ModuleThreadListener;
62  import org.xmlhammer.PreferencesHandler;
63  import org.xmlhammer.ResultModel;
64  import org.xmlhammer.gui.actions.OpenAction;
65  import org.xmlhammer.gui.input.InputPage;
66  import org.xmlhammer.gui.output.ConsolePanel;
67  import org.xmlhammer.gui.output.JavaPanel;
68  import org.xmlhammer.gui.output.LogPanel;
69  import org.xmlhammer.gui.output.ResultPanel;
70  import org.xmlhammer.gui.output.SourceBuilder;
71  import org.xmlhammer.gui.overview.OverviewNode;
72  import org.xmlhammer.gui.overview.OverviewNodeRenderer;
73  import org.xmlhammer.gui.overview.OverviewPanel;
74  import org.xmlhammer.gui.parser.ParserPage;
75  import org.xmlhammer.gui.status.StatusBar;
76  import org.xmlhammer.model.jaxp.SchemaFactoryProperty;
77  import org.xmlhammer.model.jaxp.Settings;
78  import org.xmlhammer.model.project.Filter;
79  import org.xmlhammer.model.project.Project;
80  
81  /***
82   * The UI representation of a Project.
83   * 
84   * @version $Revision: 1.48 $, $Date: 2008/03/05 22:13:43 $
85   * @author Edwin Dankert <edankert@gmail.com>
86   */
87  public abstract class ProjectView extends JPanel implements ModuleThreadListener {
88  	private static final long serialVersionUID = 4121138017445755189L;
89      
90      private AnimatedThread animatedThread = null;
91  
92      private static final ImageIcon ICON = ImageLoader.get().getImage( "/org/xmlhammer/gui/icons/project.gif");
93  
94      private long lastModifiedTime = -1;
95  	private FieldManager fieldManager = null;
96      private ProjectUndoManager changeManager = null;
97  	private boolean selected = false;
98  	protected Project project = null;
99  	private Logger logger = null;
100 
101 	private OverviewPanel overview = null;
102 
103 	private ModuleThread thread = null;
104 	private CardPanel<Page> cardPanel = null;
105 	
106 	protected InputPage inputPage = null;
107     protected ParserPage parserPage = null;
108 
109 	private LogPanel logPanel = null;
110     private ConsolePanel consolePanel = null;
111     private JavaPanel javaPanel = null;
112 	private ResultPanel resultPanel = null;
113     protected StatusBar statusBar = null;
114 	private URI uri = null;
115 	private ProjectsView parent = null;
116     private ButtonGroup pageGroup = null;
117     private JPanel pageButtonsPanel = null;
118 
119 	/***
120 	 * @param parent the projects view parent.
121 	 * @param uri the uri for the project.
122 	 */
123 	public ProjectView(ProjectsView parent, URI uri) {
124 		super(new BorderLayout());
125 		
126 		setName(ProjectView.getName( parent, uri));
127         
128 		this.parent = parent;
129 		this.uri = uri;
130 		
131         if (uri != null) {
132             lastModifiedTime = System.currentTimeMillis();
133         }
134 
135         cardPanel = new CardPanel<Page>();
136         pageGroup = new ButtonGroup();
137 
138 		JPanel main = new JPanel( new BorderLayout( 0, 0));
139 		main.setBorder( new EmptyBorder( 0, 0, 0, 0));
140 		main.add( cardPanel, BorderLayout.CENTER);
141 		
142 		add( main, BorderLayout.CENTER);
143 		
144         JPanel buttonPanel = new JPanel( new BorderLayout());
145         buttonPanel.setBorder( new EmptyBorder( 5, 0, 5, 0));
146         pageButtonsPanel = new JPanel( new FlowLayout());
147         pageGroup = new ButtonGroup();
148 
149         buttonPanel.add( pageButtonsPanel, BorderLayout.WEST);
150 
151         JPanel executeStopPanel = new JPanel( new FlowLayout());
152 
153         JButton executeButton = new JButton( getProjectsView().getRoot().getExecuteAction());
154         executeStopPanel.add( executeButton);
155         JButton stopButton = new JButton( getProjectsView().getRoot().getStopAction());
156         executeStopPanel.add( stopButton);
157 
158         buttonPanel.add( executeStopPanel, BorderLayout.EAST);
159         
160         addAncestorListener(new AncestorListener() {
161             public void ancestorAdded(AncestorEvent event) {
162                 Logger.getLogger(getClass()).debug("ancestorAdded("+event+")");
163                 
164                 checkForUpdate();
165             }
166 
167             public void ancestorRemoved(AncestorEvent event) {}
168             public void ancestorMoved(AncestorEvent event) {}
169         });
170         
171         add( buttonPanel, BorderLayout.SOUTH);
172     }
173 
174     /***
175      * Should be called by child constructor.
176      */
177     protected void initPages() {
178         createPages();
179 
180         getOverviewPanel().expandAll();
181         
182         if (getInputPage() != null) {
183             getOverviewPanel().selectNode( getInputPage());
184         }
185     }
186 	
187 	/***
188 	 * @return the project URI.
189 	 */
190 	public OverviewPanel getOverviewPanel() {
191 		if ( overview == null) {
192 			overview = new OverviewPanel( this);
193 		}
194 		
195 		return overview;
196 	}
197     
198     public void update() {
199         if (uri != null) {
200             parent.getRoot().setWait(true);
201             getStatusBar().setStatus("Updating "+uri.toString()+" ...");
202 
203             // Run in Thread!!!
204             Runnable runner = new Runnable() {
205                 public void run()  {
206                     Project project = null;
207                     
208                     try {
209                         project = OpenAction.open(uri);
210                     } catch ( JAXBException e) {
211     	               	Logger.getLogger(getClass()).error("JAXB Error", e);
212                     } catch (TransformerException e) {
213     	               	Logger.getLogger(getClass()).error("Transformer Error", e);
214                     } finally {
215                         openProject(project);
216                     }
217                 }
218             };
219             
220             // Create and start the thread ...
221             Thread thread = new Thread( runner);
222             thread.start();
223         }
224     }
225     
226     private void openProject(final Project project) {
227         SwingUtilities.invokeLater( new Runnable() {
228             public void run() {
229                 try {
230                     if (project != null) {
231                         setProject( project);
232                     }
233                 } finally {
234                     getStatusBar().setStatus("Done");
235                     parent.getRoot().setWait(false);
236                 }
237             }
238         });
239     }
240 	
241 	/***
242 	 * @return the underlying projects view.
243 	 */
244 	public ProjectsView getProjectsView() {
245 		return parent;
246 	}
247 
248 	/***
249 	 * @return the project URI.
250 	 */
251 	public URI getURI() {
252 		return uri;
253 	}
254     
255     /***
256      * @return the last modified time.
257      */
258     public long getLastModifiedTime() {
259         return lastModifiedTime;
260     }
261     
262     public boolean hasError() {
263         // This method can be called before project view has been initialised.
264         if (overview != null) {
265             return OverviewNodeRenderer.isError(overview.getModel());
266         }
267 
268         return false;
269     }
270 
271     /***
272      * @param the last modified time.
273      */
274     public void setLastModifiedTime(long time) {
275         lastModifiedTime = time;
276     }
277 
278     /***
279      * @param true when the file has been modified on disk.
280      */
281     private boolean isModified() {
282         if (uri != null && lastModifiedTime != -1) {
283             return lastModifiedTime < (new File(uri)).lastModified();
284         }
285         
286         return false;
287     }
288     
289     public void checkForUpdate() {
290         if (isModified()) {
291             lastModifiedTime = System.currentTimeMillis();
292 
293             int result = JOptionPane.showConfirmDialog(parent.getRoot(), "The \""+getName()+"\" Project has been changed by an external process.\nUpdate the Project?", "Please Confirm", JOptionPane.YES_NO_OPTION);
294             if (result == JOptionPane.OK_OPTION) {
295                 update();
296             }
297         }
298     }
299 
300     /***
301      * @param the view's URI.
302      */
303     public void setURI(URI uri) {
304         this.uri = uri;
305 
306         setName( ProjectView.getName( parent, uri));
307     }
308     
309     /***
310      * Override when want to add new pages overridden.
311      */
312     protected abstract void createPages();
313 	
314 	protected ParserPage createParserPage() {
315 		parserPage = new ParserPage( this, true);
316 		
317 		return parserPage;
318 	}
319 	
320     protected ParserPage getParserPage() {
321         return parserPage;
322     }
323 
324     /***
325 	 * @param page the page to add.
326 	 */
327 	public void addPage( Page page) {
328 		cardPanel.add( page);
329         getOverviewPanel().addPage( page);
330         addPageButton( page);
331 	}
332     
333     private void addPageButton( final Page page) {
334       JToggleButton pageButton = new JToggleButton( page.getShortName());
335       pageButton.setFont( pageButton.getFont().deriveFont(Font.PLAIN));
336       pageButton.addActionListener( new ActionListener() {
337           public void actionPerformed(ActionEvent arg0) {
338               page.getProjectView().getOverviewPanel().selectNode( page);
339           }
340       });
341       pageGroup.add( pageButton);
342       pageButtonsPanel.add( pageButton);
343     }
344 
345     protected InputPage createInputPage() {
346         inputPage = new InputPage(this);
347 
348         return inputPage;
349     }
350 
351     protected InputPage getInputPage() {
352 		return inputPage;
353 	}
354     
355     /***
356      * Returns the status bar.
357      * 
358      * @return the status bar.
359      */
360     public StatusBar getStatusBar() {
361         if ( statusBar  == null) {
362             statusBar = new StatusBar();
363         }
364         
365         return statusBar;
366     }
367 
368 
369 	/***
370 	 * @return the ResultPanel
371 	 */
372 	public ResultPanel getResultPanel() {
373 		if ( resultPanel == null) {
374 			resultPanel = new ResultPanel(parent.getRoot());
375 		}
376 		
377 		return resultPanel;
378 	}
379 
380     /***
381      * @return the ResultPanel
382      */
383     public JavaPanel getJavaPanel() {
384         if ( javaPanel == null) {
385             javaPanel = new JavaPanel();
386             javaPanel.addAncestorListener(new AncestorListener() {
387                 public void ancestorAdded(AncestorEvent event) {
388                     Logger.getLogger(getClass()).debug("ancestorAdded("+event+")");
389                     getJavaPanel().setSource(getSource());
390                 }
391 
392                 public void ancestorRemoved(AncestorEvent event) {
393                     Logger.getLogger(getClass()).debug("ancestorRemoved("+event+")");
394                 }
395 
396                 public void ancestorMoved(AncestorEvent event) {
397                     Logger.getLogger(getClass()).debug("ancestorMoved("+event+")");
398                 }
399             });
400         }
401         
402         return javaPanel;
403     }
404 
405     /***
406 	 * @return the LogPanel
407 	 */
408 	public LogPanel getLogPanel() {
409 		if (logPanel == null) {
410 			logPanel = new LogPanel();
411 		}
412 		
413 		return logPanel;
414 	}
415 
416     /***
417      * @return the LogPanel
418      */
419     public ConsolePanel getConsolePanel() {
420         if (consolePanel == null) {
421             consolePanel = new ConsolePanel();
422         }
423         
424         return consolePanel;
425     }
426 
427     /***
428      * @return the underlying project.
429      */
430     public Project getProject() {
431         if ( project == null) {
432             project = new Project();
433         }
434         
435         return project;
436     }
437 
438     /***
439 	 * @return the underlying project, setting input and parser values .
440 	 */
441 	public Project getProject(URI base) {
442 		if ( project == null) {
443 			project = new Project();
444 		}
445 
446         if (getParserPage() != null && getInputPage() != null) {
447     		project.setInput( getInputPage().getInput(base));
448     		project.setParser( getParserPage().getParser(base));
449         }
450 		
451 		return project;
452 	}
453 	
454 	/***
455 	 * @return the project Specific Logger.
456 	 */
457 	public Logger getLogger() {
458 		if ( logger == null) {
459             logger = Logger.getLogger( getClass().getName() + "@" + Integer.toHexString(hashCode()));
460             logger.setLevel(Level.INFO);
461             logger.addAppender( new WriterAppender( new PatternLayout("%m%n"), getLogPanel().getOutputStream()));
462 		}
463 
464 		return logger;
465 	}
466     
467     public void firePreferencesUpdated() {
468         updateSchemaLanguages();
469     }
470 
471     protected void updateSchemaLanguages() {
472         Logger.getLogger(getClass()).debug("updateSchemaLanguages()");
473         
474         Settings settings = project.getJAXPSettings();
475         ArrayList<String> languages = new ArrayList<String>();
476 
477         if ( settings.getJAXPSchemaFactory().getSchemaFactoryProperties() != null) {
478             List<SchemaFactoryProperty> properties = settings.getJAXPSchemaFactory().getSchemaFactoryProperties().getSchemaFactoryProperty();
479             for ( SchemaFactoryProperty property : properties) {
480                 languages.add( property.getLanguage());
481             }
482         } else {
483             settings = PreferencesHandler.getInstance().getPreferences().getJAXPSettings();
484 
485             List<SchemaFactoryProperty> properties = settings.getJAXPSchemaFactory().getSchemaFactoryProperties().getSchemaFactoryProperty();
486             for ( SchemaFactoryProperty property : properties) {
487                 languages.add( property.getLanguage());
488             }
489         }
490 
491         getParserPage().setSchemaLanguages(languages);
492     }
493 
494 	/***
495 	 * Show the properties dialog.
496 	 */
497 	public abstract void showPropertiesDialog();
498 
499 	/***
500 	 * @return the module.
501 	 */
502 	public abstract Module getModule();
503 
504 	/***
505 	 * @param thread the currently active thread that runs the project.
506 	 */
507     public void setModuleThread(ModuleThread thread) {
508         if ( this.thread != null) {
509             this.thread.removeListener( this);
510         }
511         
512         this.thread = thread;
513 
514         if (thread != null) {
515             thread.addListener(this);
516         } else {
517             animatedThread.setRunning(false);
518             animatedThread = null;
519             thread = null;
520         }
521     }
522     
523     public Page getSelectedPage() {
524         return cardPanel.selected();
525     }
526 
527     public void threadFinished(ModuleThread thread) {
528         getConsolePanel().stop(thread);
529 
530         animatedThread.setRunning(false);
531         animatedThread = null;
532 
533         this.thread.removeListener( this);
534         this.thread = null;
535     }
536 
537     public void threadStarted( ModuleThread thread) {
538         getConsolePanel().start(thread);
539 
540         animatedThread = getProjectsView().createAnimatedThread(this);
541         animatedThread.start();
542     }
543 
544     /***
545 	 * @return the currently active thread running the project.
546 	 */
547     public ModuleThread getModuleThread() {
548         return thread;
549     }
550 
551     /***
552      * @return the result model.
553      */
554     public ResultModel getResult() {
555         URI dir = null;
556 
557         if (getInputPage() != null) {
558             Filter filter = getInputPage().getInput(null).getFilter();
559             if ( filter != null) {
560                 dir = URIUtils.createURI( filter.getDir());
561             }
562     
563             return getResultPanel().createModel( dir);
564         }
565         
566         return null;
567 	}
568     
569     /***
570      * @param changed updated when any of the project content has changed or when the 
571      *        project has been saved.
572      */
573 	public void setChanged( boolean changed) {
574 		notifyChange( changed);
575 	}
576 
577     public abstract SourceBuilder getSource();
578     
579 	/***
580 	 * @return true when the project content has changed and needs to be saved.
581 	 */
582 	public boolean isChanged() {
583         if (uri == null) {
584             return true;
585         }
586 
587         return getFieldManager().isChanged();
588 	}
589 	
590 	/***
591 	 * @param changed true when the content has changed.
592 	 */
593 	public void notifyChange( boolean changed) {
594 		parent.notifyChange( this);
595 	}
596 
597 	/***
598 	 * @param selected The selected to set.
599 	 */
600 	public void setSelected( boolean selected) {
601 		this.selected = selected;
602 	}
603 
604 	/***
605 	 * @return Returns the selected.
606 	 */
607 	public boolean isSelected() {
608 		return selected;
609 	}
610 	
611 	/***
612 	 * @pre the field manager does not have to exist yet.
613 	 * @post field manager created.
614 	 * 
615 	 * @return the manager for the fields.
616 	 */
617 	public FieldManager getFieldManager() {
618 		if ( fieldManager == null) {
619 			fieldManager =  new FieldManager( this);
620 		}
621 		
622 		return fieldManager;
623 	}
624 
625     /***
626      * @pre the field manager does not have to exist yet.
627      * @post field manager created.
628      * 
629      * @return the manager for the fields.
630      */
631     public ProjectUndoManager getUndoManager() {
632         if ( changeManager == null) {
633             changeManager =  new ProjectUndoManager();
634             changeManager.addChangeListener(new ChangeListener() {
635                 public void stateChanged(ChangeEvent arg0) {
636                     if (getJavaPanel().isDisplayable()) {
637                         // make sure this happens after everything else!
638                         SwingUtilities.invokeLater(new Runnable() {
639                             public void run() {
640                                 getJavaPanel().setSource(getSource());
641                             }
642                         });
643                     }
644                 }
645             });
646         }
647         
648         return changeManager;
649     }
650 
651 	/***
652 	 * Set the project.
653 	 * 
654 	 * @param project the project.
655 	 */
656 	public void setProject( Project project) {
657         this.project = project;
658 
659 		updateSchemaLanguages();
660 
661         if (getParserPage() != null && getInputPage() != null) {
662 			if ( project != null) {
663 				getInputPage().setInput(getURI(), project.getInput());
664 				getParserPage().setParser(getURI(), project.getParser());
665 			} else {
666 				getInputPage().setInput(getURI(), null);
667 				getParserPage().setParser(getURI(), null);
668 			}
669             
670             getOverviewPanel().selectNode(getInputPage());
671             getOverviewPanel().expandAll();
672         }
673 		
674         getFieldManager().setChanged(false);
675         getUndoManager().discardAllEdits();
676 	}
677 
678 	private static String getName( ProjectsView parent, URI uri) {
679 		if ( uri != null) {
680 			return URIUtils.getName( uri);
681 		}
682 
683         return parent.getNewProjectName();
684 	}
685     
686 	/***
687 	 * @param node the panel to show.
688 	 */
689 	public void show( OverviewNode node) {
690         if (node != null) {
691     		while (node.getParentNode() != getOverviewPanel().getModel().getRoot()) {
692     			node = node.getParentNode();
693     		}
694     		
695             if (!(cardPanel.selected() == node)) {
696                 cardPanel.show((Page)node);
697                 selectPageButton((Page)node); 
698             }
699             
700             getProjectsView().getRoot().getHelpPanel().showID(((Page)node).getHelpID());
701         }
702 	}
703     
704     private void selectPageButton( Page page) {
705         Enumeration<AbstractButton> buttons = pageGroup.getElements();
706 
707         while ( buttons.hasMoreElements()) {
708             AbstractButton button = buttons.nextElement();
709             if ( button.getText().equals(page.getShortName())) {
710                 button.setSelected( true);
711             }
712         }
713     }
714     
715     public Icon getIcon() {
716         return ICON;
717     }
718     
719     public abstract void dispose();
720     
721     public void showInitializationErrorMessage(Exception exception) {
722         Throwable cause = exception;
723         
724         while (cause.getCause() != null) {
725             cause = cause.getCause();
726         }
727         
728         showMessage("Fatal Error: \""+cause.getLocalizedMessage()+"\""+
729                 "\nSee log for more information.", "Initialization Fatal Error");
730     }
731     
732     public void setAnimatedThread(AnimatedThread thread) {
733         animatedThread = thread;
734     }
735 
736     public AnimatedThread getAnimatedThread() {
737         return animatedThread;
738     }
739 
740     public abstract String getHelpID();
741 
742     public void showMessage(final String message, final String title) {
743         if (SwingUtilities.isEventDispatchThread()) {
744             JOptionPane.showMessageDialog(getProjectsView().getRoot(), message, title, JOptionPane.ERROR_MESSAGE);
745         } else {
746             SwingUtilities.invokeLater(new Runnable() {
747                 public void run() {
748                     JOptionPane.showMessageDialog(getProjectsView().getRoot(), message, title, JOptionPane.ERROR_MESSAGE);
749                 }
750             });
751         }
752     }
753 }