MoneyWiseAssetTable.java
/*
* MoneyWise: Finance Application
* Copyright 2012-2026. Tony Washer
*
* 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 io.github.tonywasher.joceanus.moneywise.ui.base;
import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
import io.github.tonywasher.joceanus.metis.list.MetisListKey;
import io.github.tonywasher.joceanus.metis.ui.MetisAction;
import io.github.tonywasher.joceanus.metis.ui.MetisErrorPanel;
import io.github.tonywasher.joceanus.metis.ui.MetisIcon;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePayee;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseAccountInfoClass;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseAssetCategory;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseSecurityType;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseIcon;
import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
import io.github.tonywasher.joceanus.moneywise.views.MoneyWiseView;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButton;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUICheckBox;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControl.TethysUIIconMapSet;
import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBoxPaneManager;
import io.github.tonywasher.joceanus.tethys.api.table.TethysUITableColumn;
import io.github.tonywasher.joceanus.tethys.api.table.TethysUITableManager;
import java.util.Iterator;
import java.util.Map;
/**
* MoneyWise Asset Table.
*
* @param <T> the Asset Data type
*/
public abstract class MoneyWiseAssetTable<T extends MoneyWiseAssetBase>
extends MoneyWiseBaseTable<T> {
/**
* ShowClosed prompt.
*/
private static final String PROMPT_CLOSED = MoneyWiseUIResource.UI_PROMPT_SHOWCLOSED.getValue();
/**
* The filter panel.
*/
private final TethysUIBoxPaneManager theFilterPanel;
/**
* The locked check box.
*/
private final TethysUICheckBox theLockedCheckBox;
/**
* The closed column.
*/
private TethysUITableColumn<Boolean, MetisDataFieldId, T> theClosedColumn;
/**
* Are we showing closed accounts?
*/
private boolean doShowClosed;
/**
* Constructor.
*
* @param pView the view
* @param pEditSet the editSet
* @param pError the error panel
* @param pDataType the dataType
*/
protected MoneyWiseAssetTable(final MoneyWiseView pView,
final PrometheusEditSet pEditSet,
final MetisErrorPanel pError,
final MetisListKey pDataType) {
/* Store parameters */
super(pView, pEditSet, pError, pDataType);
/* Access Gui factory and table */
final TethysUIFactory<?> myGuiFactory = pView.getGuiFactory();
final TethysUITableManager<MetisDataFieldId, T> myTable = getTable();
/* Create new button */
final TethysUIButton myNewButton = myGuiFactory.buttonFactory().newButton();
MetisIcon.configureNewIconButton(myNewButton);
/* Create the CheckBox */
theLockedCheckBox = myGuiFactory.controlFactory().newCheckBox(PROMPT_CLOSED);
/* Create a filter panel */
theFilterPanel = myGuiFactory.paneFactory().newHBoxPane();
theFilterPanel.addSpacer();
theFilterPanel.addNode(theLockedCheckBox);
theFilterPanel.addSpacer();
theFilterPanel.addNode(myNewButton);
/* Set table configuration */
myTable.setDisabled(MoneyWiseAssetBase::isDisabled)
.setComparator(MoneyWiseAssetBase::compareTo);
/* Create the name column */
myTable.declareStringColumn(PrometheusDataResource.DATAITEM_FIELD_NAME)
.setValidator(this::isValidName)
.setCellValueFactory(MoneyWiseAssetBase::getName)
.setEditable(true)
.setColumnWidth(WIDTH_NAME)
.setOnCommit((r, v) -> updateField(MoneyWiseAssetBase::setName, r, v));
/* Create the Category column */
myTable.declareScrollColumn(MoneyWiseBasicResource.CATEGORY_NAME, MoneyWiseAssetCategory.class)
.setMenuConfigurator(this::buildCategoryMenu)
.setCellValueFactory(MoneyWiseAssetBase::getCategory)
.setEditable(true)
.setCellEditable(r -> !r.isActive())
.setColumnWidth(WIDTH_NAME)
.setOnCommit((r, v) -> updateField(MoneyWiseAssetBase::setCategory, r, v));
/* Create the description column */
myTable.declareStringColumn(PrometheusDataResource.DATAITEM_FIELD_DESC)
.setValidator(this::isValidDesc)
.setCellValueFactory(MoneyWiseAssetBase::getDesc)
.setEditable(true)
.setColumnWidth(WIDTH_DESC)
.setOnCommit((r, v) -> updateField(MoneyWiseAssetBase::setDescription, r, v));
/* Add listeners */
myNewButton.getEventRegistrar().addEventListener(e -> addNewItem());
theLockedCheckBox.getEventRegistrar().addEventListener(e -> setShowAll(theLockedCheckBox.isSelected()));
}
/**
* finish the table.
*
* @param pParent create parent column
* @param pCurrency create currency column
* @param pEvent create event column
*/
protected void finishTable(final boolean pParent,
final boolean pCurrency,
final boolean pEvent) {
/* Access Table */
final TethysUITableManager<MetisDataFieldId, T> myTable = getTable();
/* Create the parent column */
if (pParent) {
myTable.declareScrollColumn(MoneyWiseBasicResource.ASSET_PARENT, MoneyWisePayee.class)
.setMenuConfigurator(this::buildParentMenu)
.setCellValueFactory(MoneyWiseAssetBase::getParent)
.setEditable(true)
.setCellEditable(r -> !r.isActive())
.setColumnWidth(WIDTH_NAME)
.setOnCommit((r, v) -> updateField(MoneyWiseAssetBase::setParent, r, v));
}
/* Create the currency column */
if (pCurrency) {
myTable.declareScrollColumn(MoneyWiseStaticDataType.CURRENCY, MoneyWiseCurrency.class)
.setMenuConfigurator(this::buildCurrencyMenu)
.setCellValueFactory(MoneyWiseAssetBase::getAssetCurrency)
.setEditable(true)
.setCellEditable(r -> !r.isActive())
.setColumnWidth(WIDTH_CURR)
.setOnCommit((r, v) -> updateField(MoneyWiseAssetBase::setAssetCurrency, r, v));
}
/* Create the Closed column */
final Map<Boolean, TethysUIIconMapSet<Boolean>> myClosedMapSets = MoneyWiseIcon.configureLockedIconButton(getView().getGuiFactory());
final TethysUITableColumn<Boolean, MetisDataFieldId, T> myClosedColumn
= myTable.declareIconColumn(MoneyWiseBasicResource.ASSET_CLOSED, Boolean.class)
.setIconMapSet(r -> myClosedMapSets.get(determineClosedState(r)))
.setCellValueFactory(MoneyWiseAssetBase::isClosed)
.setEditable(true)
.setCellEditable(this::determineClosedState)
.setColumnWidth(WIDTH_ICON)
.setOnCommit((r, v) -> updateField(MoneyWiseAssetBase::setClosed, r, v));
declareClosedColumn(myClosedColumn);
setShowAll(false);
/* Create the Active column */
final TethysUIIconMapSet<MetisAction> myActionMapSet = MetisIcon.configureStatusIconButton(getView().getGuiFactory());
myTable.declareIconColumn(PrometheusDataResource.DATAITEM_TOUCH, MetisAction.class)
.setIconMapSet(r -> myActionMapSet)
.setCellValueFactory(r -> r.isActive() ? MetisAction.ACTIVE : MetisAction.DELETE)
.setName(MoneyWiseUIResource.STATICDATA_ACTIVE.getValue())
.setEditable(true)
.setCellEditable(r -> !r.isActive())
.setColumnWidth(WIDTH_ICON)
.setOnCommit((r, v) -> updateField(this::deleteRow, r, v));
/* Create the latest event column */
if (pEvent) {
myTable.declareDateColumn(MoneyWiseBasicResource.ASSET_LASTEVENT)
.setCellValueFactory(this::getLatestTranDate)
.setName(MoneyWiseUIResource.ASSET_COLUMN_LATEST.getValue())
.setEditable(false);
}
}
/**
* Obtain the filter panel.
*
* @return the filter panel
*/
public TethysUIBoxPaneManager getFilterPanel() {
return theFilterPanel;
}
/**
* Declare the closed column.
*
* @param pColumn the column
*/
protected void declareClosedColumn(final TethysUITableColumn<Boolean, MetisDataFieldId, T> pColumn) {
theClosedColumn = pColumn;
}
/**
* Determine closed state.
*
* @param pAsset the asset
* @return the state
*/
protected boolean determineClosedState(final T pAsset) {
return pAsset.isClosed() || !pAsset.isRelevant();
}
/**
* Obtain the date of the latest transaction.
*
* @param pAsset the asset
* @return the date or null
*/
protected OceanusDate getLatestTranDate(final T pAsset) {
final MoneyWiseTransaction myTran = pAsset.getLatest();
return myTran == null ? null : myTran.getDate();
}
/**
* Set the showAll indicator.
*
* @param pShowAll show closed accounts?
*/
public void setShowAll(final boolean pShowAll) {
doShowClosed = pShowAll;
cancelEditing();
getTable().setFilter(this::isFiltered);
theClosedColumn.setVisible(pShowAll);
restoreSelected();
}
/**
* check whether we need to enable showAll.
*
* @param pAsset the asset to check
*/
protected void checkShowAll(final T pAsset) {
/* If the item is closed, but we are not showing closed items */
if (pAsset != null
&& pAsset.isClosed()
&& !theLockedCheckBox.isSelected()) {
theLockedCheckBox.setSelected(true);
setShowAll(true);
}
}
/**
* Build the Category list for the item.
*
* @param pAsset the item
* @param pMenu the menu to build
*/
protected abstract void buildCategoryMenu(T pAsset,
TethysUIScrollMenu<MoneyWiseAssetCategory> pMenu);
/**
* Build the Parent list for the item.
*
* @param pAsset the item
* @param pMenu the menu to build
*/
protected void buildParentMenu(final T pAsset,
final TethysUIScrollMenu<MoneyWisePayee> pMenu) {
/* No-Op */
}
/**
* Build the currency list for the item.
*
* @param pAsset the item
* @param pMenu the menu to build
*/
protected void buildCurrencyMenu(final T pAsset,
final TethysUIScrollMenu<MoneyWiseCurrency> pMenu) {
/* No-Op */
}
/**
* New item.
*/
protected abstract void addNewItem();
@Override
protected boolean isFiltered(final T pRow) {
/* Handle filter */
return super.isFiltered(pRow) && (doShowClosed || !pRow.isDisabled());
}
@Override
protected String getInvalidNameChars() {
return ":";
}
/**
* Obtain a new asset nameSpaceIterator.
*
* @return the iterator
*/
protected Iterator<PrometheusDataItem> assetNameSpaceIterator() {
return new MoneyWiseNameSpaceIterator(getEditSet(),
MoneyWiseBasicDataType.PAYEE,
MoneyWiseBasicDataType.DEPOSIT,
MoneyWiseBasicDataType.CASH,
MoneyWiseBasicDataType.LOAN,
MoneyWiseBasicDataType.PORTFOLIO);
}
/**
* is Valid data?
*
* @param pNewData the new data
* @param pClazz the class
* @return error message or null
*/
public String isValidData(final char[] pNewData,
final MoneyWiseAccountInfoClass pClazz) {
/* Reject data that is too long */
if (pNewData.length > pClazz.getMaximumLength()) {
return "Data too long";
}
/* Valid notes */
return null;
}
/**
* is Valid symbol?
*
* @param pNewSymbol the new symbol
* @param pRow the row
* @return error message or null
*/
public String isValidSymbol(final String pNewSymbol,
final T pRow) {
/* Reject null symbol */
if (pNewSymbol == null) {
return "Null Symbol not allowed";
}
/* Reject invalid symbol */
if (!PrometheusDataItem.validString(pNewSymbol, getInvalidNameChars())) {
return "Invalid characters in symbol";
}
/* Reject symbol that is too long */
if (PrometheusDataItem.byteLength(pNewSymbol) > MoneyWiseSecurityType.SYMBOL_LEN) {
return "Symbol too long";
}
/* Loop through the existing values */
final Iterator<PrometheusDataItem> myIterator = nameSpaceIterator();
while (myIterator.hasNext()) {
final PrometheusDataItem myValue = myIterator.next();
/* Ignore self and deleted */
if (!(myValue instanceof MoneyWiseSecurity)
|| myValue.isDeleted()
|| myValue.equals(pRow)) {
continue;
}
/* Check for duplicate */
final MoneyWiseSecurity mySecurity = (MoneyWiseSecurity) myValue;
if (pNewSymbol.equals(mySecurity.getSymbol())) {
return "Duplicate symbol";
}
}
/* Valid symbol */
return null;
}
}