package com.revolsys.swing.map.layer.record.table.model; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import javax.swing.Icon; import javax.swing.SwingWorker; import com.revolsys.collection.map.LruMap; import com.revolsys.logging.Logs; import com.revolsys.predicate.Predicates; import com.revolsys.record.Record; import com.revolsys.record.query.Condition; import com.revolsys.record.query.Query; import com.revolsys.swing.Icons; import com.revolsys.swing.map.layer.record.AbstractRecordLayer; import com.revolsys.swing.map.layer.record.LayerRecord; import com.revolsys.swing.parallel.Invoke; import com.revolsys.util.Property; public class ModeAllPaged extends ModeAbstractCached { private int persistedRecordCount; private SwingWorker<?, ?> recordCountWorker; private final int pageSize = 100; private final Map<Integer, List<LayerRecord>> pageCache = new LruMap<>(5); private final Set<Integer> loadingPageNumbers = new LinkedHashSet<>(); public ModeAllPaged(final RecordLayerTableModel model) { super(RecordLayerTableModel.MODE_RECORDS_ALL, model); } @Override public void activate() { final AbstractRecordLayer layer = getLayer(); addListeners( // Property.addListenerNewValueSource(layer, AbstractRecordLayer.RECORDS_INSERTED, this::addCachedRecords), // newRecordsDeletedListener(layer)// ); final RecordLayerTableModel model = getTableModel(); for (final String propertyName : new String[] { "filter", AbstractRecordLayer.RECORDS_CHANGED }) { addListeners( // Property.addListenerRunnable(layer, propertyName, this::refresh)); } addListeners( // Property.addListenerRunnable(model, "filter", this::refresh) // ); super.activate(); } private void clear() { synchronized (this) { if (this.recordCountWorker != null) { this.recordCountWorker.cancel(true); this.recordCountWorker = null; } this.loadingPageNumbers.clear(); this.pageCache.clear(); this.persistedRecordCount = -1; } } @Override public void deactivate() { clear(); super.deactivate(); } @Override public void exportRecords(final Query query, final Object target) { final AbstractRecordLayer layer = getLayer(); layer.exportRecords(query, target); } protected boolean filterTestModified(final Condition filter, final LayerRecord modifiedRecord) { boolean accept = false; if (filter.test(modifiedRecord)) { if (!filter.test(modifiedRecord.getOriginalRecord())) { accept = true; } } return accept; } @Override public void forEachRecord(final Query query, final Consumer<? super LayerRecord> action) { final AbstractRecordLayer layer = getLayer(); layer.forEachRecord(query, action); } @Override public Icon getIcon() { return Icons.getIcon("table_filter"); } @Override public int getRecordCount() { synchronized (this) { if (this.persistedRecordCount < 0) { if (this.recordCountWorker == null) { final long refreshIndex = getRefreshIndexNext(); final AbstractRecordLayer layer = getLayer(); this.recordCountWorker = Invoke.background("Query row count " + layer.getName(), this::getRecordCountPersisted, (rowCount) -> { if (canRefreshFinish(refreshIndex)) { this.persistedRecordCount = rowCount; this.recordCountWorker = null; fireTableDataChanged(); } }); } return 0; } else { int count = super.getRecordCount(); count += this.persistedRecordCount; return count; } } } protected int getRecordCountPersisted() { final Query query = getFilterQuery(); try { final AbstractRecordLayer layer = getLayer(); if (query == null) { return layer.getRecordCountPersisted(); } else { return layer.getRecordCountPersisted(query); } } catch (final Throwable e) { Logs.debug(this, "Error running query:" + query, e); return 0; } } @Override protected LayerRecord getRecordDo(final int row) { final int recordCountChanges = super.getRecordCount(); if (row >= 0) { if (row < recordCountChanges) { return super.getRecordDo(row); } else { final int persistedRow = row - recordCountChanges; final int recordCount = getRecordCount(); if (row < recordCount) { final int pageNumber = persistedRow / this.pageSize; final int recordNumber = persistedRow % this.pageSize; final LayerRecord record = getRecordPagePersisted(pageNumber, recordNumber); return record; } } } return null; } protected LayerRecord getRecordPagePersisted(final int pageNumber, final int recordIndex) { synchronized (this) { final List<LayerRecord> page = this.pageCache.get(pageNumber); if (page == null) { boolean load = false; synchronized (this.loadingPageNumbers) { if (!this.loadingPageNumbers.contains(pageNumber)) { this.loadingPageNumbers.add(pageNumber); load = true; } } if (load) { final long refreshIndex = getRefreshIndex(); Invoke.background("loadPage" + getTypeName(), 2, "Loading records " + getTypeName(), () -> loadPage(pageNumber), // (records) -> { setRecords(refreshIndex, pageNumber, records); }); } } else { if (recordIndex < page.size()) { final LayerRecord record = page.get(recordIndex); return record; } } } return null; } @Override protected List<LayerRecord> getRecordsForCache() { final AbstractRecordLayer layer = getLayer(); final List<LayerRecord> records = layer.getRecordsNew(); final Condition filter = getFilter(); if (!filter.isEmpty()) { Predicates.retain(records, filter); for (final LayerRecord modifiedRecord : layer.getRecordsModified()) { if (filterTestModified(filter, modifiedRecord)) { records.add(modifiedRecord); } } } final RecordLayerTableModel model = getTableModel(); final Comparator<Record> comparator = model.getOrderByComparatorIdentifier(); if (comparator != null) { records.sort(comparator); } return records; } protected List<LayerRecord> getRecordsLayer(final Query query) { final AbstractRecordLayer layer = getLayer(); return layer.getRecordsPersisted(query); } @Override public String getTitle() { return "Show All Records"; } private String getTypeName() { return getTableModel().getTypeName(); } @Override public boolean isFilterByBoundingBoxSupported() { return true; } /** * Has the record been changed such that * @param record * @return */ protected boolean isRecordPageQueryChanged(final LayerRecord record) { final AbstractRecordLayer layer = getLayer(); if (layer.isModified(record)) { final Condition filter = getFilter(); final RecordLayerTableModel model = getTableModel(); final Comparator<Record> comparator = model.getOrderByComparatorIdentifier(); if (comparator != null) { final Record orginialRecord = record.getOriginalRecord(); final int compare = comparator.compare(record, orginialRecord); if (compare != 0) { return true; } } if (!filter.isEmpty()) { if (filter.test(record)) { final Record orginialRecord = record.getOriginalRecord(); if (!filter.test(orginialRecord)) { return true; } } } } return false; } @Override public boolean isSortable() { return false; } private List<LayerRecord> loadPage(final int pageNumber) { final RecordLayerTableModel model = getTableModel(); try { final Query query = model.getFilterQuery(); if (query == null) { return Collections.emptyList(); } else { query.setOffset(this.pageSize * pageNumber); query.setLimit(this.pageSize); return getRecordsLayer(query); } } finally { synchronized (this.loadingPageNumbers) { this.loadingPageNumbers.remove(pageNumber); } } } @Override protected void recordUpdated(final LayerRecord record) { Invoke.later(() -> { final Condition filter = getFilter(); final AbstractRecordLayer layer = getLayer(); if (layer.isNew(record)) { if (filter.test(record)) { addCachedRecord(record); } else { removeCachedRecord(record); } } else if (!filter.isEmpty()) { if (layer.isModified(record)) { if (filterTestModified(filter, record)) { addCachedRecord(record); } else { removeCachedRecord(record); } } } }); } @Override public void refresh(final long refreshIndex) { clear(); super.refresh(refreshIndex); } protected void setRecords(final long refreshIndex, final int pageNumber, final List<LayerRecord> records) { synchronized (this) { if (canRefreshFinish(refreshIndex)) { this.pageCache.put(pageNumber, records); final RecordLayerTableModel model = getTableModel(); model.fireTableRowsUpdated(pageNumber * this.pageSize, Math.min(getRecordCount(), (pageNumber + 1) * this.pageSize - 1)); } } } }