Skip to content
Snippets Groups Projects

4014

Merged 4014
1 unresolved thread
Merged Andreas Bayha requested to merge 4014 into master
1 unresolved thread
1 file
+ 12
3
Compare changes
  • Side-by-side
  • Inline
/*-------------------------------------------------------------------------+
| Copyright 2020 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.ui.annotation.view.fx;
import static java.util.Collections.emptyList;
import static java.util.Collections.sort;
import static java.util.stream.Collectors.toList;
import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilterContentProvider.HIERARCHY_LEVELS_ALL;
import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilterContentProvider.HIERARCHY_LEVELS_CURRENT;
import static org.fortiss.tooling.base.ui.annotation.view.fx.FXAnnotationFilterContentProvider.HIERARCHY_LEVELS_SELECTED_SUBMODEL;
import static org.fortiss.tooling.kernel.ui.util.SelectionUtils.checkAndPickFirst;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.getChildrenWithType;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.getFirstParentWithType;
import static org.fortiss.tooling.kernel.utils.LoggingUtils.showWarning;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.annotation.AnnotationValueService;
import org.fortiss.tooling.base.annotation.IAnnotationValueService;
import org.fortiss.tooling.base.annotation.valueprovider.EStructuralFeatureDescriptor;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider;
import org.fortiss.tooling.base.annotation.valueprovider.ValueProviderBase;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.model.element.IModelElement;
import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeTableUIProviderBase;
import org.fortiss.tooling.common.ui.javafx.control.treetableview.DynamicTreeTableViewer;
import org.fortiss.tooling.common.ui.javafx.layout.CompositeFXControllerBase;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
import org.fortiss.tooling.kernel.model.INamedCommentedElement;
import org.fortiss.tooling.kernel.model.IProjectRootElement;
import org.fortiss.tooling.kernel.service.IPersistencyService;
import com.sun.webkit.ThemeClient;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
/**
* Controller for the Annotations view.
*
* @author bayha
*/
@SuppressWarnings("unchecked")
public class AnnotationViewFXController extends CompositeFXControllerBase<SplitPane, Node>
implements ISelectionListener {
/** Option text for the annotation type filter combo box. */
private static final String SHOW_ALL_ANNOTATION_TYPES = "Show all annotation types";
/** {@link TextField} for entering the filter pattern. */
@FXML
private TextField txtFilterText;
/** {@link RadioButton} to enable filtering for model element names. */
@FXML
private RadioButton radBtnFilterModelElements;
/** {@link RadioButton} to enable filtering for annotation names. */
@FXML
private RadioButton radBtnFilterAnnotationNames;
/** {@link CheckBox} to make name filtering case sensitive. */
@FXML
private CheckBox chkBoxMatchCase;
/** {@link CheckBox} to only show model elements with the same type as the selected one. */
@FXML
private CheckBox chkBoxselectedTypeOnly;
/**
* {@link ComboBox} to chose the hierarchy option according to which the model elements are
* displayed.
*/
@FXML
private ComboBox<String> comboHierarchy;
/** {@link ComboBox} to chose an annotation type to be displayed exclusively. */
@FXML
private ComboBox<String> comboAnnotationType;
/** The {@link TreeTableView} for the annotation table. */
@FXML
private TreeTableView<AnnotationEntry> annotationTreeTableView;
/** The {@link DynamicTreeTableViewer} to manage the annotationTreeTableView. */
private DynamicTreeTableViewer<AnnotationEntry> annotationViewer;
/** The {@link IProjectRootElement} in which the selected element is contained in. */
private IProjectRootElement curentRootElement;
/** {@link Set} of {@link ColumnHandle}s (i.e. management objects for columns). */
private Set<ColumnHandle<IAnnotatedSpecification>> columnHandles = new TreeSet<>();
/**
* {@link Map} that maps {@link ColumnHandle}s (i.e. management objects for columns) to the
* respective columns.
*/
private Map<ColumnHandle<IAnnotatedSpecification>, TreeTableColumn<AnnotationEntry, ?>> columnHandleToColumn =
new HashMap<ColumnHandle<IAnnotatedSpecification>, TreeTableColumn<AnnotationEntry, ?>>();
/** {@link Map} to get the {@link IAnnotatedSpecification} that corresponds to a column name. */
private Map<String, IAnnotatedSpecification> colNameToSpecification =
new HashMap<String, IAnnotatedSpecification>();
/** Content provider for the annotation table with all filtering functionality. */
private final FXAnnotationFilterContentProvider filterContentProvider =
new FXAnnotationFilterContentProvider();
/**
* Associates the handles of the annotations with their ordered column indexes.
* Used for the UI providers of cell items.
*/
/* package */ Map<Integer, ColumnHandle<IAnnotatedSpecification>> colIdxAnnotationMap =
new HashMap<>();
/**
* Root element to be added to the tree viewer (but not displayed). Container for all
* {@link AnnotationEntry}s.
*/
/* package */ ArtificialRoot root = new ArtificialRoot();
/** The {@link IModelElement} that is currently selected by the user. */
/* package */ IModelElement selected;
/** {@inheritDoc} */
@Override
public String getFXMLLocation() {
return "AnnotationViewFx.fxml";
}
/** {@inheritDoc} */
@Override
public void initialize() {
setupFilterWidgets();
}
/** {@inheritDoc} */
@Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
EObject selected = checkAndPickFirst(selection, EObject.class);
if(selected == null) {
clearTableViewer();
return;
}
if(selected instanceof IProjectRootElement) {
curentRootElement = (IProjectRootElement)selected;
} else {
curentRootElement = getFirstParentWithType(selected, IProjectRootElement.class);
}
if(curentRootElement == null) {
// There was no root element for the selected element. (Usually should be the
// FileProject.) This is not displayed here.
clearTableViewer();
return;
}
if(selected instanceof IModelElement) {
filterContentProvider.setCurrentySelectedModelElement((IModelElement)selected);
this.selected = (IModelElement)selected;
}
setupAnnotationTree();
// After the ColumHandles are created in setupAnnotationTree(), we can fill the
// corresponding filter combo.
ObservableList<String> annotationTypeOptions = comboAnnotationType.getItems();
annotationTypeOptions.clear();
annotationTypeOptions.add(SHOW_ALL_ANNOTATION_TYPES);
List<String> annotations =
columnHandles.stream().map(h -> h.getColumnName()).collect(toList());
annotationTypeOptions.addAll(annotations);
comboAnnotationType.setValue(SHOW_ALL_ANNOTATION_TYPES);
}
/**
* Generates / restructures the annotation tree according to the currently
* selectedElement.
*/
private void setupAnnotationTree() {
if(curentRootElement == null) {
return;
}
clearTableViewer();
// Collect the annotation entries for all elements.
EList<IModelElement> listElements =
getChildrenWithType(curentRootElement, IModelElement.class);
if(curentRootElement instanceof IModelElement) {
listElements.add((IModelElement)curentRootElement);
}
root = new ArtificialRoot();
for(IModelElement elem : listElements) {
AnnotationEntry entry = IAnnotationValueService.getInstance().getAnnotationEntry(elem);
if(entry != null && !entry.getSpecificationsList().isEmpty()) {
root.elements.add(entry);
}
}
sort(root.elements);
Set<AnnotationEntry> annotationEntries = new HashSet<>();
listElements.forEach(e -> annotationEntries
.add(AnnotationValueService.getInstance().getAnnotationEntry(e)));
// Aggregate required columns. Column order is defined by
// ColumnHandle.compareTo().
for(AnnotationEntry entry : annotationEntries) {
for(IAnnotatedSpecification spec : entry.getSpecificationsList()) {
ColumnHandle<IAnnotatedSpecification> columnHandle =
new ColumnHandle<IAnnotatedSpecification>(entry, spec);
columnHandles.add(columnHandle);
colNameToSpecification.put(columnHandle.getColumnName(), spec);
}
}
// The DynamicTreeTableViewer needs to be created new, as the root is different.
boolean showRoot = false;
int revealLevel = 1;
DynamicTreeTableUIProviderBase<AnnotationEntry> uiProvider =
new AnnotationTreeTableUIProvider(this);
annotationViewer = new DynamicTreeTableViewer<AnnotationEntry>(annotationTreeTableView,
root, showRoot, revealLevel, filterContentProvider, uiProvider);
annotationViewer.addColumn("Model Element", 200);
annotationViewer.addColumn("Comment", 250);
int columnIdx = 2;
for(ColumnHandle<IAnnotatedSpecification> handle : columnHandles) {
TreeTableColumn<AnnotationEntry, ?> col = createColumnForHandle(columnIdx, handle);
columnHandleToColumn.put(handle, col);
columnIdx++;
}
filterColumns();
}
/** Resets {@link ThemeClient} annotation table to be empty. Also removes the columns. */
private void clearTableViewer() {
colIdxAnnotationMap.clear();
columnHandles.clear();
columnHandleToColumn.clear();
annotationTreeTableView.getColumns().clear();
}
/** Configures the filter widgets for the annotation table. */
private void setupFilterWidgets() {
comboAnnotationType.getItems().add(SHOW_ALL_ANNOTATION_TYPES);
comboAnnotationType.setValue(SHOW_ALL_ANNOTATION_TYPES);
ObservableList<String> hierarchyLevels = comboHierarchy.getItems();
hierarchyLevels.clear();
hierarchyLevels.add(HIERARCHY_LEVELS_ALL);
hierarchyLevels.add(HIERARCHY_LEVELS_CURRENT);
hierarchyLevels.add(HIERARCHY_LEVELS_SELECTED_SUBMODEL);
comboHierarchy.setValue(HIERARCHY_LEVELS_ALL);
comboHierarchy.valueProperty().addListener((obs, oVal, nVal) -> {
filterContentProvider.setHierarchyLevelFilter(nVal);
updateAnnotationTree();
});
txtFilterText.textProperty().addListener((obs, oVal, nVal) -> {
filterContentProvider.setFilterExpression(nVal);
if(radBtnFilterAnnotationNames.isSelected()) {
filterColumns();
}
updateAnnotationTree();
});
radBtnFilterAnnotationNames.selectedProperty().addListener((obs, oVal, nVal) -> {
filterContentProvider.setFilterColumnName(nVal);
filterColumns();
if(nVal) {
// De-select other radio button, if this one is selected.
radBtnFilterModelElements.setSelected(false);
}
updateAnnotationTree();
});
radBtnFilterModelElements.selectedProperty().addListener((obs, oVal, nVal) -> {
filterContentProvider.setFilterRowName(nVal);
if(nVal) {
// De-select other radio button, if this one is selected.
radBtnFilterAnnotationNames.setSelected(false);
}
updateAnnotationTree();
});
chkBoxMatchCase.selectedProperty().addListener((obs, oVal, nVal) -> {
filterContentProvider.setFilterNameMatchCase(nVal);
if(radBtnFilterAnnotationNames.isSelected()) {
filterColumns();
}
updateAnnotationTree();
});
chkBoxselectedTypeOnly.selectedProperty().addListener((obs, oVal, nVal) -> {
filterContentProvider.setRestrictToSelectedModelElementType(nVal);
updateAnnotationTree();
});
comboAnnotationType.valueProperty().addListener((obs, oVal, nVal) -> {
IAnnotatedSpecification selectedSpec = colNameToSpecification.get(nVal);
if(selectedSpec != null) {
filterContentProvider.setAnnotationTypeFilter(selectedSpec.getClass());
} else {
// In this case, the filter has been reset to "All Types"
filterContentProvider.setAnnotationTypeFilter(null);
}
filterColumns();
updateAnnotationTree();
});
}
/**
* Triggers the update the annotation tree with the latest filter settings and model element
* changes.
*/
private void updateAnnotationTree() {
annotationViewer.update();
annotationViewer.expandAllItems();
}
/** Hides columns which are de-selected by the current filter settings. */
private void filterColumns() {
for(ColumnHandle<IAnnotatedSpecification> c : columnHandles) {
TreeTableColumn<AnnotationEntry, ?> col = columnHandleToColumn.get(c);
col.setVisible(filterContentProvider.passesColumnFilter(c));
}
updateAnnotationTree();
}
/** Creates the annotation column for the given handle. */
private TreeTableColumn<AnnotationEntry, ?> createColumnForHandle(int columnIdx,
ColumnHandle<IAnnotatedSpecification> handle) {
Class<IAnnotatedSpecification> annotationClass =
(Class<IAnnotatedSpecification>)handle.getAnnotatedSpecification().getClass();
AnnotationEntry entry = handle.getEntry();
IAnnotationValueProvider<IAnnotatedSpecification> valueProvider =
entry.getAnnotationValueProvider(annotationClass);
IAnnotatedSpecification specification = entry.getSpecification(annotationClass);
EClassifier valueType =
valueProvider.getEStructuralFeatureDescriptor().getEType(specification);
String columnName = valueProvider.getAnnotationName(specification);
colIdxAnnotationMap.put(columnIdx, handle);
TreeTableColumn<AnnotationEntry, ?> column = null;
// Case distinction for the cell factory depending on the type to be edited.
if(valueType == null) {
column = annotationViewer.addColumn(columnName, 150);
column.setEditable(false);
return column;
}
if(valueType instanceof EEnum) {
// Add a combo column for enumerations.
column = annotationViewer.addComboColumn(columnName, 150, rowEntry -> {
ValueProviderBase<IAnnotatedSpecification> annotationValueProvider =
(ValueProviderBase<IAnnotatedSpecification>)rowEntry
.getAnnotationValueProvider(annotationClass);
EStructuralFeatureDescriptor structuralFeature =
annotationValueProvider.getEStructuralFeatureDescriptor();
EClassifier type =
structuralFeature.getEType(rowEntry.getSpecification(annotationClass));
// Get the enum literals again, as it might be different for each row.
if(type instanceof EEnum) {
EList<EEnumLiteral> allLiterals = ((EEnum)type).getELiterals();
return allLiterals.stream().map(l -> l.getLiteral()).collect(toList());
}
return emptyList();
});
} else if(valueType.getInstanceClass().equals(Boolean.class)) {
// Use a checkbox column for booleans
column = annotationViewer.addCheckboxColumn(columnName, 100);
} else {
// For all other types, there is text editing.
column = annotationViewer.addTextColumn(columnName, 150);
}
if(column != null) {
column.setEditable(true);
}
return column;
}
/**
* Local helper to set the given newValue for the given annotationEntry and
* specification.
*/
void setAnnotationEntryValue(IAnnotatedSpecification specification,
AnnotationEntry annotationEntry, String newValue) {
ITopLevelElement tle =
IPersistencyService.getInstance().getTopLevelElementFor(curentRootElement);
tle.runAsCommand(() -> {
try {
annotationEntry.setSpecificationValue(newValue, specification.getClass());
} catch(Exception e1) {
showWarning("Problem during setting an AnnotationValue: \n" + e1.getStackTrace());
}
});
}
/** Retrieves the model element name to be displayed for the given {@link AnnotationEntry}. */
String getNameForAnnotationEntry(AnnotationEntry entry) {
IModelElement modelElement = entry.getModelElement();
if(modelElement instanceof INamedCommentedElement) {
return ((INamedCommentedElement)modelElement).getName();
}
return "";
}
/**
* Local class to represent the invisible root for the tree table.
*
* The only thing relevant here, is the {@link List} 'elements'.
*/
protected final class ArtificialRoot extends AnnotationEntry {
/**
* Constructor.
*/
public ArtificialRoot() {
super(selected);
}
/** The list of {@link AnnotationEntry}s to be displayed. */
List<AnnotationEntry> elements = new ArrayList<AnnotationEntry>();
}
}
Loading