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