/*
* Copyright 2015 Nokia Solutions and Networks
* Licensed under the Apache License, Version 2.0,
* see license.txt file for details.
*/
package org.robotframework.ide.eclipse.main.plugin.tableeditor;
import static com.google.common.collect.Lists.newArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.ui.di.UIEventTopic;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.ISources;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PlatformUI;
import org.rf.ide.core.testdata.model.AModelElement;
import org.rf.ide.core.testdata.model.table.RobotExecutableRow;
import org.rf.ide.core.testdata.model.table.exec.descs.IExecutableRowDescriptor.ERowType;
import org.robotframework.ide.eclipse.main.plugin.model.RobotCase;
import org.robotframework.ide.eclipse.main.plugin.model.RobotCodeHoldingElement;
import org.robotframework.ide.eclipse.main.plugin.model.RobotElement;
import org.robotframework.ide.eclipse.main.plugin.model.RobotElementChange;
import org.robotframework.ide.eclipse.main.plugin.model.RobotElementChange.Kind;
import org.robotframework.ide.eclipse.main.plugin.model.RobotKeywordCall;
import org.robotframework.ide.eclipse.main.plugin.model.RobotKeywordDefinition;
import org.robotframework.ide.eclipse.main.plugin.model.RobotModelEvents;
import org.robotframework.ide.eclipse.main.plugin.model.RobotSetting;
import org.robotframework.ide.eclipse.main.plugin.model.RobotSetting.SettingsGroup;
import org.robotframework.ide.eclipse.main.plugin.model.RobotSettingsSection;
import org.robotframework.ide.eclipse.main.plugin.model.RobotSuiteFile;
import org.robotframework.ide.eclipse.main.plugin.model.RobotVariable;
import org.robotframework.ide.eclipse.main.plugin.navigator.ArtificialGroupingRobotElement;
import org.robotframework.red.viewers.TreeContentProvider;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
public class RobotOutlineContentProvider extends TreeContentProvider {
private final Map<SettingsGroup, ArtificialGroupingRobotElement> groupingElements = new HashMap<>();
private TreeViewer viewer;
@Inject
@Optional
@Named(ISources.ACTIVE_SITE_NAME)
private IViewSite site;
public RobotOutlineContentProvider() {
final IEclipseContext activeContext = getContext().getActiveLeaf();
ContextInjectionFactory.inject(this, activeContext);
}
ArtificialGroupingRobotElement getGroupingElement(final SettingsGroup group) {
return groupingElements.get(group);
}
@Override
public void dispose() {
final IEclipseContext activeContext = getContext().getActiveLeaf();
ContextInjectionFactory.uninject(this, activeContext);
}
private IEclipseContext getContext() {
return (IEclipseContext) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(IEclipseContext.class);
}
@Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
this.viewer = (TreeViewer) viewer;
}
@Override
public Object[] getElements(final Object inputElement) {
return new Object[] { ((Object[]) inputElement)[0] };
}
@Override
public Object[] getChildren(final Object parentElement) {
if (parentElement instanceof RobotSettingsSection) {
final List<? extends RobotElement> children = ((RobotElement) parentElement).getChildren();
return groupedChildren(children).toArray();
} else if (parentElement instanceof RobotCodeHoldingElement) {
final List<? extends RobotElement> children = ((RobotElement) parentElement).getChildren();
return filteredRobotKeywordCalls(children).toArray();
} else if (parentElement instanceof RobotElement) {
return ((RobotElement) parentElement).getChildren().toArray();
}
return new Object[0];
}
private List<RobotElement> groupedChildren(final List<? extends RobotElement> children) {
groupingElements.clear();
final List<RobotElement> grouped = new ArrayList<>(children);
final Multimap<SettingsGroup, RobotSetting> removedElements = LinkedHashMultimap.create();
for (final RobotElement element : children) {
if (element instanceof RobotSetting) {
final RobotSetting setting = (RobotSetting) element;
final SettingsGroup group = setting.getGroup();
if (group != SettingsGroup.NO_GROUP) {
grouped.remove(element);
removedElements.put(group, setting);
}
}
}
for (final SettingsGroup key : removedElements.keySet()) {
final ArtificialGroupingRobotElement groupingElement = new ArtificialGroupingRobotElement(key, new ArrayList<>(removedElements.get(key)));
grouped.add(groupingElement);
groupingElements.put(key, groupingElement);
}
return grouped;
}
private List<RobotElement> filteredRobotKeywordCalls(final List<? extends RobotElement> children) {
return newArrayList(Iterables.filter(children, new Predicate<RobotElement>() {
@Override
public boolean apply(final RobotElement element) {
if (element instanceof RobotKeywordCall) {
final AModelElement<?> linkedElement = ((RobotKeywordCall) element).getLinkedElement();
if (linkedElement != null && linkedElement instanceof RobotExecutableRow<?>) {
final RobotExecutableRow<?> row = (RobotExecutableRow<?>) linkedElement;
// TODO: checking row type should be done without building line description
return row.isExecutable() && row.buildLineDescription().getRowType() != ERowType.FOR_CONTINUE;
}
}
return false;
}
}));
}
@Override
public Object getParent(final Object element) {
if (element instanceof RobotElement) {
return ((RobotElement) element).getParent();
}
return null;
}
@Override
public boolean hasChildren(final Object element) {
if (element instanceof RobotElement) {
return !((RobotElement) element).getChildren().isEmpty();
}
return true;
}
@Inject
@Optional
private void whenStructuralChangeWasMade(
@UIEventTopic(RobotModelEvents.ROBOT_SUITE_FILE_ALL) final RobotElement affectedElement) {
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.refresh(affectedElement);
}
}
@Inject
@Optional
private void whenFileChangesExternally(
@UIEventTopic(RobotModelEvents.EXTERNAL_MODEL_CHANGE) final RobotElementChange change) {
if (change.getElement() instanceof RobotSuiteFile && change.getKind() == Kind.CHANGED && viewer != null
&& !viewer.getTree().isDisposed()) {
viewer.refresh();
}
}
@Inject
@Optional
private void whenReconcilationWasDone(
@UIEventTopic(RobotModelEvents.REPARSING_DONE) final RobotSuiteFile fileModel) {
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.refresh();
}
}
@Inject
@Optional
private void whenCaseNameChanges(@UIEventTopic(RobotModelEvents.ROBOT_CASE_NAME_CHANGE) final RobotCase testCase) {
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.update(testCase, null);
}
}
@Inject
@Optional
private void whenKeywordDefinitionNameChanges(
@UIEventTopic(RobotModelEvents.ROBOT_KEYWORD_DEFINITION_NAME_CHANGE) final RobotKeywordDefinition keywordDef) {
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.update(keywordDef, null);
}
}
@Inject
@Optional
private void whenKeywordCallNameChanges(
@UIEventTopic(RobotModelEvents.ROBOT_KEYWORD_CALL_NAME_CHANGE) final RobotKeywordCall keywordCall) {
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.update(keywordCall, null);
}
}
@Inject
@Optional
private void whenVariableTypeChanges(
@UIEventTopic(RobotModelEvents.ROBOT_VARIABLE_TYPE_CHANGE) final RobotVariable variable) {
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.update(variable, null);
}
}
@Inject
@Optional
private void whenVariableNameChanges(
@UIEventTopic(RobotModelEvents.ROBOT_VARIABLE_NAME_CHANGE) final RobotVariable variable) {
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.update(variable, null);
}
}
@Inject
@Optional
private void whenSettingArgumentChanges(
@UIEventTopic(RobotModelEvents.ROBOT_KEYWORD_CALL_ARGUMENT_CHANGE) final RobotSetting setting) {
// in case of Library/Resource/etc. import or metadata, we are also
// interested in changes of first argument to keyword call, since it
// used as label
if (viewer != null && !viewer.getTree().isDisposed()) {
viewer.update(setting, null);
}
}
}