001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package org.terracotta.dso.editors;
006:
007: import org.eclipse.core.resources.IFile;
008: import org.eclipse.core.resources.IMarker;
009: import org.eclipse.core.resources.IProject;
010: import org.eclipse.core.resources.IResourceChangeEvent;
011: import org.eclipse.core.resources.IResourceChangeListener;
012: import org.eclipse.core.resources.IWorkspace;
013: import org.eclipse.core.resources.ResourcesPlugin;
014: import org.eclipse.core.runtime.CoreException;
015: import org.eclipse.core.runtime.IPath;
016: import org.eclipse.core.runtime.IProgressMonitor;
017: import org.eclipse.core.runtime.IStatus;
018: import org.eclipse.jface.dialogs.ErrorDialog;
019: import org.eclipse.jface.dialogs.IMessageProvider;
020: import org.eclipse.jface.dialogs.MessageDialog;
021: import org.eclipse.jface.text.DocumentEvent;
022: import org.eclipse.jface.text.IDocument;
023: import org.eclipse.jface.text.IDocumentListener;
024: import org.eclipse.jface.text.ITextInputListener;
025: import org.eclipse.jface.window.Window;
026: import org.eclipse.swt.SWT;
027: import org.eclipse.swt.custom.ScrolledComposite;
028: import org.eclipse.swt.widgets.Display;
029: import org.eclipse.swt.widgets.Shell;
030: import org.eclipse.ui.IEditorInput;
031: import org.eclipse.ui.IEditorSite;
032: import org.eclipse.ui.IFileEditorInput;
033: import org.eclipse.ui.PartInitException;
034: import org.eclipse.ui.PlatformUI;
035: import org.eclipse.ui.dialogs.SaveAsDialog;
036: import org.eclipse.ui.ide.IDE;
037: import org.eclipse.ui.ide.IGotoMarker;
038: import org.eclipse.ui.part.FileEditorInput;
039: import org.eclipse.ui.part.MultiPageEditorPart;
040: import org.eclipse.ui.texteditor.IDocumentProvider;
041: import org.eclipse.ui.texteditor.IElementStateListener;
042: import org.terracotta.dso.ConfigurationHelper;
043: import org.terracotta.dso.IConfigurationListener;
044: import org.terracotta.dso.Messages;
045: import org.terracotta.dso.TcPlugin;
046: import org.terracotta.dso.editors.xml.XMLEditor;
047: import org.terracotta.dso.editors.xmlbeans.XmlObjectStructureChangeEvent;
048: import org.terracotta.dso.editors.xmlbeans.XmlObjectStructureListener;
049:
050: import com.terracottatech.config.TcConfigDocument;
051: import com.terracottatech.config.TcConfigDocument.TcConfig;
052:
053: import java.io.IOException;
054: import java.text.MessageFormat;
055:
056: public class ConfigurationEditor extends MultiPageEditorPart implements
057: IResourceChangeListener, IGotoMarker,
058: XmlObjectStructureListener {
059: private static final int XML_EDITOR_PAGE_INDEX = 0;
060: private static final int DSO_APPLICATION_PAGE_INDEX = 1;
061: private static final int SERVERS_PAGE_INDEX = 2;
062: private static final int CLIENT_PAGE_INDEX = 3;
063:
064: private IProject m_project;
065: private DsoApplicationPanel m_dsoAppPanel;
066: private ServersPanel m_serversPanel;
067: private ClientsPanel m_clientsPanel;
068: private XMLEditor m_xmlEditor;
069: private int m_xmlEditorPageIndex;
070: private boolean m_haveActiveConfig;
071: private DocumentListener m_docListener;
072: private ElementStateListener m_elementStateListener;
073: private TextInputListener m_textInputListener;
074: private Runnable m_parseTimer;
075: private boolean m_syncXmlText;
076: private Display m_display;
077: private IConfigurationListener m_configAdapter;
078:
079: public ConfigurationEditor() {
080: super ();
081:
082: m_docListener = new DocumentListener();
083: m_elementStateListener = new ElementStateListener();
084: m_textInputListener = new TextInputListener();
085: m_parseTimer = new ParseTimer();
086: m_display = Display.getDefault();
087: m_configAdapter = new ConfigAdapter();
088: }
089:
090: protected void pageChange(final int newPageIndex) {
091: if (newPageIndex != 0) {
092: if (m_project != null && m_project.isOpen()) {
093: TcPlugin plugin = TcPlugin.getDefault();
094: TcConfig config = plugin.getConfiguration(m_project);
095:
096: if (config == TcPlugin.BAD_CONFIG) {
097: Display.getDefault().syncExec(new Runnable() {
098: public void run() {
099: getControl(newPageIndex).setVisible(false);
100:
101: Shell shell = Display.getDefault()
102: .getActiveShell();
103: String title = "Terracotta Config Editor";
104: String msg = "The source page has errors. The other pages cannot be\nused until these errors are resolved.";
105:
106: MessageDialog
107: .openWarning(shell, title, msg);
108: }
109: });
110: setActivePage(0);
111: return;
112: }
113: }
114: }
115: super .pageChange(newPageIndex);
116: }
117:
118: class ConfigAdapter implements IConfigurationListener {
119: private void update(boolean initPanels) {
120: if (!m_syncXmlText) {
121: syncXmlDocument();
122: }
123: if (initPanels) {
124: initPanels();
125: }
126: internalSetDirty(Boolean.TRUE);
127: }
128:
129: private void handleUpdate(final boolean updatePanels) {
130: if (Display.getCurrent() != null) {
131: update(updatePanels);
132: return;
133: }
134: asyncExec(new Runnable() {
135: public void run() {
136: update(updatePanels);
137: }
138: });
139: }
140:
141: public void configurationChanged(IProject project) {
142: if (TcPlugin.getDefault().hasTerracottaNature(project)) {
143: if (m_project != null && m_project.equals(project)) {
144: handleUpdate(true);
145: }
146: }
147: }
148:
149: private void handleChange(IProject project, boolean updatePanels) {
150: if (TcPlugin.getDefault().hasTerracottaNature(project)) {
151: if (m_project != null && m_project.equals(project)) {
152: handleUpdate(updatePanels);
153: }
154: }
155: }
156:
157: public void serverChanged(IProject project, int index) {
158: handleChange(project, false);
159: }
160:
161: public void serversChanged(IProject project) {
162: handleChange(project, true);
163: }
164:
165: public void rootChanged(IProject project, int index) {
166: handleChange(project, false);
167: }
168:
169: public void rootsChanged(IProject project) {
170: handleChange(project, true);
171: }
172:
173: public void distributedMethodsChanged(IProject project) {
174: handleChange(project, true);
175: }
176:
177: public void distributedMethodChanged(IProject project, int index) {
178: handleChange(project, false);
179: }
180:
181: public void bootClassesChanged(IProject project) {
182: handleChange(project, true);
183: }
184:
185: public void bootClassChanged(IProject project, int index) {
186: handleChange(project, false);
187: }
188:
189: public void transientFieldsChanged(IProject project) {
190: handleChange(project, true);
191: }
192:
193: public void transientFieldChanged(IProject project, int index) {
194: handleChange(project, false);
195: }
196:
197: public void autolockChanged(IProject project, int index) {
198: handleChange(project, false);
199: }
200:
201: public void autolocksChanged(IProject project) {
202: handleChange(project, true);
203: }
204:
205: public void namedLockChanged(IProject project, int index) {
206: handleChange(project, false);
207: }
208:
209: public void namedLocksChanged(IProject project) {
210: handleChange(project, true);
211: }
212:
213: public void includeRuleChanged(IProject project, int index) {
214: handleChange(project, false);
215: }
216:
217: public void includeRulesChanged(IProject project) {
218: handleChange(project, true);
219: }
220:
221: public void excludeRuleChanged(IProject project, int index) {
222: handleChange(project, false);
223: }
224:
225: public void excludeRulesChanged(IProject project) {
226: handleChange(project, true);
227: }
228:
229: public void instrumentationRulesChanged(IProject project) {
230: handleChange(project, true);
231: }
232:
233: public void clientChanged(IProject project) {
234: handleChange(project, false);
235: }
236:
237: public void moduleReposChanged(IProject project) {
238: handleChange(project, true);
239: }
240:
241: public void moduleRepoChanged(IProject project, int index) {
242: handleChange(project, false);
243: }
244:
245: public void moduleChanged(IProject project, int index) {
246: handleChange(project, false);
247: }
248:
249: public void modulesChanged(IProject project) {
250: handleChange(project, true);
251: }
252: }
253:
254: private void setTimer(boolean start) {
255: if (start)
256: m_display.timerExec(2000, m_parseTimer);
257: else
258: m_display.timerExec(-1, m_parseTimer);
259: }
260:
261: private class ParseTimer implements Runnable {
262: public void run() {
263: if (getActivePage() == XML_EDITOR_PAGE_INDEX) {
264: m_syncXmlText = true;
265: syncXmlModel();
266: m_syncXmlText = false;
267: }
268: }
269: }
270:
271: void createDsoApplicationPage(int pageIndex) {
272: addPage(pageIndex, m_dsoAppPanel = new DsoApplicationPanel(
273: getContainer(), SWT.NONE));
274: setPageText(pageIndex, "DSO config");
275: m_dsoAppPanel.addXmlObjectStructureListener(this );
276: }
277:
278: public DsoApplicationPanel getDsoApplicationPanel() {
279: return m_dsoAppPanel;
280: }
281:
282: public void showDsoApplicationPanel() {
283: setActivePage(0);
284: }
285:
286: void createServersPage(int pageIndex) {
287: ScrolledComposite scroll = new ScrolledComposite(
288: getContainer(), SWT.V_SCROLL | SWT.H_SCROLL);
289: scroll.setContent(m_serversPanel = new ServersPanel(scroll,
290: SWT.NONE));
291: scroll.setExpandHorizontal(true);
292: scroll.setExpandVertical(true);
293: scroll.setMinSize(m_serversPanel.computeSize(SWT.DEFAULT,
294: SWT.DEFAULT));
295: addPage(pageIndex, scroll);
296: setPageText(pageIndex, "Servers config");
297: m_serversPanel.addXmlObjectStructureListener(this );
298: }
299:
300: void createClientPage(int pageIndex) {
301: ScrolledComposite scroll = new ScrolledComposite(
302: getContainer(), SWT.V_SCROLL | SWT.H_SCROLL);
303: scroll.setContent(m_clientsPanel = new ClientsPanel(scroll,
304: SWT.NONE));
305: scroll.setExpandHorizontal(true);
306: scroll.setExpandVertical(true);
307: scroll.setMinSize(m_clientsPanel.computeSize(SWT.DEFAULT,
308: SWT.DEFAULT));
309: addPage(pageIndex, scroll);
310: setPageText(pageIndex, "Clients config");
311: m_clientsPanel.addXmlObjectStructureListener(this );
312: }
313:
314: void createXMLEditorPage(int pageIndex) {
315: try {
316: IEditorInput input = getEditorInput();
317:
318: addPage(m_xmlEditorPageIndex = pageIndex,
319: m_xmlEditor = new XMLEditor(), input);
320: setPageText(m_xmlEditorPageIndex, m_xmlEditor.getTitle());
321:
322: m_xmlEditor.addTextInputListener(m_textInputListener);
323: m_xmlEditor.getDocument()
324: .addDocumentListener(m_docListener);
325: m_xmlEditor.getDocumentProvider().addElementStateListener(
326: m_elementStateListener);
327: } catch (PartInitException e) {
328: ErrorDialog.openError(getSite().getShell(),
329: "Error creating nested text editor", null, e
330: .getStatus());
331: }
332: }
333:
334: public boolean isActiveConfig() {
335: TcPlugin plugin = TcPlugin.getDefault();
336: IFile configFile = plugin.getConfigurationFile(m_project);
337: IFileEditorInput fileEditorInput = (FileEditorInput) getEditorInput();
338: IFile file = fileEditorInput.getFile();
339:
340: return file != null && file.equals(configFile)
341: && plugin.hasTerracottaNature(m_project);
342: }
343:
344: boolean haveActiveConfig() {
345: return m_haveActiveConfig;
346: }
347:
348: protected void createPages() {
349: createXMLEditorPage(XML_EDITOR_PAGE_INDEX);
350: if (haveActiveConfig()) {
351: createDsoApplicationPage(DSO_APPLICATION_PAGE_INDEX);
352: createServersPage(SERVERS_PAGE_INDEX);
353: createClientPage(CLIENT_PAGE_INDEX);
354: ResourcesPlugin.getWorkspace().addResourceChangeListener(
355: this );
356: initPanels();
357: TcPlugin.getDefault().addConfigurationListener(
358: m_configAdapter);
359: }
360: }
361:
362: public void structureChanged(XmlObjectStructureChangeEvent e) {
363: // This means that either the servers, clients, or dsoApplication elements were
364: // removed.
365: }
366:
367: public void dispose() {
368: if (haveActiveConfig()) {
369: m_serversPanel.removeXmlObjectStructureListener(this );
370: m_clientsPanel.removeXmlObjectStructureListener(this );
371: m_dsoAppPanel.removeXmlObjectStructureListener(this );
372: ResourcesPlugin.getWorkspace()
373: .removeResourceChangeListener(this );
374: TcPlugin.getDefault().removeConfigurationListener(
375: m_configAdapter);
376: }
377: super .dispose();
378: }
379:
380: public void doSave(IProgressMonitor monitor) {
381: if (haveActiveConfig())
382: setTimer(false);
383: TcPlugin.getDefault().ignoreNextConfigChange();
384: m_xmlEditor.doSave(monitor);
385: m_syncXmlText = true;
386: syncXmlModel();
387: m_syncXmlText = false;
388: clearDirty();
389: }
390:
391: public void doSaveAs() {
392: performSaveAs(null);
393: }
394:
395: protected void performSaveAs(IProgressMonitor progressMonitor) {
396: Shell shell = getSite().getShell();
397: IEditorInput input = getEditorInput();
398: SaveAsDialog dialog = new SaveAsDialog(shell);
399: IFile original = null;
400:
401: if (input instanceof IFileEditorInput) {
402: if ((original = ((IFileEditorInput) input).getFile()) != null) {
403: dialog.setOriginalFile(original);
404: }
405: }
406:
407: dialog.create();
408:
409: IDocumentProvider provider = m_xmlEditor.getDocumentProvider();
410: if (provider == null) {
411: // editor has programmatically been closed while the dialog was open
412: return;
413: }
414:
415: if (provider.isDeleted(input) && original != null) {
416: String message = MessageFormat.format(
417: Messages.Editor_warning_save_delete,
418: new Object[] { original.getName() });
419:
420: dialog.setErrorMessage(null);
421: dialog.setMessage(message, IMessageProvider.WARNING);
422: }
423:
424: if (dialog.open() == Window.CANCEL) {
425: if (progressMonitor != null) {
426: progressMonitor.setCanceled(true);
427: }
428: return;
429: }
430:
431: IPath filePath = dialog.getResult();
432: if (filePath == null) {
433: if (progressMonitor != null) {
434: progressMonitor.setCanceled(true);
435: }
436: return;
437: }
438:
439: IWorkspace workspace = ResourcesPlugin.getWorkspace();
440: IFile file = workspace.getRoot().getFile(filePath);
441: final IEditorInput newInput = new FileEditorInput(file);
442: boolean success = false;
443:
444: try {
445: provider.aboutToChange(newInput);
446: provider.saveDocument(progressMonitor, newInput, provider
447: .getDocument(input), true);
448: success = true;
449: clearDirty();
450: } catch (CoreException ce) {
451: IStatus status = ce.getStatus();
452:
453: if (status == null
454: || status.getSeverity() != IStatus.CANCEL) {
455: String title = Messages.Editor_error_save_title;
456: String msg = MessageFormat.format(
457: Messages.Editor_error_save_message,
458: new Object[] { ce.getMessage() });
459:
460: if (status != null) {
461: switch (status.getSeverity()) {
462: case IStatus.INFO:
463: MessageDialog
464: .openInformation(shell, title, msg);
465: break;
466: case IStatus.WARNING:
467: MessageDialog.openWarning(shell, title, msg);
468: break;
469: default:
470: MessageDialog.openError(shell, title, msg);
471: }
472: } else {
473: MessageDialog.openError(shell, title, msg);
474: }
475: }
476: } finally {
477: provider.changed(newInput);
478: if (success) {
479: setInput(newInput);
480: }
481: }
482:
483: if (progressMonitor != null) {
484: progressMonitor.setCanceled(!success);
485: }
486: }
487:
488: public void gotoMarker(IMarker marker) {
489: setActivePage(0);
490: IDE.gotoMarker(getEditor(0), marker);
491: }
492:
493: public void newInputFile(final IFile file) {
494: if (file != null && file.exists()) {
495: syncExec(new Runnable() {
496: public void run() {
497: final FileEditorInput input = new FileEditorInput(
498: file);
499:
500: setInput(input);
501:
502: m_project = file.getProject();
503:
504: if (haveActiveConfig()) {
505: if (getPageCount() == 1) {
506: createDsoApplicationPage(1);
507: createServersPage(2);
508: createClientPage(3);
509: }
510: ResourcesPlugin.getWorkspace()
511: .addResourceChangeListener(
512: ConfigurationEditor.this );
513: initPanels();
514: } else {
515: ResourcesPlugin.getWorkspace()
516: .removeResourceChangeListener(
517: ConfigurationEditor.this );
518: for (int i = getPageCount() - 1; i > 0; i--) {
519: removePage(i);
520: }
521: }
522:
523: m_xmlEditor.setInput(input);
524: String name = file.getName();
525: setPartName(name);
526: setPageText(m_xmlEditorPageIndex, name);
527: }
528: });
529: }
530: }
531:
532: private void syncExec(Runnable runner) {
533: getSite().getShell().getDisplay().syncExec(runner);
534: }
535:
536: private void asyncExec(Runnable runner) {
537: getSite().getShell().getDisplay().asyncExec(runner);
538: }
539:
540: public void init(IEditorSite site, IEditorInput editorInput)
541: throws PartInitException {
542: if (!(editorInput instanceof IFileEditorInput)) {
543: throw new PartInitException(
544: "Invalid Input: Must be IFileEditorInput");
545: }
546:
547: IFileEditorInput fileEditorInput = (IFileEditorInput) editorInput;
548: IFile file = fileEditorInput.getFile();
549: IProject project = file.getProject();
550:
551: if (!project.exists()) {
552: String msg = "Project '" + project.getName()
553: + "' does not exist";
554:
555: ResourcesPlugin.getWorkspace()
556: .removeResourceChangeListener(this );
557: throw new PartInitException(msg);
558: }
559:
560: if (!project.isOpen()) {
561: String msg = "Project '" + project.getName()
562: + "' is not open";
563:
564: ResourcesPlugin.getWorkspace()
565: .removeResourceChangeListener(this );
566: throw new PartInitException(msg);
567: }
568:
569: m_project = project;
570: super .init(site, editorInput);
571:
572: setPartName(file.getName());
573: }
574:
575: protected void setInput(IEditorInput input) {
576: super .setInput(input);
577: m_haveActiveConfig = isActiveConfig();
578: }
579:
580: public boolean isSaveAsAllowed() {
581: return true;
582: }
583:
584: public void initPanels() {
585: if (m_project != null && m_project.isOpen()) {
586: m_dsoAppPanel.setup(m_project);
587: m_serversPanel.setup(m_project);
588: m_clientsPanel.setup(m_project);
589: enablePanels();
590: } else {
591: disablePanels();
592: }
593: }
594:
595: private void disablePanels() {
596: m_dsoAppPanel.setEnabled(false);
597: m_serversPanel.setEnabled(false);
598: m_clientsPanel.setEnabled(false);
599: }
600:
601: private void enablePanels() {
602: m_dsoAppPanel.setEnabled(true);
603: m_serversPanel.setEnabled(true);
604: m_clientsPanel.setEnabled(true);
605: }
606:
607: public void closeEditor() {
608: getSite().getPage().closeEditor(this , true);
609: }
610:
611: public void resourceChanged(final IResourceChangeEvent event) {
612: switch (event.getType()) {
613: case IResourceChangeEvent.PRE_DELETE:
614: case IResourceChangeEvent.PRE_CLOSE: {
615: if (m_project.equals(event.getResource())) {
616: if (Display.getCurrent() != null) {
617: ConfigurationEditor.this .closeEditor();
618: return;
619: }
620: syncExec(new Runnable() {
621: public void run() {
622: ConfigurationEditor.this .closeEditor();
623: }
624: });
625: }
626: break;
627: }
628: }
629: }
630:
631: public void syncXmlDocument() {
632: if (PlatformUI.getWorkbench().isClosing())
633: return;
634: asyncExec(new Runnable() {
635: public void run() {
636: m_xmlEditor.getTextWidget().setRedraw(false);
637: try {
638: int topLine = m_xmlEditor.getTopIndex();
639: TcPlugin plugin = TcPlugin.getDefault();
640: IDocument doc = m_xmlEditor.getDocument();
641: TcConfig config = plugin
642: .getConfiguration(m_project);
643: if (config != null && config != TcPlugin.BAD_CONFIG) {
644: TcConfigDocument configDoc = TcConfigDocument.Factory
645: .newInstance();
646: configDoc.setTcConfig(config);
647: doc.removeDocumentListener(m_docListener);
648: doc.set(plugin
649: .configDocumentAsString(configDoc));
650: doc.addDocumentListener(m_docListener);
651: }
652: m_xmlEditor.setTopIndex(topLine);
653: } finally {
654: m_xmlEditor.getTextWidget().setRedraw(true);
655: }
656: }
657: });
658: }
659:
660: public synchronized void syncXmlModel() {
661: TcPlugin plugin = TcPlugin.getDefault();
662: IDocument doc = m_xmlEditor.getDocument();
663: String xmlText = doc.get();
664: try {
665: plugin.setConfigurationFromString(m_project, xmlText);
666: } catch (IOException ioe) {
667: disablePanels();
668: }
669: }
670:
671: private void clearDirty() {
672: internalSetDirty(Boolean.FALSE);
673: }
674:
675: private void internalSetDirty(Boolean isDirty) {
676: TcPlugin.getDefault().setConfigurationFileDirty(m_project,
677: isDirty);
678: firePropertyChange(PROP_DIRTY);
679: }
680:
681: public boolean isDirty() {
682: if (m_project != null && haveActiveConfig()) {
683: return m_project.isOpen()
684: && TcPlugin.getDefault().isConfigurationFileDirty(
685: m_project);
686: } else {
687: return super .isDirty();
688: }
689: }
690:
691: public void modelChanged() {
692: syncXmlDocument();
693: internalSetDirty(Boolean.TRUE);
694: }
695:
696: public IDocument getDocument() {
697: return m_xmlEditor.getDocument();
698: }
699:
700: class TextInputListener implements ITextInputListener {
701: public void inputDocumentAboutToBeChanged(IDocument oldInput,
702: IDocument newInput) {/**/
703: }
704:
705: public void inputDocumentChanged(IDocument oldInput,
706: IDocument newInput) {
707: if (oldInput != null) {
708: oldInput.removeDocumentListener(m_docListener);
709: }
710: if (newInput != null) {
711: newInput.addDocumentListener(m_docListener);
712: }
713: }
714: }
715:
716: class ElementStateListener implements IElementStateListener {
717: public void elementContentAboutToBeReplaced(Object element) {
718: m_xmlEditor.getDocument().removeDocumentListener(
719: m_docListener);
720: }
721:
722: public void elementContentReplaced(Object element) {
723: m_xmlEditor.getDocument()
724: .addDocumentListener(m_docListener);
725: }
726:
727: public void elementDeleted(Object element) {/**/
728: }
729:
730: public void elementMoved(Object originalElement,
731: Object movedElement) {/**/
732: }
733:
734: public void elementDirtyStateChanged(Object element,
735: boolean isDirty) {/**/
736: }
737: }
738:
739: class DocumentListener implements IDocumentListener {
740: public void documentAboutToBeChanged(DocumentEvent event) {/**/
741: }
742:
743: public void documentChanged(DocumentEvent event) {
744: if (haveActiveConfig())
745: setTimer(false);
746: internalSetDirty(Boolean.TRUE);
747: if (haveActiveConfig())
748: setTimer(true);
749: }
750: }
751:
752: public void applyProblemToText(String text, String msg,
753: String markerType) {
754: TcPlugin plugin = TcPlugin.getDefault();
755: IDocument doc = m_xmlEditor.getDocument();
756: ConfigurationHelper configHelper = plugin
757: .getConfigurationHelper(m_project);
758:
759: configHelper.applyProblemToText(doc, text, msg, markerType);
760: }
761: }
|