/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.transformation.ui.builder;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.I18nUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.query.IQueryFactory;
import org.teiid.designer.query.IQueryService;
import org.teiid.designer.query.sql.lang.ICompoundCriteria;
import org.teiid.designer.query.sql.lang.ICriteria;
import org.teiid.designer.query.sql.lang.IExpression;
import org.teiid.designer.query.sql.lang.ILanguageObject;
import org.teiid.designer.query.sql.lang.INotCriteria;
import org.teiid.designer.query.sql.lang.IPredicateCriteria;
import org.teiid.designer.query.sql.symbol.IConstant;
import org.teiid.designer.query.sql.symbol.IFunction;
import org.teiid.designer.transformation.ui.UiConstants;
import org.teiid.designer.transformation.ui.builder.util.LanguageObjectContentProvider;
import org.teiid.query.ui.builder.util.BuilderUtils;
import org.teiid.query.ui.builder.util.LanguageObjectLabelProvider;
/**
* @since 8.0
*/
public class LanguageObjectBuilderTreeViewer extends TreeViewer implements ILanguageObjectInputProvider, UiConstants {
private static final String PREFIX = I18nUtil.getPropertyPrefix(LanguageObjectBuilderTreeViewer.class);
LanguageObjectContentProvider contentProvider;
private ILanguageObject langObj;
public LanguageObjectBuilderTreeViewer( Composite theParent ) {
super(theParent, SWT.NONE);
contentProvider = new LanguageObjectContentProvider();
setContentProvider(contentProvider);
setLabelProvider(new LanguageObjectLabelProvider());
getTree().setLayoutData(new GridData(GridData.FILL_BOTH));
}
public void addUndefinedAndCriteria() {
addUndefinedCriteria((ICriteria)getSelectedObject(), true);
}
private void addUndefinedCriteria( ICriteria theCriteria,
boolean theAndFlag ) {
CoreArgCheck.isNotNull(theCriteria); // should not be calling if null
Object newSelection = StructuredSelection.EMPTY;
if (theCriteria instanceof ICompoundCriteria) {
ICompoundCriteria criteria = (ICompoundCriteria)theCriteria;
criteria.addCriteria((ICriteria)null);
refresh(true);
newSelection = contentProvider.getChildAt((criteria.getCriteriaCount() - 1), criteria);
} else if (theCriteria instanceof INotCriteria) {
// the contained Criteria must be a CompoundCriteria
addUndefinedCriteria(((INotCriteria)theCriteria).getCriteria(), theAndFlag);
} else if (theCriteria instanceof IPredicateCriteria) {
IQueryService queryService = ModelerCore.getTeiidQueryService();
IQueryFactory factory = queryService.createQueryFactory();
List<? extends ICriteria> criteriaList = Arrays.asList(theCriteria, null);
ICompoundCriteria compoundCriteria = factory.createCompoundCriteria(
(theAndFlag) ? ICompoundCriteria.AND : ICompoundCriteria.OR,
criteriaList);
// modify parent here
ILanguageObject parent = (ILanguageObject)contentProvider.getParent(theCriteria);
int index = contentProvider.getChildIndex(theCriteria);
if (parent == null) {
setLanguageObject(compoundCriteria);
refresh(true);
// select undefined criteria
newSelection = contentProvider.getChildAt(1, contentProvider.getRoot());
expandToLevel(newSelection, ALL_LEVELS);
} else if ((parent instanceof INotCriteria) || (parent instanceof ICompoundCriteria)) {
ICompoundCriteria criteria = null;
if (parent instanceof INotCriteria) {
// then NotCriteria's contained criteria must be a CompoundCriteria
criteria = (ICompoundCriteria)((INotCriteria)parent).getCriteria();
} else {
criteria = (ICompoundCriteria)parent;
}
List crits = criteria.getCriteria();
crits.set(index, compoundCriteria);
refresh(true);
// select undefined criteria
newSelection = contentProvider.getChildAt(1, compoundCriteria);
} else {
CoreArgCheck.isTrue(false, Util.getString(PREFIX + "unexpectedType", //$NON-NLS-1$
new Object[] {"addUndefinedCriteria", parent.getClass()})); //$NON-NLS-1$
}
}
// select appropriate tree item
final Object selection = newSelection;
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
setSelection(new StructuredSelection(selection));
}
});
}
public void addUndefinedOrCriteria() {
addUndefinedCriteria((ICriteria)getSelectedObject(), false);
}
private boolean canDelete( Object theLangObj ) {
boolean result = true;
ILanguageObject parent = (ILanguageObject)contentProvider.getParent(theLangObj);
if (parent == null) {
// object is root. can only delete if not undefined
result = !isUndefined(theLangObj);
} else if (parent instanceof IFunction) {
// all function arguments except conversion type constants can be deleted
if (theLangObj instanceof IConstant) {
result = !BuilderUtils.isConversionType((IConstant)theLangObj);
}
} else if (parent instanceof INotCriteria) {
// get compound criteria and ask again
INotCriteria notCrit = (INotCriteria)parent;
ICriteria criteria = notCrit.getCriteria();
// NotCriteria must contain either another NotCriteria or a CompoundCriteria
while (!(criteria instanceof ICompoundCriteria)) {
// must be a NotCriteria
criteria = ((INotCriteria)criteria).getCriteria();
result = canDelete(criteria);
}
}
return result;
}
public boolean canDeleteSelection() {
return canDelete(getSelectedObject());
}
private void delete( Object theLangObj ) {
if (canDelete(theLangObj)) {
boolean doSelect = true;
//
// set the parent object to reflect the change in it's child. parent can't be undefined.
//
Object newSelection = StructuredSelection.EMPTY;
ILanguageObject parent = (ILanguageObject)contentProvider.getParent(theLangObj);
if (parent == null) {
// object being delete is root language object
setLanguageObject(null); // resets undefined count
refresh(true);
newSelection = contentProvider.getRoot();
expandToLevel(newSelection, ALL_LEVELS);
} else if (parent instanceof IFunction) {
// set the arg to null in parent
int index = contentProvider.getChildIndex(theLangObj);
IExpression[] args = ((IFunction)parent).getArgs();
args[index] = null;
refresh(true);
newSelection = contentProvider.getChildAt(index, parent);
} else if (parent instanceof ICriteria) {
if (contentProvider.getChildCount(parent) > 1) {
ICompoundCriteria compoundCriteria = null;
// parent is either a compound criteria or not criteria
if (parent instanceof ICompoundCriteria) {
compoundCriteria = (ICompoundCriteria)parent;
} else { // NotCriteria
INotCriteria notCrit = (INotCriteria)parent;
ICriteria criteria = notCrit.getCriteria();
// NotCriteria must contain either another NotCriteria or a CompoundCriteria
while (!(criteria instanceof ICompoundCriteria)) {
// must be a NotCriteria
criteria = ((INotCriteria)criteria).getCriteria();
}
compoundCriteria = (ICompoundCriteria)criteria;
}
List crits = compoundCriteria.getCriteria();
int index = contentProvider.getChildIndex(theLangObj);
// CompoundCriteria has to have at least 2 criteria to be compound. if it has more than 2
// just delete the selected obj. if there are exactly 2 criteria than this special processing block occurs.
if (contentProvider.getChildCount(parent) == 2) {
int siblingIndex = (index == 0) ? 1 : 0;
// if deleting this node would leave just an undefined node under the parent, delete parent
if (isUndefined(contentProvider.getChildAt(siblingIndex, parent))) {
delete(parent);
} else {
// sibling is not undefined
// if deleting an undefined node, delete from parent. this will leave one node
// under the parent.
if (isUndefined(theLangObj)) {
crits.remove(index);
// selection taken care of below in block checking for size of 1
} else {
// replace deleted node with undefined node
crits.set(index, (ICriteria)null);
refresh(true);
newSelection = contentProvider.getChildAt(index, parent);
}
}
} else {
// just remove from parent
crits.remove(index);
refresh(true);
newSelection = parent;
}
if (crits.size() == 1) {
// need to modify parent node with the child (refresh done there)
modifyLanguageObject(parent, (ILanguageObject)contentProvider.getChildAt(0, parent), false);
doSelect = false; // modify does select
}
}
}
if (doSelect) {
final Object selection = newSelection;
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
setSelection(new StructuredSelection(selection));
}
});
}
}
}
public void deleteSelection() {
delete(getSelectedObject());
}
/**
* @see org.teiid.designer.transformation.ui.builder.ILanguageObjectInputProvider#getLanguageObject()
*/
@Override
public ILanguageObject getLanguageObject() {
return langObj;
}
// returns null if no selection
// if there is a selection it will either by a LanguageObject or BuilderUtils.UNDEFINED
public Object getSelectedObject() {
Object result = null;
IStructuredSelection selection = (IStructuredSelection)getSelection();
if (selection.size() == 1) {
result = selection.getFirstElement();
}
return result;
}
/**
* Indicates if the given object has undefined children.
*
* @param theLangObj the object being queried
* @return <code>true</code> if undefined child exist; <code>false</code> otherwise.
*/
public boolean hasUndefinedChild( Object theLangObj ) {
boolean result = false;
Object[] kids = contentProvider.getChildren(theLangObj);
if (kids.length > 0) {
// go in reverse order since it is more likely undefined will be at the end
for (int i = (kids.length - 1); i >= 0; i--) {
if (isUndefined(kids[i])) {
result = true;
} else {
result = hasUndefinedChild(kids[i]);
}
if (result) {
break;
}
}
}
return result;
}
public boolean isComplete() {
Object root = contentProvider.getRoot();
boolean result = ((root == null) || (isUndefined(root))) ? false : !hasUndefinedChild(root);
return result;
}
public boolean isUndefined( Object theLangObj ) {
return ((theLangObj == null) || theLangObj.equals(BuilderUtils.UNDEFINED));
}
private void modifyLanguageObject( Object theObject,
ILanguageObject theNewValue,
boolean retainSelection ) {
CoreArgCheck.isNotNull(theNewValue); // should not be null when modifying
//
// set the parent object to reflect the change in it's child. parent's can't be undefined
//
Object newSelection = StructuredSelection.EMPTY;
ILanguageObject parent = (ILanguageObject)contentProvider.getParent(theObject);
Object newValue = theNewValue;
if (parent == null) {
// root language object
setLanguageObject((ILanguageObject)newValue);
refresh(true);
newSelection = contentProvider.getRoot();
expandToLevel(newSelection, ALL_LEVELS);
if (newSelection instanceof IFunction) {
IFunction function = (IFunction)newSelection;
if (function.getArgs().length > 0) {
newSelection = contentProvider.getChildAt(0, function);
}
}
} else if (parent instanceof IFunction) {
// set the arg to new value in parent
int index = contentProvider.getChildIndex(theObject);
IExpression[] args = ((IFunction)parent).getArgs();
args[index] = (IExpression)newValue;
refresh(true);
expandToLevel(parent, ALL_LEVELS);
newSelection = contentProvider.getChildAt(index, parent);
if (!retainSelection) {
if (newSelection instanceof IFunction) {
// if function arg change to be a function, select first function arg
if (contentProvider.getChildCount(newSelection) > 0) {
newSelection = contentProvider.getChildAt(0, newSelection);
}
} else {
// select next sibling function arg if exists
if ((args.length - 1) > index) {
newSelection = contentProvider.getChildAt(index + 1, parent);
} else if (index > 0) {
newSelection = contentProvider.getChildAt(0, parent);
} else {
newSelection = contentProvider.getChildAt(index, parent);
}
}
}
} else if (parent instanceof ICriteria) {
// theLangObj must also be a Criteria
// since NotCriteria aren't really edited in the editor (their contained criteria is). Need to
// save the state in order to restore it when modifying
ICriteria newCriteria = (ICriteria)newValue;
int index = contentProvider.getChildIndex(theObject);
if ((parent instanceof ICompoundCriteria) || (parent instanceof INotCriteria)) {
ICompoundCriteria compoundCriteria = null;
if (parent instanceof ICompoundCriteria) {
compoundCriteria = (ICompoundCriteria)parent;
} else {
compoundCriteria = (ICompoundCriteria)((INotCriteria)parent).getCriteria();
}
List criteriaCollection = compoundCriteria.getCriteria();
criteriaCollection.set(index, newCriteria);
refresh(true);
if (retainSelection) {
newSelection = criteriaCollection.get(index);
} else {
// set selection to next sibling criteria or to the first sibling
if ((criteriaCollection.size() - 1) > index) {
newSelection = criteriaCollection.get(index + 1);
} else if (index > 0) {
newSelection = criteriaCollection.get(0);
} else {
newSelection = contentProvider.getChildAt(index + 1, parent);
}
}
} else {
CoreArgCheck.isTrue(false, Util.getString(PREFIX + "unexpectedType", //$NON-NLS-1$
new Object[] {"modifyLanguageObject", //$NON-NLS-1$
parent.getClass().getName()}));
}
expandToLevel(parent, ALL_LEVELS);
}
// select next node
setSelection((newSelection == null) ? StructuredSelection.EMPTY : new StructuredSelection(newSelection));
}
public void modifyNotCriteriaStatus() {
Object selectedObj = getSelectedObject();
CoreArgCheck.isNotNull(selectedObj); // should not be calling if no row selected
if (selectedObj instanceof INotCriteria) {
modifySelectedItem(((INotCriteria)selectedObj).getCriteria(), true);
} else if (selectedObj instanceof ICriteria) {
IQueryService queryService = ModelerCore.getTeiidQueryService();
IQueryFactory factory = queryService.createQueryFactory();
modifySelectedItem(factory.createNotCriteria((ICriteria)selectedObj), true);
} else if (isUndefined(selectedObj)) {
CoreArgCheck.isTrue(false, Util.getString(PREFIX + "unexpectedType", //$NON-NLS-1$
new Object[] {"modifyNotCriteriaStatus", BuilderUtils.UNDEFINED})); //$NON-NLS-1$
} else {
CoreArgCheck.isTrue(false, Util.getString(PREFIX + "unexpectedType", //$NON-NLS-1$
new Object[] {"modifyNotCriteriaStatus", selectedObj.getClass()})); //$NON-NLS-1$
}
}
public void modifySelectedItem( ILanguageObject theLangObj,
boolean retainSelection ) {
modifyLanguageObject(getSelectedObject(), theLangObj, retainSelection);
}
public void selectRoot() {
// put this in the back of the UI thread event queue.
// at startup the listeners we're working. this makes sure the listeners have all been wired.
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
setSelection(new StructuredSelection(contentProvider.getRoot()));
}
});
}
public void setLanguageObject( ILanguageObject theLangObj ) {
langObj = theLangObj;
setInput(this);
}
}