001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: *
013: * @created Nov 1, 2005
014: * @author dmoran
015: */
016:
017: package org.pentaho.designstudio.controls;
018:
019: import org.dom4j.Attribute;
020: import org.dom4j.DocumentHelper;
021: import org.dom4j.Element;
022: import org.dom4j.Node;
023: import org.eclipse.jface.util.ListenerList;
024: import org.eclipse.jface.util.SafeRunnable;
025: import org.eclipse.swt.SWT;
026: import org.eclipse.swt.events.ModifyEvent;
027: import org.eclipse.swt.events.ModifyListener;
028: import org.eclipse.swt.widgets.Composite;
029: import org.eclipse.swt.widgets.Control;
030: import org.eclipse.swt.widgets.Label;
031:
032: /**
033: * Abstract base class for UI controls that are XML aware. The XML aware control modifies the
034: * specified XML node to stay in sync with the text being displayed in the control.
035: *
036: * @author Doug Moran
037: *
038: */
039: public abstract class XMLAwareControl implements ModifyListener,
040: IXmlModifier {
041:
042: protected Element ancestorElement;
043: protected String xpathToElement;
044: protected String attributeName;
045: protected Node theTargetNode;
046: Label label;
047: boolean useCData = false;
048: boolean deleteWhenEmpty = false;
049:
050: private ListenerList xmlListeners = new ListenerList(1);
051:
052: /**
053: * Creates an XML aware control.
054: * @param labelStr the name of the node that is being kept in sync with the control.
055: */
056: protected XMLAwareControl() {
057: }
058:
059: /**
060: * Creates an XML aware control.
061: * @param parent Composite on which to place this guy
062: * @param parentNode The parent of the DOM element
063: * @param labelStr The node name for this DOM element
064: * @param controlLayoutData the layout data for the control
065: * @param labelLayoutData the layout data for the label
066: */
067: protected XMLAwareControl(Composite parent, String labelStr,
068: Object controlLayoutData, Object labelLayoutData) {
069: this (parent, SWT.NONE, labelStr, controlLayoutData,
070: labelLayoutData);
071: }
072:
073: /**
074: * Creates an XML aware control.
075: * @param parent Composite on which to place this guy
076: * @param parentNode The parent of the DOM element
077: * @param style the control style
078: * @param labelStr The node name for this DOM element
079: * @param controlLayoutData the layout data for the control
080: * @param labelLayoutData the layout data for the label
081: */
082: protected XMLAwareControl(Composite parent, int style,
083: String labelStr, Object controlLayoutData,
084: Object labelLayoutData) {
085: createControl(parent, style, labelStr, controlLayoutData,
086: labelLayoutData);
087: initListeners();
088: }
089:
090: protected void createLabel(Composite parent, Object layoutData,
091: String labelStr) {
092: // @todo Get the actual XML node from properties file from nodeName
093: if (labelStr != null) {
094: label = WidgetFactory.createLabel(parent, labelStr);
095: if (layoutData != null) {
096: label.setLayoutData(layoutData);
097: }
098: }
099: }
100:
101: protected void initListeners() {
102: addModifyListener();
103: Composite parent = getControl().getParent();
104: if (parent instanceof IXmlModificationListener) {
105: addXmlModificationListener((IXmlModificationListener) parent);
106: }
107: }
108:
109: /**
110: * Creates the UI control
111: * @param parent the parent of this control
112: * @param toolkit the toolkit to use when creating the control
113: * @param span the span of this control in a TableWrapLayout
114: * @return the UI control
115: */
116: abstract protected Control createControl(Composite parent,
117: int style, String labelStr, Object controlLayoutData,
118: Object labelLayoutData);
119:
120: /**
121: * Returns the UI control
122: * @return the UI control
123: */
124: abstract public Control getControl();
125:
126: /**
127: * Returns the text currently in the UI control
128: * @return the UI control text
129: */
130: abstract protected String getText();
131:
132: /**
133: * Sets the text currently in the UI control
134: * @param textStr the text to be displayed in the control.
135: */
136: abstract protected void setText(String textStr);
137:
138: /**
139: * Enables and disables the UI control.
140: */
141: abstract protected void setEnabled(boolean enabled);
142:
143: /**
144: * Returns the value of the DOM node.
145: * @return the DOM node value.
146: */
147: protected String getNodeText() {
148: Node node = getTargetNode();
149: return ((node != null) ? node.getText() : ""); //$NON-NLS-1$
150: }
151:
152: /**
153: * Returns the label to be displayed with the control.
154: * @return the label
155: */
156: protected String getLabel() {
157: return label.getText();
158: }
159:
160: /**
161: * Sets the node value to reflect what's in the value of the UI control.
162: */
163: protected void synchronizeNodeWithControl() {
164: Node node = getTargetNode();
165: if (node != null) {
166: String controlText = getText().trim();
167: if ((controlText.trim().length() == 0) && deleteWhenEmpty) {
168: node.detach();
169: theTargetNode = null;
170: fireXmlModificationEvent(new XmlModificationEvent(this ,
171: node, XmlModificationEvent.NODE_REMOVED));
172: } else if (!controlText.equals(getNodeText())) {
173: if (useCData && (node instanceof Element)) {
174: ((Element) node).clearContent();
175: ((Element) node).addCDATA(controlText);
176: } else {
177: node.setText(controlText);
178: }
179: fireXmlModificationEvent(new XmlModificationEvent(this ,
180: node, XmlModificationEvent.NODE_VALUE_CHANGED));
181: }
182: }
183: }
184:
185: public Node getTargetNode() {
186: if ((theTargetNode != null)
187: && (theTargetNode.getParent() == null)) {
188: theTargetNode = null;
189: }
190: if (theTargetNode == null) {
191: if (ancestorElement != null) {
192: Element element = (Element) ancestorElement
193: .selectSingleNode(xpathToElement);
194: if (element != null) {
195: if (attributeName != null) {
196: theTargetNode = element
197: .attribute(attributeName);
198: } else {
199: theTargetNode = element;
200: }
201: }
202: }
203: }
204: return (theTargetNode);
205: }
206:
207: protected void addTheNode() {
208: if (getTargetNode() == null) {
209: if (ancestorElement != null) {
210: Element element = (Element) ancestorElement
211: .selectSingleNode(xpathToElement);
212: XmlModificationEvent xmlEvent = null;
213: if (element == null) {
214: element = DocumentHelper.makeElement(
215: ancestorElement, xpathToElement);
216: xmlEvent = new XmlModificationEvent(this , element,
217: XmlModificationEvent.NODE_ADDED);
218: }
219: if (attributeName == null) {
220: theTargetNode = element;
221: theTargetNode.setText(getText().trim());
222: } else {
223: Attribute attribute = element
224: .attribute(attributeName);
225: int eventType = -1;
226: if (attribute == null) {
227: eventType = XmlModificationEvent.NODE_ADDED;
228: } else if (!attribute.getValue().equals(
229: getText().trim())) {
230: eventType = XmlModificationEvent.NODE_VALUE_CHANGED;
231: }
232: if (eventType != -1) {
233: element.addAttribute(attributeName, getText()
234: .trim());
235: attribute = element.attribute(attributeName);
236: theTargetNode = attribute;
237: xmlEvent = new XmlModificationEvent(this ,
238: attribute, eventType);
239: }
240: }
241:
242: if (xmlEvent != null) {
243: fireXmlModificationEvent(xmlEvent);
244: }
245: }
246: }
247:
248: }
249:
250: protected void deleteTheNode() {
251: Node node = getTargetNode();
252: if (node != null) {
253: node.detach();
254: XmlModificationEvent event = new XmlModificationEvent(this ,
255: node, XmlModificationEvent.NODE_REMOVED);
256: theTargetNode = null;
257: refresh();
258: fireXmlModificationEvent(event);
259: }
260: }
261:
262: public void setTargetElement(Element element) {
263: attributeName = null;
264: if (element == null) {
265: ancestorElement = null;
266: xpathToElement = null;
267: theTargetNode = null;
268: } else {
269: ancestorElement = element.getParent();
270: xpathToElement = element.getName();
271: theTargetNode = element;
272: }
273: refresh();
274: }
275:
276: public void setTargetAttribute(Attribute attribute) {
277: if (attribute == null) {
278: ancestorElement = null;
279: xpathToElement = null;
280: attributeName = null;
281: theTargetNode = null;
282: } else {
283: ancestorElement = attribute.getParent().getParent();
284: xpathToElement = attribute.getParent().getName();
285: attributeName = attribute.getName();
286: theTargetNode = attribute;
287: }
288: refresh();
289: }
290:
291: public void setTargetElement(Element ancestor, String xPathToElement) {
292: ancestorElement = ancestor;
293: this .xpathToElement = xPathToElement;
294: attributeName = null;
295: refresh();
296: }
297:
298: public void setTargetAttribute(Element element, String attributeName) {
299: ancestorElement = element.getParent();
300: xpathToElement = element.getName();
301: this .attributeName = attributeName;
302: refresh();
303: }
304:
305: public void setTargetAttribute(Element ancestor,
306: String xPathToElement, String attributeName) {
307: ancestorElement = ancestor;
308: this .xpathToElement = xPathToElement;
309: this .attributeName = attributeName;
310: refresh();
311: }
312:
313: /**
314: * Refreshes the underlying control to be in sync. with the DOM node.
315: */
316: public void refresh() {
317: theTargetNode = null;
318: String nodeText = getNodeText();
319: if (!getText().equals(nodeText)) {
320: removeModifyListener();
321: setText(nodeText);
322: addModifyListener();
323: }
324: }
325:
326: abstract protected void addModifyListener();
327:
328: abstract protected void removeModifyListener();
329:
330: public void modifyText(ModifyEvent e) {
331: Node node = getTargetNode();
332: if (!deleteWhenEmpty) {
333: if (node == null) {
334: addTheNode();
335: } else {
336: synchronizeNodeWithControl();
337: }
338: } else {
339: if (node == null) {
340: if (getText().trim().length() != 0) {
341: addTheNode();
342: }
343: } else {
344: synchronizeNodeWithControl();
345: }
346: }
347: }
348:
349: public void addXmlModificationListener(
350: IXmlModificationListener listener) {
351: xmlListeners.add(listener);
352: }
353:
354: public void removeXmlModificationListener(
355: IXmlModificationListener listener) {
356: xmlListeners.remove(listener);
357: }
358:
359: public void fireXmlModificationEvent(
360: final XmlModificationEvent event) {
361: Object[] listeners = xmlListeners.getListeners();
362: for (int i = 0; i < listeners.length; ++i) {
363: final IXmlModificationListener l = (IXmlModificationListener) listeners[i];
364: SafeRunnable.run(new SafeRunnable() {
365: public void run() {
366: l.contentChanged(event);
367: }
368: });
369: }
370: }
371:
372: public void setUseCData(boolean use) {
373: useCData = use;
374: }
375:
376: public boolean getUseCData() {
377: return useCData;
378: }
379:
380: public void setDeleteWhenEmpty(boolean delete) {
381: deleteWhenEmpty = delete;
382: }
383:
384: public boolean getDeleteWhenEmpty() {
385: return deleteWhenEmpty;
386: }
387: }
|