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