blob: 38878cff6818c70c974f73e2dc326b28e22c6252 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.bpel.ui.properties;
import java.util.ArrayList;
import java.util.Collections;
import javax.xml.namespace.QName;
import org.eclipse.bpel.common.ui.details.IDetailsAreaConstants;
import org.eclipse.bpel.common.ui.flatui.FlatFormAttachment;
import org.eclipse.bpel.common.ui.flatui.FlatFormData;
import org.eclipse.bpel.model.Assign;
import org.eclipse.bpel.model.BPELFactory;
import org.eclipse.bpel.model.Copy;
import org.eclipse.bpel.model.From;
import org.eclipse.bpel.model.Query;
import org.eclipse.bpel.model.To;
import org.eclipse.bpel.model.Variable;
import org.eclipse.bpel.model.messageproperties.Property;
import org.eclipse.bpel.model.util.BPELConstants;
import org.eclipse.bpel.model.util.BPELUtils;
import org.eclipse.bpel.model.util.XSD2XMLGenerator;
import org.eclipse.bpel.ui.Messages;
import org.eclipse.bpel.ui.adapters.IVirtualCopyRuleSide;
import org.eclipse.bpel.ui.commands.InsertCopyCommand;
import org.eclipse.bpel.ui.details.providers.ModelTreeLabelProvider;
import org.eclipse.bpel.ui.details.providers.VariableTreeContentProvider;
import org.eclipse.bpel.ui.details.tree.ITreeNode;
import org.eclipse.bpel.ui.util.BPELUtil;
import org.eclipse.bpel.ui.util.XSDUtils;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.PlatformUI;
import org.eclipse.wst.wsdl.Message;
import org.eclipse.wst.wsdl.Part;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDNamedComponent;
import org.eclipse.xsd.XSDTypeDefinition;
import org.eclipse.xsd.impl.XSDTypeDefinitionImpl;
/**
* An AssignCategory presenting a tree from which the user can select any of: -
* a Variable - a Part within a Variable - some XSD element within a Part within
* a Variable.
*
*/
public class VariablePartAssignCategory extends AssignCategoryBase {
Label fNameLabel;
Text fNameText;
Tree fVariableTree;
TreeViewer fVariableViewer;
VariableTreeContentProvider variableContentProvider;
Shell shell;
protected VariablePartAssignCategory(BPELPropertySection anOwnerSection) {
super(anOwnerSection);
}
/**
* @see org.eclipse.bpel.ui.properties.IAssignCategory#getName()
*/
@Override
public String getName() {
return Messages.VariablePartAssignCategory_Variable_or_Part_1;
}
protected boolean isPropertyTree() {
return false;
}
/**
* This is for XPath specific queries.
*
* TODO: Need to somehow externalized this for for non-XPath languages.
*/
@SuppressWarnings("nls")
protected void updateQueryFieldFromTreeSelection() {
if (displayQuery() == false || fChangeHelper.isNonUserChange()
|| this.fModelObject == null || fModelObject.eContainer()==null) {
return;
}
IStructuredSelection sel = (IStructuredSelection) fVariableViewer
.getSelection();
Object[] path = variableContentProvider.getPathToRoot(sel
.getFirstElement());
StringBuilder builder = new StringBuilder();
ArrayList<String> querySegments = new ArrayList<String>();
for (Object next : path) {
Object eObj = BPELUtil.resolveXSDObject(BPELUtil.adapt(next,
ITreeNode.class).getModelObject());
builder.setLength(0);
String targetNamespace = null;
String namespacePrefix = null;
if (eObj instanceof XSDAttributeDeclaration) {
XSDAttributeDeclaration att = (XSDAttributeDeclaration) eObj;
targetNamespace = att.getTargetNamespace();
builder.append("/@");
if (targetNamespace != null) {
namespacePrefix = BPELUtil.lookupOrCreateNamespacePrefix(
fModelObject, targetNamespace, "xsd", shell);
if (namespacePrefix == null) {
break;
}
builder.append(namespacePrefix).append(":");
}
builder.append(att.getName());
} else if (eObj instanceof XSDElementDeclaration) {
XSDElementDeclaration elm = (XSDElementDeclaration) eObj;
targetNamespace = elm.getTargetNamespace();
int maxOccurs = XSDUtils.getMaxOccurs(elm);
builder.append("/");
if (targetNamespace != null) {
namespacePrefix = BPELUtil.lookupOrCreateNamespacePrefix(
fModelObject, targetNamespace, "xsd", shell);
if (namespacePrefix == null) {
break;
}
builder.append(namespacePrefix).append(":");
}
builder.append(elm.getName());
// Unbounded or bounded by something higher then 1.
if (maxOccurs != 1) {
builder.append("[1]");
}
}
// If the current builder has length > 0, then there is a query
// segment for us to put in.
if (builder.length() > 0) {
querySegments.add(builder.toString());
}
}
Collections.reverse(querySegments);
builder.setLength(0);
for (String s : querySegments) {
builder.append(s);
}
if (builder.length() > 0) {
builder.deleteCharAt(0);
}
try {
fChangeHelper.startNonUserChange();
fNameText.setText(builder.toString());
fNameText.setEnabled(true);
fNameLabel.setEnabled(true);
} finally {
fChangeHelper.finishNonUserChange();
}
}
@Override
protected void createClient2(Composite parent) {
FlatFormData data;
fVariableTree = fWidgetFactory
.createTree(parent, SWT.NONE /* SWT.BORDER */);
if (displayQuery()) {
// area for query string and wizard button
fNameLabel = fWidgetFactory.createLabel(parent,
Messages.VariablePartAssignCategory_Query__8);
fNameText = fWidgetFactory.createText(parent, ""); //$NON-NLS-1$
data = new FlatFormData();
data.left = new FlatFormAttachment(0, BPELUtil.calculateLabelWidth(
fNameText, STANDARD_LABEL_WIDTH_SM));
data.right = new FlatFormAttachment(100,
-IDetailsAreaConstants.HSPACE);
data.bottom = new FlatFormAttachment(100, 0);
data.top = new FlatFormAttachment(100, (-1)
* (fNameText.getLineHeight() + 4 * fNameText
.getBorderWidth()) - IDetailsAreaConstants.VSPACE);
fNameText.setLayoutData(data);
fChangeHelper.startListeningTo(fNameText);
fChangeHelper.startListeningForEnter(fNameText);
data = new FlatFormData();
data.left = new FlatFormAttachment(0, 0);
data.right = new FlatFormAttachment(fNameText,
-IDetailsAreaConstants.HSPACE);
data.top = new FlatFormAttachment(fNameText, 0, SWT.CENTER);
fNameLabel.setLayoutData(data);
}
data = new FlatFormData();
data.left = new FlatFormAttachment(0, 0);
data.top = new FlatFormAttachment(0, 0);
data.right = new FlatFormAttachment(100, 0);
if (displayQuery()) {
data.bottom = new FlatFormAttachment(fNameText,
-IDetailsAreaConstants.VSPACE, SWT.TOP);
} else {
data.bottom = new FlatFormAttachment(100, 0);
}
// data.borderType = IBorderConstants.BORDER_2P2_BLACK;
fVariableTree.setLayoutData(data);
variableContentProvider = new VariableTreeContentProvider(true,
isPropertyTree(), displayQuery());
fVariableViewer = new TreeViewer(fVariableTree);
fVariableViewer.setContentProvider(variableContentProvider);
fVariableViewer.setLabelProvider(new ModelTreeLabelProvider());
// TODO: does this sorter work at the top level? does it affect nested
// levels too?
// variableViewer.setSorter(ModelViewerSorter.getInstance());
fVariableViewer.setInput(fOwnerSection.getModel());
fVariableViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
updateQueryFieldFromTreeSelection();
}
});
fChangeHelper.startListeningTo(fVariableTree);
}
/**
* @see org.eclipse.bpel.ui.properties.IAssignCategory#isCategoryForModel(org.eclipse.emf.ecore.EObject)
*/
@Override
public boolean isCategoryForModel(EObject aModel) {
if (aModel == null) {
return false;
}
IVirtualCopyRuleSide side = BPELUtil.adapt(aModel,
IVirtualCopyRuleSide.class);
if (side == null) {
return false;
}
return side.getVariable() != null
&& (side.getProperty() != null) == isPropertyTree();
}
@SuppressWarnings("nls")
@Override
protected void load(IVirtualCopyRuleSide side) {
fChangeHelper.startNonUserChange();
try {
fVariableViewer.setSelection(StructuredSelection.EMPTY, false);
if (displayQuery()) {
fNameText.setText(EMPTY_STRING);
fNameText.setEnabled(true);
fNameLabel.setEnabled(true);
}
} finally {
fChangeHelper.finishNonUserChange();
}
ArrayList<ITreeNode> pathToNode = new ArrayList<ITreeNode>();
ITreeNode node = null;
// First, find the variable node.
Object context = side.getVariable();
if (context != null) {
Object[] items = variableContentProvider
.getElements(fVariableViewer.getInput());
node = variableContentProvider.findModelNode(items, context, 0);
if (node != null) {
pathToNode.add(node);
}
}
// Find the part (or property) node within the container node.
if (isPropertyTree()) {
context = side.getProperty();
} else {
context = side.getPart();
}
if (node != null && context != null) {
Object[] items = variableContentProvider.getChildren(node);
node = variableContentProvider.findModelNode(items, context,
variableContentProvider.isCondensed() ? 0 : 1);
if (node != null) {
pathToNode.add(node);
}
}
if (context == null) {
context = side.getVariable();
}
String query = null;
if (node != null && context != null) {
Query queryObject = side.getQuery();
if (queryObject != null) {
// TODO: we shouldn't ignore the queryLanguage here!!
query = queryObject.getValue();
}
if (query != null && !query.equals(EMPTY_STRING)) {
int tokenCount = 0;
outer: for (String token : query.split("\\/")) {
tokenCount += 1;
if (token.length() == 0) {
// Is it the first empty string preceeding the first /
if (tokenCount == 1) {
continue;
}
// could be // , as in //foo:bar/bar, which is
// impossible to show here.
break outer;
}
QueryStep step = new QueryStep(token);
step.updateNamespaceURI(this.fModelObject);
if (step.fAxis.equals("child") == false) {
break outer;
}
Object[] items = variableContentProvider.getChildren(node);
inner: for (Object item : items) {
Object originalMatch = ((ITreeNode) item)
.getModelObject();
Object match = BPELUtil.resolveXSDObject(originalMatch);
if (match instanceof XSDElementDeclaration) {
XSDElementDeclaration elmDecl = (XSDElementDeclaration) match;
if (match(step, elmDecl) == false) {
continue;
}
node = variableContentProvider.findModelNode(items,
originalMatch, 0);
// no matching node, we are done
if (node == null) {
break outer;
}
pathToNode.add(node);
break inner;
} else if (match instanceof XSDAttributeDeclaration) {
XSDAttributeDeclaration attrDecl = (XSDAttributeDeclaration) match;
if (match(step, attrDecl)) {
node = variableContentProvider.findModelNode(
items, originalMatch, 0);
if (node != null) {
pathToNode.add(node);
}
}
// attribute is the leaf node
break outer;
}
}
}
}
}
if (pathToNode.size() > 0) {
node = pathToNode.get(pathToNode.size() - 1);
}
if (node != null) {
fChangeHelper.startNonUserChange();
try {
if (displayQuery()) {
fNameText.setText(query == null ? EMPTY_STRING : query);
}
fVariableViewer.expandToLevel(node, 0);
fVariableViewer.setSelection(new StructuredSelection(node),
true);
} finally {
fChangeHelper.finishNonUserChange();
}
}
}
boolean match(QueryStep step, XSDNamedComponent xsdNamed) {
// local name
if (step.fLocalPart.equals(xsdNamed.getName()) == false) {
return false;
}
// namespace
return (step.fNamespaceURI.equals(xsdNamed.getTargetNamespace()) || (step.fNamespaceURI
.equals(EMPTY_STRING) && xsdNamed.getTargetNamespace() == null));
}
@Override
protected void store(IVirtualCopyRuleSide side) {
IStructuredSelection sel = (IStructuredSelection) fVariableViewer
.getSelection();
Object[] path = variableContentProvider.getPathToRoot(sel
.getFirstElement());
String query = displayQuery() ? fNameText.getText() : EMPTY_STRING;
side.setVariable(null);
side.setPart(null);
side.setProperty(null);
side.setQuery(null);
for (Object n : path) {
ITreeNode treeNode = BPELUtil.adapt(n, ITreeNode.class);
Object model = treeNode.getModelObject();
if (model instanceof Variable) {
side.setVariable((Variable) model);
}
if (model instanceof Part) {
side.setPart((Part) model);
}
if (model instanceof Property) {
side.setProperty((Property) model);
}
}
query = query.trim();
if (displayQuery() && query.length() > 0) {
Query queryObject = BPELFactory.eINSTANCE.createQuery();
queryObject
.setQueryLanguage(BPELConstants.XMLNS_XPATH_QUERY_LANGUAGE);
queryObject.setValue(query);
side.setQuery(queryObject);
} else {
side.setQuery(null);
}
// https://jira.jboss.org/browse/JBIDE-7351
// TODO: Think about how to implement this in a way that makes sense...
// currently, a popup dialog asks the user to initialize variables whenever
// focus changes - this makes for a bad user experience!
// From?
if (side.isSource())
return;
// if initializer doesn't exist and query != null then we should
// generate it
if (query == null)
return;
Variable var = side.getVariable();
if (needInitializer(var, side)) {
if (MessageDialog
.openQuestion(
PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getShell(),
"Initializer",
NLS
.bind(
"Variable {0} doesn't have initializer. Should it be generated?",
(new Object[] { var.getName() })))) {
initTargetVariable(var, side);
}
}
}
private boolean needInitializer(Variable var, IVirtualCopyRuleSide side) {
Assign a = (Assign) ((To) side.getCopyRuleSide()).getContainer()
.getContainer();
EList<Copy> list = a.getCopy();
for (Copy copy : list) {
if (copy.getFrom().getLiteral() == null)
continue;
To to = copy.getTo();
Variable v = to.getVariable();
if (v == null)
continue;
if (!v.equals(var))
continue;
Part p = to.getPart();
if (p == null)
continue;
if (p.equals(side.getPart()))
return false;
}
// https://issues.jboss.org/browse/JBIDE-8048
// check if initialization is possible: this is not possible if the
// variable is implicit (e.g. in a ForEach or OnEvent)
String rootElement = null;
String uriWSDL = null;
// Variable is defined using "messageType"
Message msg = var.getMessageType();
if (msg != null && !msg.eIsProxy()) {
// https://jira.jboss.org/browse/JBIDE-6697
// from eclipse.org/bpel rev 1.17 on 7/23/2010 3:13AM bugzilla 302943 by gqian: apply the patch from bugzilla
if (side.getPart() != null) {
XSDElementDeclaration declaration = side.getPart().getElementDeclaration();
if (declaration != null) {
uriWSDL = declaration.getSchema().getSchemaLocation();
rootElement = declaration.getName();
}
}
}
// can we construct an initializer for this variable?
// Variable is defined using "type"
XSDTypeDefinitionImpl type = (XSDTypeDefinitionImpl)var.getType();
if (type != null && !type.eIsProxy()) {
QName qname = new QName(type.getTargetNamespace(), type.getName());
rootElement = qname.getLocalPart();
uriWSDL = type.eResource().getURI().toString();
}
// Variable is defined using "element"
XSDElementDeclaration element = var.getXSDElement();
if (element != null && !element.eIsProxy()) {
QName qname = new QName(element.getTargetNamespace(), element
.getName());
rootElement = qname.getLocalPart();
uriWSDL = element.eResource().getURI().toString();
}
// Incomplete variable definition
if (rootElement == null || uriWSDL == null) {
return false;
}
return true;
}
private void initTargetVariable(Variable var, IVirtualCopyRuleSide side) {
String rootElement = null;
String uriWSDL = null;
// Variable is defined using "messageType"
Message msg = var.getMessageType();
if (msg != null) {
// https://jira.jboss.org/browse/JBIDE-6697
// from eclipse.org/bpel rev 1.17 on 7/23/2010 3:13AM bugzilla 302943 by gqian: apply the patch from bugzilla
if (side.getPart() != null) {
XSDElementDeclaration declaration = side.getPart().getElementDeclaration();
if (declaration != null) {
uriWSDL = declaration.getSchema().getSchemaLocation();
rootElement = declaration.getName();
}
}
}
// Variable is defined using "type"
XSDTypeDefinition type = var.getType();
if (type != null) {
QName qname = new QName(type.getTargetNamespace(), type.getName());
rootElement = qname.getLocalPart();
uriWSDL = type.eResource().getURI().toString();
}
// Variable is defined using "element"
XSDElementDeclaration element = var.getXSDElement();
if (element != null) {
QName qname = new QName(element.getTargetNamespace(), element
.getName());
rootElement = qname.getLocalPart();
uriWSDL = element.eResource().getURI().toString();
}
// Incomplete variable definition
if (rootElement == null || uriWSDL == null) {
return;
}
// https://jira.jboss.org/browse/JBIDE-7351
// use the new and improved XSD -> XML generator
// this was an internal class that was moved to org.eclipse.bpel.model.util
XSD2XMLGenerator generator = new XSD2XMLGenerator(uriWSDL, rootElement);
// be sure to tell the generator which elements in a "choice" we are interested in
if (side.getQuery()!=null && side.getQuery().getValue().trim().length()>0)
generator.setQueryPath(side.getQuery().getValue());
try {
String literal = generator.createXML();
Copy copy = BPELFactory.eINSTANCE.createCopy();
Assign a = (Assign) ((To) side.getCopyRuleSide()).getContainer()
.getContainer();
getCommandFramework()
.execute(
wrapInShowContextCommand(new InsertCopyCommand(a,
copy, 0)));
To to = BPELFactory.eINSTANCE.createTo();
From from = BPELFactory.eINSTANCE.createFrom();
from.setLiteral(literal);
copy.setFrom(from);
to.setVariable(side.getVariable());
to.setPart(side.getPart());
copy.setTo(to);
fChangeHelper.startNonUserChange();
fOwnerSection.basicSetInput(a);
fChangeHelper.finishNonUserChange();
} catch (Exception e) {
throw new IllegalStateException(
"Can't generate initializer, check WSDL file");
}
}
@Override
protected void basicSetInput(EObject newInput) {
super.basicSetInput(newInput);
/**
* During initialization of variable, the list of available variables
* must not include the current variable and the variables logically
* after. So we just alter the input to the variable viewer, if we are
* looking at a variable. In assign statement, where the model is Copy,
* this does not happen.
*/
EObject container = newInput.eContainer();
if (container instanceof Variable) {
fChangeHelper.startNonUserChange();
try {
fVariableViewer.setInput(container);
} finally {
fChangeHelper.finishNonUserChange();
}
}
}
/**
* @see org.eclipse.bpel.ui.properties.BPELPropertySection#getUserContext()
*/
@Override
public Object getUserContext() {
return null;
}
/**
* @see org.eclipse.bpel.ui.properties.BPELPropertySection#restoreUserContext(java.lang.Object)
*/
@Override
public void restoreUserContext(Object userContext) {
fVariableTree.setFocus();
}
private boolean displayQuery() {
return !isPropertyTree();
}
/**
* Parse the query step into this object.
*
* TODO: This has to be externalized someplace. We can't just assume it is
* XPath all the time ...
*/
@SuppressWarnings("nls")
class QueryStep {
String fAxis = "child";
String fPrefix = EMPTY_STRING;
String fLocalPart = EMPTY_STRING;
String fNamespaceURI = EMPTY_STRING;
@SuppressWarnings("nls")
QueryStep(String step) {
int axisMark = step.indexOf("::");
if (axisMark >= 0) {
fAxis = step.substring(0, axisMark);
step = step.substring(axisMark + 2);
}
int qnameMark = step.indexOf(":");
if (qnameMark < 0) {
fLocalPart = step;
} else {
fLocalPart = step.substring(qnameMark + 1);
fPrefix = step.substring(0, qnameMark);
}
if (fLocalPart.charAt(0) == '@') {
fLocalPart = fLocalPart.substring(1);
}
int arrayMark1 = fLocalPart.indexOf('[');
int arrayMark2 = fLocalPart.indexOf(']');
if (arrayMark2 > arrayMark1 && arrayMark1 > 0) {
fLocalPart = fLocalPart.substring(0, arrayMark1);
}
}
void updateNamespaceURI(EObject eObj) {
if (fPrefix.length() > 0) {
fNamespaceURI = BPELUtils.getNamespace(eObj, fPrefix);
if (fNamespaceURI == null) {
fNamespaceURI = "urn:unresolved:"
+ System.currentTimeMillis() + ":" + fPrefix;
}
} else {
fNamespaceURI = EMPTY_STRING;
}
}
}
}