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: package org.pentaho.designstudio.controls;
014:
015: import java.util.ArrayList;
016: import java.util.Comparator;
017: import java.util.HashSet;
018: import java.util.Iterator;
019: import java.util.TreeSet;
020: import java.util.regex.Matcher;
021: import java.util.regex.Pattern;
022:
023: import org.dom4j.Element;
024: import org.eclipse.swt.SWT;
025: import org.eclipse.swt.dnd.DND;
026: import org.eclipse.swt.dnd.DropTarget;
027: import org.eclipse.swt.dnd.DropTargetEvent;
028: import org.eclipse.swt.dnd.DropTargetListener;
029: import org.eclipse.swt.dnd.Transfer;
030: import org.eclipse.swt.events.ModifyEvent;
031: import org.eclipse.swt.events.ModifyListener;
032: import org.eclipse.swt.events.SelectionEvent;
033: import org.eclipse.swt.events.SelectionListener;
034: import org.eclipse.swt.widgets.Combo;
035: import org.eclipse.swt.widgets.Composite;
036: import org.eclipse.swt.widgets.Control;
037: import org.pentaho.actionsequence.dom.AbstractIOElement;
038: import org.pentaho.actionsequence.dom.AbstractIOElement;
039: import org.pentaho.actionsequence.dom.ActionInput;
040: import org.pentaho.actionsequence.dom.ActionOutput;
041: import org.pentaho.actionsequence.dom.ActionSequenceDocument;
042: import org.pentaho.actionsequence.dom.ActionSequenceInput;
043: import org.pentaho.actionsequence.dom.IActionInputVariable;
044: import org.pentaho.actionsequence.dom.actions.ActionDefinition;
045:
046: /**
047: * An action definition input editor that is backed by an SWT combo box.
048: *
049: * @author Angelo Rodriguez
050: */
051: public class ActionInCombo implements IActionSequenceControl,
052: ModifyListener, SelectionListener {
053: protected Combo combo;
054: protected ActionDefinition actionDefinition;
055: protected String[] inputTypes;
056: protected String inputName;
057: DropTarget dropTarget;
058: protected String[] defaultConstants = null;
059: HashSet referencedInputs = new HashSet();
060: boolean removeInputWhenNoValue = true;
061:
062: class InputNameComparator implements Comparator {
063:
064: public int compare(Object o1, Object o2) {
065: int result = o1.toString().toUpperCase().compareTo(
066: o2.toString().toUpperCase());
067: if (result == 0) {
068: result = o1.toString().compareTo(o2.toString());
069: }
070: return result;
071: }
072:
073: }
074:
075: /**
076: * @param toolkit the toolkit being used to create UI components
077: * @param parent the parent control
078: * @param layoutData the check box layout data. Can be null.
079: */
080: public ActionInCombo(Composite parent, Object layoutData) {
081: combo = WidgetFactory.createCombo(parent, SWT.NONE);
082: if (layoutData != null) {
083: combo.setLayoutData(layoutData);
084: }
085:
086: dropTarget = new DropTarget(combo, DND.DROP_MOVE
087: | DND.DROP_COPY | DND.DROP_DEFAULT);
088: dropTarget
089: .setTransfer(new Transfer[] { ActionSequenceParamTransfer
090: .getInstance() });
091:
092: dropTarget.addDropListener(new DropTargetListener() {
093:
094: public void dragEnter(DropTargetEvent event) {
095: if (event.detail == DND.DROP_DEFAULT) {
096: if ((event.operations & DND.DROP_COPY) != 0) {
097: event.detail = DND.DROP_COPY;
098: } else {
099: event.detail = DND.DROP_NONE;
100: }
101: }
102: }
103:
104: public void dragOver(DropTargetEvent event) {
105: }
106:
107: public void dragOperationChanged(DropTargetEvent event) {
108: }
109:
110: public void dragLeave(DropTargetEvent event) {
111: }
112:
113: public void dropAccept(DropTargetEvent event) {
114: }
115:
116: public void drop(DropTargetEvent event) {
117: if (ActionSequenceParamTransfer.getInstance()
118: .isSupportedType(event.currentDataType)) {
119: AbstractIOElement param = (AbstractIOElement) event.data;
120: if ((param instanceof ActionSequenceInput)
121: || (param instanceof ActionOutput)) {
122: performDrop((AbstractIOElement) param);
123: }
124: }
125: }
126: });
127: combo.addModifyListener(this );
128: combo.addSelectionListener(this );
129: }
130:
131: /**
132: * Sets the target action definition input.
133: * @param actionDefinition the action definition being managed.
134: * @param inputName the name of the input within the action definition
135: * @param type the data type supported by this input
136: */
137: public void setTargetInput(ActionDefinition actionDefinition,
138: String inputName, String type) {
139: if (type != null) {
140: setTargetInput(actionDefinition, inputName,
141: new String[] { type });
142: } else {
143: setTargetInput(actionDefinition, inputName, (String[]) null);
144: }
145: }
146:
147: /**
148: * Sets the target action definition input.
149: * @param actionDefinition the action definition being managed.
150: * @param inputName the name of the input within the action definition
151: * @param types the data types supported by this input
152: */
153: public void setTargetInput(ActionDefinition actionDefinition,
154: String inputName, String[] types) {
155: this .actionDefinition = actionDefinition;
156: inputTypes = types;
157: this .inputName = inputName;
158: refresh();
159: }
160:
161: /**
162: * @return The action definition whose input is being managed.
163: */
164: public ActionDefinition getActionDefinition() {
165: return actionDefinition;
166: }
167:
168: /**
169: * @return The name of the action definition input being managed.
170: */
171: public String getInputName() {
172: return inputName;
173: }
174:
175: /**
176: * @return the input types supported by this input.
177: */
178: public String[] getInputTypes() {
179: return inputTypes;
180: }
181:
182: private void syncInputReferences() {
183: HashSet currentInputReferences = getReferencedInputs();
184: ArrayList addedInputs = new ArrayList();
185: ArrayList removedInputs = new ArrayList();
186: for (Iterator iter = currentInputReferences.iterator(); iter
187: .hasNext();) {
188: String inputName = (String) iter.next();
189: if (!referencedInputs.contains(inputName)) {
190: addedInputs.add(inputName);
191: referencedInputs.add(inputName);
192: actionDefinition.addInputParam(inputName,
193: ActionSequenceDocument.STRING_TYPE);
194: }
195: }
196: for (Iterator iter = referencedInputs.iterator(); iter
197: .hasNext();) {
198: String inputName = (String) iter.next();
199: if (!currentInputReferences.contains(inputName)) {
200: removedInputs.add(inputName);
201: }
202: }
203: for (Iterator iter = removedInputs.iterator(); iter.hasNext();) {
204: String inputName = (String) iter.next();
205: referencedInputs.remove(inputName);
206: Element componentDefElement = (Element) actionDefinition
207: .getElement().selectSingleNode(
208: ActionSequenceDocument.COMPONENT_DEF_NAME);
209: if (componentDefElement.equals(null)
210: || (componentDefElement.asXML().indexOf(
211: "{" + inputName + "}") == -1)) { //$NON-NLS-1$ //$NON-NLS-2$
212: ActionInput actionInput = actionDefinition
213: .getInputParam(inputName);
214: if (actionInput != null) {
215: actionInput.delete();
216: }
217: }
218: }
219: for (Iterator iter = addedInputs.iterator(); iter.hasNext();) {
220: String inputName = (String) iter.next();
221: referencedInputs.add(inputName);
222: actionDefinition.addInputParam(inputName,
223: ActionSequenceDocument.STRING_TYPE);
224: }
225: }
226:
227: /* (non-Javadoc)
228: * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
229: */
230: public void modifyText(ModifyEvent e) {
231: updateActionSequence();
232: syncInputReferences();
233: }
234:
235: public void refresh() {
236: combo.removeModifyListener(this );
237: combo.removeSelectionListener(this );
238: String comboText = ""; //$NON-NLS-1$
239: updateAvailActionInputs();
240: ActionInput actionInput = null;
241: String componentDefValue = null;
242: if ((inputName != null) && (inputName.trim().length() > 0)) {
243: componentDefValue = actionDefinition
244: .getComponentDefinitionValue(inputName);
245: actionInput = actionDefinition.getInputParam(inputName);
246: }
247: if (actionInput != null) {
248: comboText = ActionUtil.parameterToDisplayText(actionInput
249: .getReferencedVariableName());
250: } else if (componentDefValue != null) {
251: comboText = componentDefValue;
252: }
253: combo.setText(comboText);
254: combo.addSelectionListener(this );
255: combo.addModifyListener(this );
256: referencedInputs = getReferencedInputs();
257: }
258:
259: public void widgetDefaultSelected(SelectionEvent e) {
260: }
261:
262: public void widgetSelected(SelectionEvent e) {
263: updateActionSequence();
264: }
265:
266: public void updateActionSequence() {
267: String txtString = combo.getText().trim();
268: if (ActionUtil.isParamDispText(txtString)) {
269: actionDefinition.setComponentDefinition(inputName,
270: (String) null);
271: String mapping = ""; //$NON-NLS-1$
272: if (txtString.length() > 2) {
273: mapping = txtString
274: .substring(1, txtString.length() - 1).trim();
275: }
276: if (mapping.length() == 0) {
277: ActionInput actionInput = actionDefinition
278: .getInputParam(inputName);
279: if (actionInput != null) {
280: actionInput.delete();
281: }
282: } else {
283: IActionInputVariable inputVariable = null;
284: IActionInputVariable[] availInputs = actionDefinition
285: .getAvailInputVariables(inputTypes);
286: for (int i = 0; i < availInputs.length; i++) {
287: if (availInputs[i].getVariableName()
288: .equals(mapping)) {
289: inputVariable = availInputs[i];
290: break;
291: }
292: }
293:
294: String type = (inputVariable != null ? inputVariable
295: .getType() : ActionSequenceDocument.STRING_TYPE);
296: ActionInput actionInput = actionDefinition
297: .addInputParam(inputName, type);
298: actionInput.setMapping(mapping);
299: }
300: } else {
301: if ((txtString.length() > 0) || (!removeInputWhenNoValue)) {
302: actionDefinition.setInputValue(inputName, txtString);
303: } else {
304: actionDefinition.setInputValue(inputName, null);
305: }
306: }
307: }
308:
309: /**
310: * Sets a list of constant values the user can choose from.
311: * @param defaultConstants The list.
312: */
313: public void setDefaultConstants(String[] defaultConstants) {
314: this .defaultConstants = defaultConstants;
315: refresh();
316: }
317:
318: protected void performDrop(AbstractIOElement io) {
319: String droppedType = io.getType();
320: String droppedName = (io instanceof ActionOutput) ? ((ActionOutput) io)
321: .getPublicName()
322: : io.getName();
323: if ((droppedType != null) && (droppedType.trim().length() > 0)) {
324: boolean isTempType = (inputTypes == null);
325: if (isTempType) {
326: inputTypes = new String[] { droppedType };
327: }
328:
329: if ((io instanceof ActionSequenceInput)
330: && (actionDefinition.getDocument().getInput(
331: io.getName()) != null)) {
332: combo.setText(ActionUtil
333: .parameterToDisplayText(droppedName));
334: } else if (io instanceof ActionOutput) {
335: ActionOutput droppedOutput = (ActionOutput) io;
336: IActionInputVariable[] availInputs = actionDefinition
337: .getAvailInputVariables(inputTypes);
338: for (int i = 0; i < availInputs.length; i++) {
339: if (availInputs[i] instanceof ActionOutput) {
340: ActionOutput availInput = (ActionOutput) availInputs[i];
341: if (availInput.getName().equals(
342: droppedOutput.getName())
343: && availInput.getPublicName().equals(
344: droppedOutput.getPublicName())
345: && availInput.getType().equals(
346: droppedOutput.getType())) {
347: combo
348: .setText(ActionUtil
349: .parameterToDisplayText(droppedName));
350: break;
351: }
352: }
353: }
354: }
355:
356: if (isTempType) {
357: inputTypes = null;
358: }
359: }
360: }
361:
362: protected void updateAvailActionInputs() {
363: combo.setItems(new String[0]);
364: if (actionDefinition != null) {
365: TreeSet availParams = new TreeSet(new InputNameComparator());
366: availParams.add(""); //$NON-NLS-1$
367: IActionInputVariable[] availInputs = actionDefinition
368: .getAvailInputVariables(inputTypes);
369: for (int i = 0; i < availInputs.length; i++) {
370: availParams.add(ActionUtil
371: .parameterToDisplayText(availInputs[i]
372: .getVariableName()));
373: }
374: ArrayList items = new ArrayList(availParams);
375: if (defaultConstants != null) {
376: for (int i = 0; i < defaultConstants.length; i++) {
377: items.add(defaultConstants[i]);
378: }
379: }
380: combo.setItems((String[]) items.toArray(new String[0]));
381: }
382: }
383:
384: public String getText() {
385: return combo.getText();
386: }
387:
388: public void setText(String text) {
389: combo.setText(text);
390: }
391:
392: public Control getControl() {
393: return combo;
394: }
395:
396: public HashSet getReferencedInputs() {
397: HashSet inputs = new HashSet();
398: String comboText = combo.getText();
399: Pattern pattern = Pattern.compile("\\{[a-zA-Z_0-9\\-]+\\}"); //$NON-NLS-1$
400: Matcher matcher = pattern.matcher(comboText);
401: int startIndex = 0;
402: while (matcher.find() && (startIndex < comboText.length())) {
403: String inputName = matcher.group();
404: inputName = inputName.substring(1, inputName.length() - 1);
405: inputs.add(inputName);
406: }
407: return inputs;
408: }
409:
410: public boolean getRemoveInputWhenNoValue() {
411: return removeInputWhenNoValue;
412: }
413:
414: public void setRemoveInputWhenNoValue(boolean removeInputWhenNoValue) {
415: this.removeInputWhenNoValue = removeInputWhenNoValue;
416: }
417: }
|