/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 de.unioninvestment.eai.portal.portlet.crud.scripting.model;
import com.vaadin.ui.Component;
import com.vaadin.ui.Table.ColumnGenerator;
import de.unioninvestment.eai.portal.portlet.crud.domain.events.*;
import de.unioninvestment.eai.portal.portlet.crud.domain.model.ContainerRow;
import de.unioninvestment.eai.portal.portlet.crud.domain.model.ContainerRowId;
import de.unioninvestment.eai.portal.portlet.crud.domain.model.Table;
import de.unioninvestment.eai.portal.portlet.crud.domain.model.Table.DisplayMode;
import de.unioninvestment.eai.portal.portlet.crud.domain.model.Table.DynamicColumnChanges;
import de.unioninvestment.eai.portal.portlet.crud.domain.model.Table.Mode;
import de.unioninvestment.eai.portal.portlet.crud.domain.support.map.TransformedEntryMap;
import de.unioninvestment.eai.portal.support.vaadin.groovy.VaadinBuilder;
import groovy.lang.Closure;
import java.util.*;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet;
/**
*
* Repräsentiert eine Tabelle.
*
* @author siva.selvarajah
*/
@SuppressWarnings("serial")
public class ScriptTable extends ScriptComponent {
private Table table;
private ScriptContainer container;
private List<ScriptTableAction> scriptActions = new ArrayList<ScriptTableAction>();
private Closure<?> onSelectionChange;
private Closure<?> onModeChange;
private Closure<?> onRowChange;
private Closure<?> onDoubleClick;
private Closure<?> onInitialize;
private Map<String, Closure<?>> generatedColumnClosures = new HashMap<String, Closure<?>>();
private ScriptTableSelection currentSelection;
TableDoubleClickEventHandler doubleClickEventHandler = null;
/**
* Konstruktor mit Parameter.
*
* @param table
* Tabelle
*/
ScriptTable(Table table) {
this.table = table;
currentSelection = new ScriptTableSelection(table.getContainer(),
new HashSet<ScriptRowId>());
initializeEventHandler(table);
}
/**
* Fügt ein Button hinzu.
*
* @param scriptTableAction
* ScriptTableAction
*/
void addAction(ScriptTableAction scriptTableAction) {
this.scriptActions.add(scriptTableAction);
scriptTableAction.setTable(this);
}
/**
*
* @return Ob die Sortierung aktiviert ist
*/
public boolean isSortingEnabled() {
return table.isSortingEnabled();
}
/**
* @return die aktuelle Zeilenauswahl der Tabelle
*/
public ScriptTableSelection getSelection() {
return currentSelection;
}
/**
* Liefert die Zeile mit der übergebenen ID.
*
* @param rowId
* die ID der Zeile
* @return die Zeile mit der ID {@code rowId}
*/
public ScriptRow getRowById(ScriptRowId rowId) {
ContainerRow row = table.getRow(rowId.getContainerRowId());
if (row != null) {
return new ScriptRow(row);
} else {
return null;
}
}
/**
* Liefert alle momentan selektierten Zeilen.
*
* @return alle momentan selektierten Zeilen
*/
public List<ScriptRow> getSelectedRows() {
List<ScriptRow> selectedRows = new ArrayList<ScriptRow>();
for (ScriptRowId rowId : getSelection().getIds()) {
selectedRows.add(getRowById(rowId));
}
return selectedRows;
}
/**
*
* @return Alle Tabellenbuttons, die der Tabelle zugeordnet sind.
*/
public List<ScriptTableAction> getActions() {
return unmodifiableList(scriptActions);
}
/**
*
* @return Closure mit zwei Parametern { ScriptTable it,
* ScriptTableSelection selection -> ... }, die bei einer Änderung
* der Selektion ausgeführt wird.
*/
public Closure<?> getOnSelectionChange() {
return onSelectionChange;
}
/**
*
* @param onSelectionChange
* Closure mit zwei Parametern { ScriptTable it,
* ScriptTableSelection selection -> ... }, die bei einer
* Änderung der Selektion ausgeführt werden soll.
*/
public void setOnSelectionChange(Closure<?> onSelectionChange) {
this.onSelectionChange = onSelectionChange;
}
/**
* @return Closure die bei einem Doppelklick ausgeführt wird
*/
public Closure<?> getOnDoubleClick() {
return onDoubleClick;
}
/**
* Setzt die Closure die bei einem Doppelklick ausgeführt wird.
*
* @param onDoubleClick
* die Closure
*/
public void setOnDoubleClick(Closure<?> onDoubleClick) {
if (onDoubleClick == null) {
removeExistingDoubleClickEventHandler();
} else {
addMissingDoubleClickEventHandler();
}
this.onDoubleClick = onDoubleClick;
}
private void addMissingDoubleClickEventHandler() {
if (doubleClickEventHandler == null) {
doubleClickEventHandler = new TableDoubleClickEventHandler() {
@Override
public void onDoubleClick(TableDoubleClickEvent event) {
ScriptRow scriptRow = new ScriptRow(event.getRow());
ScriptTable.this.onDoubleClick.call(ScriptTable.this,
scriptRow);
}
};
table.addDoubleClickEventHandler(doubleClickEventHandler);
}
}
private void removeExistingDoubleClickEventHandler() {
if (doubleClickEventHandler != null) {
table.removeDoubleClickEventHandler(doubleClickEventHandler);
doubleClickEventHandler = null;
}
}
/**
* @return <code>true</code>, falls direct edit für die Tabelle aktiv ist
*/
public boolean isDirectEdit() {
return table.isDirectEdit();
}
/**
* @return liefert den aktuellen Editiermodus 'VIEW' oder 'EDIT' zurück.
*/
public String getMode() {
return table.getMode().name();
}
/**
* @param mode
* der Zielmodus. Kann nur geändert werden, wenn direct editing
* deaktiviert ist
* @throws IllegalStateException
* , falls eine Änderung nicht möglich ist
*/
public void setMode(String mode) {
table.changeMode(Mode.valueOf(mode));
}
/**
* @return den aktuellen Anzeigemodus 'TABLE' oder 'FORM' zurück
*/
public String getDisplayMode() {
return table.getDisplayMode().name();
}
/**
* Schließt eine evtl. geöffnete Formularansicht
*/
public void closeEditForm() {
table.changeDisplayMode(DisplayMode.TABLE);
}
/**
*
* @return Closure mit zwei Parametern { ScriptTable it, String mode -> ...
* }, die beim Wechseln zwischen "VIEW" und "EDIT"-Mode ausgeführt
* werden soll.
*/
public Closure<?> getOnModeChange() {
return onModeChange;
}
/**
*
* @param onModeChange
* Closure mit zwei Parametern { ScriptTable it, String mode ->
* ... }, die beim Wechseln zwischen "VIEW" und "EDIT"-Mode
* ausgeführt werden soll.
*/
public void setOnModeChange(Closure<?> onModeChange) {
this.onModeChange = onModeChange;
}
/**
* @param columnName
* der Spaltenname
* @return die onRender-Closure oder <code>null</code>, falls keine gesetzt
*/
public Closure<?> getOnRender(String columnName) {
return generatedColumnClosures.get(columnName);
}
/**
* @param columnName
* der Spaltenname der generierten Spalte
* @param onRender
* die onRender-Closure. Als Parameter der Closure wird ein
* {@link VaadinBuilder} übergeben
*/
public void addOnRender(String columnName, Closure<?> onRender) {
generatedColumnClosures.put(columnName, onRender);
}
void setContainer(ScriptContainer scriptContainer) {
this.container = scriptContainer;
}
/**
* @return den Container zur Tabelle
*/
public ScriptContainer getContainer() {
return container;
}
private void initializeEventHandler(final Table table) {
table.addSelectionEventHandler(new SelectionEventHandler() {
@Override
public void onSelectionChange(SelectionEvent selectionEvent) {
Set<ScriptRowId> selectedRowIds = new HashSet<ScriptRowId>();
for (ContainerRowId id : selectionEvent.getSelection()) {
selectedRowIds.add(new ScriptRowId(id));
}
currentSelection = new ScriptTableSelection(table
.getContainer(), unmodifiableSet(selectedRowIds));
if (onSelectionChange != null) {
onSelectionChange.call(ScriptTable.this, currentSelection);
}
}
});
table.addModeChangeEventHandler(new ModeChangeEventHandler<Table, Mode>() {
@Override
public void onModeChange(ModeChangeEvent<Table, Mode> event) {
if (onModeChange != null) {
onModeChange.call(ScriptTable.this, event.getMode().name());
}
}
});
table.addRowChangeEventHandler(new RowChangeEventHandler() {
@Override
public void rowChange(RowChangeEvent event) {
if (onRowChange != null) {
ScriptRow scriptRow = new ScriptRow(event.getSource());
onRowChange.call(ScriptTable.this, scriptRow,
event.getChangedValues());
}
}
});
table.addInitializeEventHandler(new InitializeEventHandler<Table>() {
@Override
public void onInitialize(InitializeEvent<Table> event) {
if (onInitialize != null) {
onInitialize.call(ScriptTable.this);
}
}
});
}
/**
* Aktualisiert die Daten auf der Tabelle.
*/
public void refresh() {
table.refresh();
}
/**
* @return die Closure die bei einem Zeilenwechsel ausgeführt wird.
*/
public Closure<?> getOnRowChange() {
return onRowChange;
}
/**
* @param onRowChange
* Closure mit den Parametern { ScriptTable it, ScriptRow row,
* Map<String,Object> changedValues -> ... }, die nach Änderung
* von Zeilenwerten durch die UI aufgerufen wird.
*
* <code>changedValues</code> enthält eine Map der geänderten
* Spaltennamen mit den ursprünglichen Werte.
*/
public void setOnRowChange(Closure<?> onRowChange) {
this.onRowChange = onRowChange;
}
/**
* @return die onInitialize-Closure
*/
public Closure<?> getOnInitialize() {
return onInitialize;
}
/**
* @param onInitialize
* die onInitialize-Closure
*/
public void setOnInitialize(Closure<?> onInitialize) {
this.onInitialize = onInitialize;
}
/**
* Fügt der Tabelle dynamisch eine neue Spalte hinzu.
*
* @param columnName
* Name und Titel der neuen Spalte
* @param generator
* die Closure, die den Inhalt der einzelnen Zellen generiert
*/
public void addGeneratedColumn(final String columnName,
final Closure<Component> generator) {
addGeneratedColumn(columnName, columnName, generator);
}
/**
* Fügt der Tabelle dynamisch eine neue Spalte hinzu.
*
* @param columnName
* der Name der Spalte
* @param columnTitle
* der Titel der Spalte
* @param generator
* die Closure, die den Inhalt der einzelnen Zellen generiert
*/
public void addGeneratedColumn(final String columnName,
final String columnTitle, final Closure<Component> generator) {
table.addGeneratedColumn(columnName, columnTitle,
new ColumnGenerator() {
private static final long serialVersionUID = 1L;
@Override
public Component generateCell(com.vaadin.ui.Table source,
Object itemIdVaadin, Object columnIdVaadin) {
ScriptRow row = new ScriptRow(table
.getRowByItemId(itemIdVaadin));
return generator.call(row, new VaadinBuilder());
}
});
}
/**
* Entfernt eine zuvor per {@link #addGeneratedColumn(String, Closure)}
* hinzugefügte Spalte aus der Tabelle.
*
* @param columnId
* die ID der Spalte, die bei
* {@link #addGeneratedColumn(String, Closure)} verwendet wurde
*/
public void removeGeneratedColumn(String columnId) {
table.removeGeneratedColumn(columnId);
}
/**
* Führt Änderungen an der Tabelle zu wie beispielsweise dynamisches
* Hinzufügen und Entfernen von Spalten. Das Content-Refreshing
* (Rerendering) wird vor dem Ausführen von {@code change} ausgeschaltet und
* (falls es vorher eingeschaltet war) danach wieder eingeschaltet.
*
* @param dynamicColumnChanges
* Closure, die die durchzuführenden Änderungen kapselt
*/
public void renderOnce(final Closure<?> dynamicColumnChanges) {
table.renderOnce(new DynamicColumnChanges() {
@Override
public void apply() {
dynamicColumnChanges.call();
}
});
}
public void revertChanges() {
table.revertChanges();
}
/**
* @param columnId
* die Spalten-ID/Spalten-Überschrift.
* @return {@code true} wenn die Tabelle eine generierte Spalte des
* angegebenen Namens hat.
*/
public boolean hasGeneratedColumn(String columnId) {
return table.hasGeneratedColumn(columnId);
}
/**
* Entfernt alle generierten Spalten.
*/
public void clearAllGeneratedColumns() {
table.clearAllGeneratedColumns();
}
/**
* @return die IDs der sichtbaren Spalten in der Reihenfolge, in der sie
* angezeigt werden.
* @see #setVisibleColumns(List)
*/
public List<String> getVisibleColumns() {
return table.getVisibleColumns();
}
/**
* Setzt gleichzeitig die Reihenfolge und die Sichtbarkeit der Spalten. Alle
* Spalten, deren ID in der übergebenen Liste {@code visibleColumns}
* enthalten sind, werden angezeigt, alle anderen nicht. Das bezieht sich
* nicht nur auf per {@code addGeneratedColumn} hinzugefügten Spalten
* sondern auf alle Spalten inkl. der fest in der Konfiguration
* deklarierten. Die Spalten werden in der Reihenfolge angeordnet, wie sie
* in {@code visibleColumns} stehen.
*
* @param visibleColumns
* die Liste der ColumnNames. Für eine per
* {@link #addGeneratedColumn(String, String, Closure)}
* hinzugefügte Spalten ist das der String, der dabei als
* Argument {@code columnName} verwendet wurde, für eine
* deklarierte Spalte das Attribut {@code name}
*/
public void setVisibleColumns(List<String> visibleColumns) {
table.setVisibleColumns(visibleColumns);
}
/**
* Fügt der Tabelle eine neue Zeile hinzu. Werte der Zeile können über
* {@code values} vorbelegt werden.
*
* @param values
* die Werte, die in der neuen Zeile bereits gesetzt sein sollen.
* Die Keys der Map entsprechen den Column Names, die
* dazugehörigen Values sind die Werte in der Zeile.
* @return die neu angelegte Zeile
*/
public ScriptRow createNewRow(Map<String, Object> values) {
return new ScriptRow(table.createNewRow(mapValuesToModel(values)));
}
private Map<String, Object> mapValuesToModel(Map<String, Object> values) {
if (values == null) {
return Collections.<String, Object> emptyMap();
} else {
return new TransformedEntryMap<String, Object, Object>(values,
ScriptField.SCRIPT_TO_MODEL_VALUE_TRANSFORMER);
}
}
/**
* Setzt die Sichtbarkeit der TableAction (Button) mit der ID {@code id}.
*
* @param id
* die ID der TableAction, so wie sie im Attribut <tt>id</tt> des
* Tags <tt>action</tt> definiert ist.
*
* @param visible
* {@code true}: sichtbar, {@code false}: unsichtbar,
*/
public void setTableActionVisibility(String id, boolean visible) {
table.setTableActionVisibility(id, visible);
}
}