001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc;
005:
006: import org.apache.commons.io.IOUtils;
007: import org.apache.xmlbeans.XmlError;
008: import org.apache.xmlbeans.XmlException;
009:
010: import com.tc.admin.common.XAbstractAction;
011: import com.tc.admin.common.XTextPane;
012:
013: import java.awt.Color;
014: import java.awt.Toolkit;
015: import java.awt.event.ActionEvent;
016: import java.awt.event.ActionListener;
017: import java.awt.event.InputEvent;
018: import java.awt.event.KeyEvent;
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.StringReader;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.Iterator;
025: import java.util.List;
026:
027: import javax.swing.Action;
028: import javax.swing.ImageIcon;
029: import javax.swing.JOptionPane;
030: import javax.swing.JPopupMenu;
031: import javax.swing.JSeparator;
032: import javax.swing.KeyStroke;
033: import javax.swing.Timer;
034: import javax.swing.event.DocumentEvent;
035: import javax.swing.event.DocumentListener;
036: import javax.swing.event.UndoableEditEvent;
037: import javax.swing.text.DefaultStyledDocument;
038: import javax.swing.text.SimpleAttributeSet;
039: import javax.swing.text.StyleConstants;
040: import javax.swing.text.StyledDocument;
041: import javax.swing.undo.UndoManager;
042: import javax.swing.undo.UndoableEdit;
043:
044: public class ConfigTextPane extends XTextPane {
045: private static final int SHORTCUT_KEY_MASK = Toolkit
046: .getDefaultToolkit().getMenuShortcutKeyMask();
047:
048: private SaveAction m_saveAction;
049: private UndoAction m_undoAction;
050: private RedoAction m_redoAction;
051:
052: private static final String SAVE_CMD = "Save";
053: private static final String UNDO_CMD = "Undo";
054: private static final String REDO_CMD = "Redo";
055:
056: private static final KeyStroke SAVE_STROKE = KeyStroke
057: .getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY_MASK, false);
058: private static final KeyStroke UNDO_STROKE = KeyStroke
059: .getKeyStroke(KeyEvent.VK_Z, SHORTCUT_KEY_MASK, false);
060: private static final KeyStroke REDO_STROKE = KeyStroke
061: .getKeyStroke(KeyEvent.VK_Z, SHORTCUT_KEY_MASK
062: | InputEvent.SHIFT_MASK, false);
063:
064: private ConfigTextListener m_configTextListener;
065: private SimpleAttributeSet m_errorAttrSet;
066: private Timer m_parseTimer;
067: private MyUndoManager m_undoManager;
068:
069: public ConfigTextPane() {
070: super ();
071:
072: m_errorAttrSet = new SimpleAttributeSet();
073: StyleConstants.setForeground(m_errorAttrSet, Color.red);
074:
075: m_undoManager = new MyUndoManager();
076:
077: JPopupMenu popup = getPopupMenu();
078: popup.add(new JSeparator());
079: popup.add(m_undoAction = new UndoAction());
080: popup.add(m_redoAction = new RedoAction());
081: popup.add(new JSeparator());
082: popup.add(m_saveAction = new SaveAction());
083: popup.add(new SaveAsAction());
084:
085: getActionMap().put(SAVE_CMD, m_saveAction);
086: getActionMap().put(UNDO_CMD, m_undoAction);
087: getActionMap().put(REDO_CMD, m_redoAction);
088:
089: getInputMap().put(SAVE_STROKE, SAVE_CMD);
090: getInputMap().put(UNDO_STROKE, UNDO_CMD);
091: getInputMap().put(REDO_STROKE, REDO_CMD);
092:
093: m_parseTimer = new Timer(2000, new ParseTimerAction());
094: m_parseTimer.setRepeats(false);
095:
096: m_configTextListener = new ConfigTextListener();
097: }
098:
099: private void removeListeners() {
100: m_parseTimer.stop();
101: getDocument().removeDocumentListener(m_configTextListener);
102: removeUndoableEditListener();
103: }
104:
105: private void addListeners() {
106: getDocument().addDocumentListener(m_configTextListener);
107: addUndoableEditListener();
108: }
109:
110: public void load(String filename) {
111: FileInputStream fis = null;
112:
113: removeListeners();
114: try {
115: fis = new FileInputStream(new File(filename));
116: setContent(IOUtils.toString(fis));
117: hasErrors();
118: } catch (Exception e) {
119: e.printStackTrace();
120: } finally {
121: IOUtils.closeQuietly(fis);
122: }
123: addListeners();
124: }
125:
126: public void set(String text) {
127: removeListeners();
128: try {
129: setContent(text);
130: hasErrors();
131: } catch (Exception e) {
132: e.printStackTrace();
133: }
134: addListeners();
135: }
136:
137: class ConfigTextListener implements DocumentListener {
138: public void insertUpdate(DocumentEvent e) {
139: m_saveAction.setEnabled(true);
140: m_parseTimer.stop();
141: m_parseTimer.start();
142: }
143:
144: public void removeUpdate(DocumentEvent e) {
145: m_saveAction.setEnabled(true);
146: m_parseTimer.stop();
147: m_parseTimer.start();
148: }
149:
150: public void changedUpdate(DocumentEvent e) {/**/
151: }
152: }
153:
154: class ParseTimerAction implements ActionListener {
155: public void actionPerformed(ActionEvent ae) {
156: checkForErrors();
157: }
158: }
159:
160: private void checkForErrors() {
161: setEditable(false);
162: removeListeners();
163: hasErrors();
164: addListeners();
165: setEditable(true);
166: }
167:
168: class SaveAction extends XAbstractAction {
169: SaveAction() {
170: super ("Save");
171: setAccelerator(SAVE_STROKE);
172: String uri = "/com/tc/admin/icons/save_edit.gif";
173: setSmallIcon(new ImageIcon(getClass().getResource(uri)));
174: setEnabled(false);
175: }
176:
177: public void actionPerformed(ActionEvent ae) {
178: save();
179: }
180: }
181:
182: class SaveAsAction extends XAbstractAction {
183: SaveAsAction() {
184: super ("Save As...");
185: String uri = "/com/tc/admin/icons/saveas_edit.gif";
186: setSmallIcon(new ImageIcon(getClass().getResource(uri)));
187: }
188:
189: public void actionPerformed(ActionEvent ae) {
190: saveAs();
191: }
192: }
193:
194: public boolean hasErrors() {
195: boolean hasErrors = false;
196:
197: clearAllStyles();
198:
199: try {
200: TextLineInfo lineInfo = getLineInfo();
201:
202: try {
203: ConfigHelper configHelper = getMainFrame()
204: .getConfigHelper();
205: List errors = configHelper.validate(getText());
206:
207: hasErrors = errors.size() > 0;
208: handleErrors(errors, lineInfo);
209: } catch (XmlException e) {
210: hasErrors = true;
211:
212: Collection c = e.getErrors();
213: ArrayList errorList = new ArrayList();
214:
215: if (c != null) {
216: errorList.addAll(c);
217: handleErrors(new ArrayList(c), lineInfo);
218: } else {
219: errorList.add(e);
220: getMainFrame().setConfigErrors(errorList);
221: }
222: }
223: } catch (Exception e) {
224: e.printStackTrace();
225: }
226:
227: return hasErrors;
228: }
229:
230: private void clearAllStyles() {
231: StyledDocument doc = (StyledDocument) getDocument();
232: doc.setCharacterAttributes(0, doc.getLength(),
233: SimpleAttributeSet.EMPTY, true);
234: }
235:
236: private void handleErrors(List errorList, TextLineInfo lineInfo) {
237: StyledDocument doc = (StyledDocument) getDocument();
238: Iterator errors = errorList.iterator();
239: XmlError error;
240:
241: while (errors.hasNext()) {
242: error = (XmlError) errors.next();
243:
244: int line = error.getLine();
245: int col = error.getColumn();
246: int start = lineInfo.offset(line - 1) + col - 1;
247: int len = getElementLength(start);
248:
249: doc
250: .setCharacterAttributes(start, len, m_errorAttrSet,
251: true);
252: }
253:
254: getMainFrame().setConfigErrors(errorList);
255: }
256:
257: private int getElementLength(int start) {
258: StyledDocument doc = (StyledDocument) getDocument();
259:
260: try {
261: String text = doc.getText(start, doc.getLength() - start);
262: int nameEnd = text.indexOf('>');
263: String name = text.substring(1, nameEnd);
264: String endTok = "</" + name + ">";
265: int endIndex = text.indexOf(endTok);
266:
267: if (endIndex != -1) {
268: return endIndex + endTok.length();
269: } else {
270: return nameEnd + 1;
271: }
272: } catch (Exception e) {
273: return 1;
274: }
275: }
276:
277: private void addUndoableEditListener() {
278: ((DefaultStyledDocument) getDocument())
279: .addUndoableEditListener(m_undoManager);
280: }
281:
282: private void removeUndoableEditListener() {
283: ((DefaultStyledDocument) getDocument())
284: .removeUndoableEditListener(m_undoManager);
285: }
286:
287: private TextLineInfo getLineInfo() {
288: try {
289: return new TextLineInfo(new StringReader(getText()));
290: } catch (Exception e) {
291: return new TextLineInfo();
292: }
293: }
294:
295: void selectError(XmlError error) {
296: TextLineInfo lineInfo = getLineInfo();
297: int line = error.getLine();
298: int col = error.getColumn();
299: int start = lineInfo.offset(line - 1) + col - 1;
300: int len = getElementLength(start);
301:
302: setCaretPosition(start);
303: moveCaretPosition(start + len);
304:
305: requestFocusInWindow();
306: }
307:
308: private SessionIntegratorFrame getMainFrame() {
309: return (SessionIntegratorFrame) getAncestorOfClass(SessionIntegratorFrame.class);
310: }
311:
312: private void save() {
313: SessionIntegratorFrame frame = getMainFrame();
314:
315: removeListeners();
316: if (hasErrors()) {
317: String msg = "There are configuration errors. Save anyway?";
318: String title = frame.getTitle();
319: int type = JOptionPane.YES_NO_OPTION;
320: int answer = JOptionPane.showConfirmDialog(frame, msg,
321: title, type);
322:
323: if (answer == JOptionPane.YES_OPTION) {
324: frame.saveXML(getContent());
325: }
326: } else {
327: frame.saveXML(getContent());
328: }
329: addListeners();
330: m_undoManager.discardAllEdits();
331: m_saveAction.setEnabled(false);
332: m_undoAction.setEnabled(false);
333: m_redoAction.setEnabled(false);
334: }
335:
336: private void saveAs() {
337: SessionIntegratorFrame frame = getMainFrame();
338:
339: removeListeners();
340: if (hasErrors()) {
341: String msg = "There are configuration errors. Save anyway?";
342: String title = frame.getTitle();
343: int type = JOptionPane.YES_NO_OPTION;
344: int answer = JOptionPane.showConfirmDialog(frame, msg,
345: title, type);
346:
347: if (answer == JOptionPane.YES_OPTION) {
348: frame.exportConfiguration();
349: }
350: } else {
351: frame.exportConfiguration();
352: }
353: addListeners();
354: }
355:
356: class MyUndoManager extends UndoManager {
357: public UndoableEdit nextUndoable() {
358: return editToBeUndone();
359: }
360:
361: public UndoableEdit nextRedoable() {
362: return editToBeRedone();
363: }
364:
365: public void undoableEditHappened(UndoableEditEvent e) {
366: super .undoableEditHappened(e);
367: m_undoAction.setEnabled(canUndo());
368: m_redoAction.setEnabled(canRedo());
369: }
370: }
371:
372: class UndoAction extends XAbstractAction {
373: UndoAction() {
374: super ("Undo");
375: setAccelerator(UNDO_STROKE);
376: String uri = "/com/tc/admin/icons/undo_edit.gif";
377: setSmallIcon(new ImageIcon(getClass().getResource(uri)));
378: setEnabled(false);
379: }
380:
381: public void actionPerformed(ActionEvent ae) {
382: UndoableEdit next = m_undoManager.nextUndoable();
383:
384: if (next != null) {
385: m_undoManager.undo();
386:
387: setEnabled(m_undoManager.canUndo());
388: m_redoAction.setEnabled(m_undoManager.canRedo());
389: }
390: }
391: }
392:
393: class RedoAction extends XAbstractAction {
394: RedoAction() {
395: super ("Redo");
396: setAccelerator(REDO_STROKE);
397: String uri = "/com/tc/admin/icons/redo_edit.gif";
398: setSmallIcon(new ImageIcon(getClass().getResource(uri)));
399: setEnabled(false);
400: }
401:
402: public void actionPerformed(ActionEvent ae) {
403: UndoableEdit next = m_undoManager.nextRedoable();
404:
405: if (next != null) {
406: m_undoManager.redo();
407: setEnabled(m_undoManager.canRedo());
408: m_undoAction.setEnabled(m_undoManager.canUndo());
409: }
410: }
411: }
412:
413: Action getSaveAction() {
414: return m_saveAction;
415: }
416:
417: Action getUndoAction() {
418: return m_undoAction;
419: }
420:
421: Action getRedoAction() {
422: return m_redoAction;
423: }
424:
425: Action getCutAction() {
426: return m_helper.getCutAction();
427: }
428:
429: Action getCopyAction() {
430: return m_helper.getCopyAction();
431: }
432:
433: Action getPasteAction() {
434: return m_helper.getPasteAction();
435: }
436: }
|