package com.revolsys.swing.map.layer.record;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
import java.util.function.Predicate;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.undo.UndoableEdit;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import com.revolsys.collection.list.Lists;
import com.revolsys.collection.map.MapEx;
import com.revolsys.collection.map.Maps;
import com.revolsys.collection.set.Sets;
import com.revolsys.datatype.DataType;
import com.revolsys.datatype.DataTypes;
import com.revolsys.geometry.cs.CoordinateSystem;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.util.BoundingBoxUtil;
import com.revolsys.identifier.Identifier;
import com.revolsys.io.BaseCloseable;
import com.revolsys.io.FileUtil;
import com.revolsys.io.IoFactory;
import com.revolsys.io.PathName;
import com.revolsys.io.file.FileNameExtensionFilter;
import com.revolsys.io.map.MapObjectFactory;
import com.revolsys.logging.Logs;
import com.revolsys.record.ArrayRecord;
import com.revolsys.record.Record;
import com.revolsys.record.RecordFactory;
import com.revolsys.record.RecordState;
import com.revolsys.record.Records;
import com.revolsys.record.code.CodeTable;
import com.revolsys.record.io.ListRecordReader;
import com.revolsys.record.io.RecordIo;
import com.revolsys.record.io.RecordReader;
import com.revolsys.record.io.RecordWriter;
import com.revolsys.record.io.RecordWriterFactory;
import com.revolsys.record.property.DirectionalFields;
import com.revolsys.record.query.Condition;
import com.revolsys.record.query.Q;
import com.revolsys.record.query.Query;
import com.revolsys.record.query.QueryValue;
import com.revolsys.record.schema.FieldDefinition;
import com.revolsys.record.schema.RecordDefinition;
import com.revolsys.record.schema.RecordDefinitionProxy;
import com.revolsys.spring.resource.ByteArrayResource;
import com.revolsys.spring.resource.Resource;
import com.revolsys.swing.Borders;
import com.revolsys.swing.Icons;
import com.revolsys.swing.SwingUtil;
import com.revolsys.swing.action.enablecheck.EnableCheck;
import com.revolsys.swing.component.BaseDialog;
import com.revolsys.swing.component.BasePanel;
import com.revolsys.swing.component.TabbedValuePanel;
import com.revolsys.swing.component.ValueField;
import com.revolsys.swing.dnd.ClipboardUtil;
import com.revolsys.swing.dnd.transferable.RecordReaderTransferable;
import com.revolsys.swing.dnd.transferable.StringTransferable;
import com.revolsys.swing.field.TextField;
import com.revolsys.swing.layout.GroupLayouts;
import com.revolsys.swing.logging.LoggingEventPanel;
import com.revolsys.swing.map.MapPanel;
import com.revolsys.swing.map.form.FieldNamesSetPanel;
import com.revolsys.swing.map.form.LayerRecordForm;
import com.revolsys.swing.map.form.SnapLayersPanel;
import com.revolsys.swing.map.layer.AbstractLayer;
import com.revolsys.swing.map.layer.Layer;
import com.revolsys.swing.map.layer.LayerGroup;
import com.revolsys.swing.map.layer.LayerRenderer;
import com.revolsys.swing.map.layer.Project;
import com.revolsys.swing.map.layer.record.component.MergeRecordsDialog;
import com.revolsys.swing.map.layer.record.component.RecordLayerFields;
import com.revolsys.swing.map.layer.record.renderer.AbstractMultipleRenderer;
import com.revolsys.swing.map.layer.record.renderer.AbstractRecordLayerRenderer;
import com.revolsys.swing.map.layer.record.renderer.GeometryStyleRenderer;
import com.revolsys.swing.map.layer.record.renderer.MultipleRenderer;
import com.revolsys.swing.map.layer.record.style.GeometryStyle;
import com.revolsys.swing.map.layer.record.style.panel.LayerStylePanel;
import com.revolsys.swing.map.layer.record.style.panel.QueryFilterField;
import com.revolsys.swing.map.layer.record.table.RecordLayerTable;
import com.revolsys.swing.map.layer.record.table.RecordLayerTablePanel;
import com.revolsys.swing.map.layer.record.table.model.RecordDefinitionTableModel;
import com.revolsys.swing.map.layer.record.table.model.RecordLayerErrors;
import com.revolsys.swing.map.layer.record.table.model.RecordLayerTableModel;
import com.revolsys.swing.map.layer.record.table.model.RecordValidationDialog;
import com.revolsys.swing.map.overlay.AbstractOverlay;
import com.revolsys.swing.map.overlay.AddGeometryCompleteAction;
import com.revolsys.swing.map.overlay.CloseLocation;
import com.revolsys.swing.map.overlay.EditRecordGeometryOverlay;
import com.revolsys.swing.menu.MenuFactory;
import com.revolsys.swing.menu.Menus;
import com.revolsys.swing.menu.WrappedMenuFactory;
import com.revolsys.swing.parallel.Invoke;
import com.revolsys.swing.table.BaseJTable;
import com.revolsys.swing.undo.SetRecordFieldValueUndo;
import com.revolsys.util.CompareUtil;
import com.revolsys.util.Label;
import com.revolsys.util.PreferencesUtil;
import com.revolsys.util.Property;
import com.revolsys.util.ShortCounter;
public abstract class AbstractRecordLayer extends AbstractLayer
implements AddGeometryCompleteAction, RecordDefinitionProxy {
public static final String ALL = "All";
public static final String FORM_FACTORY_EXPRESSION = "formFactoryExpression";
public static final String RECORD_CACHE_MODIFIED = "recordCacheModified";
public static final String RECORD_DELETED_PERSISTED = "recordDeletedPersisted";
public static final String RECORD_UPDATED = "recordUpdated";
public static final String RECORDS_CHANGED = "recordsChanged";
public static final String RECORDS_DELETED = "recordsDeleted";
public static final String RECORDS_INSERTED = "recordsInserted";
public static final String RECORDS_SELECTED = "recordsSelected";
static {
final MenuFactory menu = MenuFactory.getMenu(AbstractRecordLayer.class);
menu.setName("Layer");
menu.addGroup(0, "table");
menu.addGroup(2, "edit");
menu.addGroup(3, "dnd");
final Predicate<AbstractRecordLayer> exists = AbstractRecordLayer::isExists;
Menus.addMenuItem(menu, "table", "View Records", "table_go", exists,
AbstractRecordLayer::showRecordsTable, false);
final Predicate<AbstractRecordLayer> hasSelectedRecords = AbstractRecordLayer::isHasSelectedRecords;
final Predicate<AbstractRecordLayer> hasSelectedRecordsWithGeometry = AbstractRecordLayer::isHasSelectedRecordsWithGeometry;
Menus.addMenuItem(menu, "zoom", "Zoom to Selected", "magnifier_zoom_selected",
hasSelectedRecordsWithGeometry, AbstractRecordLayer::zoomToSelected, true);
Menus.addMenuItem(menu, "zoom", "Pan to Selected", "pan_selected",
hasSelectedRecordsWithGeometry, AbstractRecordLayer::panToSelected, true);
final Predicate<AbstractRecordLayer> notReadOnly = ((Predicate<AbstractRecordLayer>)AbstractRecordLayer::isReadOnly)
.negate();
final Predicate<AbstractRecordLayer> canAdd = AbstractRecordLayer::isCanAddRecords;
Menus.addCheckboxMenuItem(menu, "edit", "Editable", "pencil", notReadOnly,
AbstractRecordLayer::toggleEditable, AbstractRecordLayer::isEditable, false);
Menus.addMenuItem(menu, "edit", "Save Changes", "table_save", AbstractLayer::isHasChanges,
AbstractLayer::saveChanges, true);
Menus.addMenuItem(menu, "edit", "Cancel Changes", "table_cancel", AbstractLayer::isHasChanges,
AbstractRecordLayer::cancelChanges, true);
Menus.addMenuItem(menu, "edit", "Add New Record", "table_row_insert", canAdd,
AbstractRecordLayer::addNewRecord, false);
Menus.addMenuItem(menu, "edit", "Delete Selected Records", "table_row_delete",
hasSelectedRecords.and(AbstractRecordLayer::isCanDeleteRecords),
AbstractRecordLayer::deleteSelectedRecords, true);
Menus.addMenuItem(menu, "edit", "Merge Selected Records", "table_row_merge",
AbstractRecordLayer::isCanMergeRecords, AbstractRecordLayer::mergeSelectedRecords, false);
Menus.addMenuItem(menu, "dnd", "Copy Selected Records", "page_copy", hasSelectedRecords,
AbstractRecordLayer::copySelectedRecords, true);
Menus.addMenuItem(menu, "dnd", "Paste New Records", "paste_plain",
canAdd.and(AbstractRecordLayer::isCanPasteRecords), AbstractRecordLayer::pasteRecords, true);
Menus.addMenuItem(menu, "layer", 0, "Layer Style", "palette",
AbstractRecordLayer::isHasGeometry,
(final AbstractRecordLayer layer) -> layer.showProperties("Style"), false);
}
public static void addVisibleLayers(final List<AbstractRecordLayer> layers,
final LayerGroup group, final double scale) {
if (group.isExists() && group.isVisible(scale)) {
for (final Layer layer : group) {
if (layer instanceof LayerGroup) {
final LayerGroup layerGroup = (LayerGroup)layer;
addVisibleLayers(layers, layerGroup, scale);
} else if (layer instanceof AbstractRecordLayer) {
if (layer.isExists() && layer.isVisible(scale)) {
final AbstractRecordLayer recordLayer = (AbstractRecordLayer)layer;
layers.add(recordLayer);
}
}
}
}
}
public static void exportRecords(final String title, final boolean hasGeometryField,
final Consumer<File> exportAction) {
Invoke.later(() -> {
final JFileChooser fileChooser = SwingUtil.newFileChooser("Export Records",
"com.revolsys.swing.map.table.export", "directory");
final String defaultFileExtension = PreferencesUtil
.getUserString("com.revolsys.swing.map.table.export", "fileExtension", "tsv");
final List<FileNameExtensionFilter> recordFileFilters = new ArrayList<>();
for (final RecordWriterFactory factory : IoFactory.factories(RecordWriterFactory.class)) {
if (hasGeometryField || factory.isCustomFieldsSupported()) {
recordFileFilters.add(IoFactory.newFileFilter(factory));
}
}
IoFactory.sortFilters(recordFileFilters);
fileChooser.setAcceptAllFileFilterUsed(false);
fileChooser.setSelectedFile(new File(fileChooser.getCurrentDirectory(), title));
for (final FileNameExtensionFilter fileFilter : recordFileFilters) {
fileChooser.addChoosableFileFilter(fileFilter);
if (Arrays.asList(fileFilter.getExtensions()).contains(defaultFileExtension)) {
fileChooser.setFileFilter(fileFilter);
}
}
fileChooser.setMultiSelectionEnabled(false);
final int returnVal = fileChooser.showSaveDialog(SwingUtil.getActiveWindow());
if (returnVal == JFileChooser.APPROVE_OPTION) {
final FileNameExtensionFilter fileFilter = (FileNameExtensionFilter)fileChooser
.getFileFilter();
File file = fileChooser.getSelectedFile();
if (file != null) {
final String fileExtension = FileUtil.getFileNameExtension(file);
final String expectedExtension = fileFilter.getExtensions().get(0);
if (!fileExtension.equals(expectedExtension)) {
file = FileUtil.getFileWithExtension(file, expectedExtension);
}
final File targetFile = file;
PreferencesUtil.setUserString("com.revolsys.swing.map.table.export", "fileExtension",
expectedExtension);
PreferencesUtil.setUserString("com.revolsys.swing.map.table.export", "directory",
file.getParent());
final String description = "Export " + title + " to " + targetFile.getAbsolutePath();
Invoke.background(description, () -> {
exportAction.accept(targetFile);
});
}
}
});
}
public static void forEachSelectedRecords(final Layer layer,
final Consumer<List<LayerRecord>> action) {
if (layer instanceof LayerGroup) {
final LayerGroup group = (LayerGroup)layer;
for (final Layer childLayer : group) {
forEachSelectedRecords(childLayer, action);
}
} else if (layer instanceof AbstractRecordLayer) {
final AbstractRecordLayer recordLayer = (AbstractRecordLayer)layer;
final List<LayerRecord> records = recordLayer.getSelectedRecords();
if (!records.isEmpty()) {
action.accept(records);
}
}
}
public static List<AbstractRecordLayer> getVisibleLayers(final LayerGroup group,
final double scale) {
final List<AbstractRecordLayer> layers = new ArrayList<>();
addVisibleLayers(layers, group, scale);
return layers;
}
private final Label cacheIdDeleted = new Label("deleted");
private final Label cacheIdForm = new Label("form");
private final Label cacheIdHighlighted = new Label("highlighted");
private final Label cacheIdIndex = new Label("index");
private final Label cacheIdModified = new Label("modified");
private final Label cacheIdNew = new Label("new");
private final Label cacheIdSelected = new Label("selected");
private boolean canAddRecords = true;
private boolean canDeleteRecords = true;
private boolean canEditRecords = true;
private boolean canPasteRecords = true;
private List<String> fieldNames = Collections.emptyList();
private String fieldNamesSetName = ALL;
private List<String> fieldNamesSetNames = new ArrayList<>();
private Map<String, List<String>> fieldNamesSets = new HashMap<>();
private Condition filter = Condition.ALL;
private final Map<String, Integer> fieldColumnWidths = new HashMap<>();
private List<Component> formComponents = new LinkedList<>();
private List<Record> formRecords = new LinkedList<>();
private List<Window> formWindows = new LinkedList<>();
private LayerRecordQuadTree index = new LayerRecordQuadTree();
private final Map<Identifier, ShortCounter> proxiedRecordIdentifiers = new HashMap<>();
private Set<LayerRecord> proxiedRecords = new HashSet<>();
private RecordDefinition recordDefinition;
private RecordFactory<? extends LayerRecord> recordFactory = this::newLayerRecord;
private LayerRecordMenu recordMenu;
private Map<Label, Collection<LayerRecord>> recordsByCacheId = new HashMap<>();
private LayerRecordQuadTree selectedRecordsIndex;
private boolean snapToAllLayers = true;
private boolean useFieldTitles = false;
private Set<String> userReadOnlyFieldNames = new LinkedHashSet<>();
private String where;
protected AbstractRecordLayer(final String type) {
super(type);
setReadOnly(false);
setSelectSupported(true);
setQuerySupported(true);
setRenderer(new GeometryStyleRenderer(this));
}
private void actionFlipFields(final LayerRecord record) {
final DirectionalFields property = DirectionalFields.getProperty(record);
property.reverseFieldValues(record);
}
private void actionFlipLineOrientation(final LayerRecord record) {
final DirectionalFields property = DirectionalFields.getProperty(record);
property.reverseGeometry(record);
}
private void actionFlipRecordOrientation(final LayerRecord record) {
DirectionalFields.reverse(record);
}
@Override
public void activatePanelComponent(final Component component, final Map<String, Object> config) {
super.activatePanelComponent(component, config);
if (component instanceof RecordLayerTablePanel) {
final RecordLayerTablePanel panel = (RecordLayerTablePanel)component;
final String fieldFilterMode = Maps.getString(config, "fieldFilterMode");
panel.setFieldFilterMode(fieldFilterMode);
}
}
@Override
public void addComplete(final AbstractOverlay overlay, final Geometry geometry) {
if (geometry != null) {
final RecordDefinition recordDefinition = getRecordDefinition();
final String geometryFieldName = recordDefinition.getGeometryFieldName();
final Map<String, Object> parameters = new HashMap<>();
parameters.put(geometryFieldName, geometry);
showAddForm(parameters);
}
}
protected void addHighlightedRecord(final LayerRecord record) {
addRecordToCache(this.cacheIdHighlighted, record);
}
public void addHighlightedRecords(final Collection<? extends LayerRecord> records) {
synchronized (getSync()) {
for (final LayerRecord record : records) {
addHighlightedRecord(record);
}
cleanCachedRecords();
}
fireHighlighted();
}
public void addHighlightedRecords(final LayerRecord... records) {
addHighlightedRecords(Arrays.asList(records));
}
protected void addModifiedRecord(final LayerRecord record) {
if (addRecordToCache(this.cacheIdModified, record)) {
firePropertyChange(RECORD_CACHE_MODIFIED, null, record.newRecordProxy());
fireHasChangedRecords();
cleanCachedRecords();
}
}
public void addNewRecord() {
final RecordDefinition recordDefinition = getRecordDefinition();
final FieldDefinition geometryField = recordDefinition.getGeometryField();
if (geometryField == null) {
showAddForm(null);
} else {
final MapPanel map = getMapPanel();
if (map != null) {
final EditRecordGeometryOverlay addGeometryOverlay = map
.getMapOverlay(EditRecordGeometryOverlay.class);
synchronized (addGeometryOverlay) {
clearSelectedRecords();
addGeometryOverlay.addRecord(this, this);
}
}
}
}
void addProxiedRecord(final LayerRecord record) {
synchronized (this.proxiedRecords) {
this.proxiedRecords.add(record);
}
}
void addProxiedRecordIdentifier(final Identifier identifier) {
ShortCounter.increment(this.proxiedRecordIdentifiers, identifier);
}
protected void addProxiedRecordIdsToCollection(final Collection<Identifier> identifiers) {
synchronized (this.proxiedRecords) {
for (final LayerRecord record : this.proxiedRecords) {
final Identifier identifier = record.getIdentifier();
if (identifier != null) {
identifiers.add(identifier);
}
}
}
synchronized (this.proxiedRecordIdentifiers) {
identifiers.addAll(this.proxiedRecordIdentifiers.keySet());
}
}
protected void addRecordsToCache(final Label cacheId,
final Iterable<? extends LayerRecord> records) {
synchronized (getSync()) {
for (final LayerRecord record : records) {
addRecordToCache(cacheId, record);
}
cleanCachedRecords();
}
}
protected boolean addRecordToCache(final Label cacheId, LayerRecord record) {
record = getProxiedRecord(record);
if (isLayerRecord(record)) {
if (record.getState() == RecordState.DELETED && !isDeleted(record)) {
} else {
synchronized (getSync()) {
Collection<LayerRecord> cachedRecords = this.recordsByCacheId.get(cacheId);
if (cachedRecords == null) {
cachedRecords = new ArrayList<>();
this.recordsByCacheId.put(cacheId, cachedRecords);
}
if (!cachedRecords.contains(record)) {
cachedRecords.add(record);
return true;
}
}
}
}
return false;
}
@Override
public int addRenderer(final LayerRenderer<?> child, final int index) {
final AbstractRecordLayerRenderer oldRenderer = getRenderer();
AbstractMultipleRenderer rendererGroup;
if (oldRenderer instanceof AbstractMultipleRenderer) {
rendererGroup = (AbstractMultipleRenderer)oldRenderer;
} else {
final AbstractRecordLayer layer = oldRenderer.getLayer();
rendererGroup = new MultipleRenderer(layer);
rendererGroup.addRenderer(oldRenderer);
setRenderer(rendererGroup);
}
if (index == 0) {
rendererGroup.addRenderer(0, (AbstractRecordLayerRenderer)child);
return 0;
} else {
rendererGroup.addRenderer((AbstractRecordLayerRenderer)child);
return rendererGroup.getRenderers().size() - 1;
}
}
protected boolean addSelectedRecord(final LayerRecord record) {
final boolean added = addRecordToCache(this.cacheIdSelected, record);
clearSelectedRecordsIndex();
return added;
}
public void addSelectedRecords(final BoundingBox boundingBox) {
if (isSelectable()) {
final List<LayerRecord> records = getRecordsVisible(boundingBox);
addSelectedRecords(records);
postSelectByBoundingBox(records);
}
}
public void addSelectedRecords(final Collection<? extends LayerRecord> records) {
final List<LayerRecord> newSelectedRecords = new ArrayList<>();
synchronized (getSync()) {
for (final LayerRecord record : records) {
if (addSelectedRecord(record)) {
newSelectedRecords.add(record);
}
}
}
firePropertyChange(RECORDS_SELECTED, null, newSelectedRecords);
fireSelected();
}
public void addSelectedRecords(final LayerRecord... records) {
addSelectedRecords(Arrays.asList(records));
}
public void addToIndex(final Collection<? extends LayerRecord> records) {
// Sync before to avoid deadlock if record calls layer.getSync() during add
synchronized (getSync()) {
for (final LayerRecord record : records) {
addToIndexDo(record);
}
}
}
public void addToIndex(final LayerRecord record) {
// Sync before to avoid deadlock if record calls layer.getSync() during add
synchronized (getSync()) {
if (record != null) {
if (record.hasGeometry()) {
addToIndexDo(record);
}
}
}
}
private void addToIndexDo(final LayerRecord record) {
synchronized (getSync()) {
addRecordToCache(this.cacheIdIndex, record);
final LayerRecord recordProxy = record.newRecordProxy();
this.index.addRecord(recordProxy);
}
}
public void addUserReadOnlyFieldNames(final Collection<String> userReadOnlyFieldNames) {
if (userReadOnlyFieldNames != null) {
this.userReadOnlyFieldNames.addAll(userReadOnlyFieldNames);
}
}
public void cancelChanges() {
try {
synchronized (this.getSync()) {
boolean cancelled = true;
try (
BaseCloseable eventsEnabled = eventsDisabled()) {
cancelled &= internalCancelChanges(this.cacheIdNew);
cancelled &= internalCancelChanges(this.cacheIdDeleted);
cancelled &= internalCancelChanges(this.cacheIdModified);
clearSelectedRecordsIndex();
cleanCachedRecords();
} finally {
fireRecordsChanged();
}
if (!cancelled) {
JOptionPane.showMessageDialog(getMapPanel(),
"<html><p>There was an error cancelling changes for one or more records.</p>" + "<p>"
+ getPath() + "</p>" + "<p>Check the logging panel for details.</html>",
"Error Cancelling Changes", JOptionPane.ERROR_MESSAGE);
}
}
} finally {
fireHasChangedRecords();
}
}
public boolean canPasteRecordGeometry(final LayerRecord record) {
if (isEditable()) {
final Geometry geometry = getPasteRecordGeometry(record, false);
return geometry != null;
} else {
return false;
}
}
protected void cleanCachedRecords() {
System.gc();
}
public void clearCachedRecords(final Label cacheId) {
synchronized (getSync()) {
this.recordsByCacheId.remove(cacheId);
}
}
public void clearHighlightedRecords() {
synchronized (getSync()) {
clearCachedRecords(this.cacheIdHighlighted);
cleanCachedRecords();
}
fireHighlighted();
}
public void clearSelectedRecords() {
final List<LayerRecord> selectedRecords = getSelectedRecords();
synchronized (getSync()) {
clearCachedRecords(this.cacheIdSelected);
clearCachedRecords(this.cacheIdHighlighted);
clearSelectedRecordsIndex();
}
firePropertyChange(RECORDS_SELECTED, selectedRecords, Collections.emptyList());
fireSelected();
}
protected void clearSelectedRecordsIndex() {
this.selectedRecordsIndex = null;
}
@Override
public AbstractRecordLayer clone() {
final AbstractRecordLayer clone = (AbstractRecordLayer)super.clone();
clone.recordsByCacheId = new HashMap<>();
clone.fieldNames = new ArrayList<>(this.fieldNames);
clone.fieldNamesSetNames = new ArrayList<>(this.fieldNamesSetNames);
clone.fieldNamesSets = new HashMap<>(this.fieldNamesSets);
clone.formRecords = new LinkedList<>();
clone.formComponents = new LinkedList<>();
clone.formWindows = new LinkedList<>();
clone.index = new LayerRecordQuadTree(getGeometryFactory());
clone.selectedRecordsIndex = null;
clone.proxiedRecords = new HashSet<>();
clone.filter = this.filter.clone();
clone.userReadOnlyFieldNames = new LinkedHashSet<>(this.userReadOnlyFieldNames);
return clone;
}
public void copyRecordGeometry(final LayerRecord record) {
final Geometry geometry = record.getGeometry();
if (geometry != null) {
final StringTransferable transferable = new StringTransferable(DataFlavor.stringFlavor,
geometry.toString());
ClipboardUtil.setContents(transferable);
}
}
public void copyRecordsToClipboard(final List<LayerRecord> records) {
if (!records.isEmpty()) {
final RecordDefinition recordDefinition = getRecordDefinition();
final List<Record> copies = new ArrayList<>();
for (final LayerRecord record : records) {
final ArrayRecord recordCopy = new ArrayRecord(recordDefinition, record);
copies.add(recordCopy);
}
final RecordReader reader = new ListRecordReader(recordDefinition, copies);
final RecordReaderTransferable transferable = new RecordReaderTransferable(reader);
ClipboardUtil.setContents(transferable);
}
}
public void copyRecordToClipboard(final LayerRecord record) {
copyRecordsToClipboard(Collections.singletonList(record));
}
public void copySelectedRecords() {
final List<LayerRecord> selectedRecords = getSelectedRecords();
copyRecordsToClipboard(selectedRecords);
}
@Override
public void delete() {
super.delete();
for (final Window window : this.formWindows) {
SwingUtil.dispose(window);
}
for (final Component form : this.formComponents) {
if (form != null) {
if (form instanceof LayerRecordForm) {
final LayerRecordForm recordForm = (LayerRecordForm)form;
Invoke.later(recordForm::destroy);
}
}
}
this.fieldNamesSetNames.clear();
this.fieldNamesSets.clear();
this.formRecords.clear();
this.formComponents.clear();
this.formWindows.clear();
this.index = new LayerRecordQuadTree(getGeometryFactory());
this.recordsByCacheId.clear();
this.selectedRecordsIndex = null;
}
public void deleteRecord(final LayerRecord record) {
final List<LayerRecord> records = Collections.singletonList(record);
deleteRecords(records);
}
public void deleteRecordAndSaveChanges(final LayerRecord record) {
deleteRecord(record);
saveChanges(record);
}
protected boolean deleteRecordDo(final LayerRecord record) {
final boolean isNew = isNew(record);
deleteRecordPre(record);
if (!isNew) {
addRecordToCache(this.cacheIdDeleted, record);
}
record.setState(RecordState.DELETED);
deleteRecordPost(record);
return true;
}
protected void deleteRecordPost(final LayerRecord record) {
}
protected void deleteRecordPre(final LayerRecord record) {
removeFromIndex(record);
removeRecordFromCache(record);
}
public void deleteRecords(final Collection<? extends LayerRecord> records) {
removeForms(records);
final List<LayerRecord> recordsDeleted = new ArrayList<>();
final List<LayerRecord> recordsSelected = new ArrayList<>();
try (
BaseCloseable eventsEnabled = eventsDisabled()) {
synchronized (this.getSync()) {
final boolean canDelete = isCanDeleteRecords();
for (final LayerRecord record : records) {
final boolean selected = isSelected(record);
boolean deleted = false;
if (removeRecordFromCache(this.cacheIdNew, record)) {
removeRecordFromCache(record);
record.setState(RecordState.DELETED);
deleted = true;
} else if (canDelete) {
if (deleteRecordDo(record)) {
deleted = true;
}
}
if (deleted) {
final LayerRecord recordProxy = record.newRecordProxy();
recordsDeleted.add(recordProxy);
if (selected) {
removeSelectedRecord(recordProxy);
recordsSelected.add(recordProxy);
}
}
}
}
}
deleteRecordsPost(recordsDeleted, recordsSelected);
}
public void deleteRecords(final LayerRecord... records) {
deleteRecords(Arrays.asList(records));
}
protected void deleteRecordsPost(final List<LayerRecord> recordsDeleted,
final List<LayerRecord> recordsSelected) {
if (!recordsSelected.isEmpty()) {
clearSelectedRecordsIndex();
firePropertyChange(RECORDS_SELECTED, recordsSelected, null);
fireSelected();
}
if (!recordsDeleted.isEmpty()) {
firePropertyChange(RECORDS_DELETED, null, recordsDeleted);
fireHasChangedRecords();
}
}
public void deleteSelectedRecords() {
final List<LayerRecord> selectedRecords = getSelectedRecords();
deleteRecords(selectedRecords);
}
public void exportRecords(final Iterable<LayerRecord> records,
final Predicate<? super LayerRecord> filter, final Map<String, Boolean> orderBy,
final Object target) {
if (Property.hasValue(records) && target != null) {
final List<LayerRecord> exportRecords = Lists.toArray(records);
Records.filterAndSort(exportRecords, filter, orderBy);
if (!exportRecords.isEmpty()) {
final RecordDefinition recordDefinition = getRecordDefinition();
RecordIo.copyRecords(recordDefinition, exportRecords, target);
}
}
}
public void exportRecords(final Query query, final Object target) {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition != null) {
try (
RecordWriter writer = RecordWriter.newRecordWriter(recordDefinition, target)) {
forEachRecordInternal(query, writer::write);
}
}
}
protected void fireHasChangedRecords() {
final boolean hasChangedRecords = isHasChangedRecords();
firePropertyChange("hasChangedRecords", !hasChangedRecords, hasChangedRecords);
}
protected void fireHighlighted() {
final int highlightedCount = getHighlightedCount();
final boolean highlighted = highlightedCount > 0;
firePropertyChange("hasHighlightedRecords", !highlighted, highlighted);
firePropertyChange("highlightedCount", -1, highlightedCount);
}
protected void fireRecordInserted(final LayerRecord record) {
final List<LayerRecord> records = Collections.singletonList(record);
firePropertyChange(RECORDS_INSERTED, null, records);
}
public void fireRecordsChanged() {
firePropertyChange(RECORDS_CHANGED, false, true);
}
protected void fireSelected() {
final int selectionCount = getSelectionCount();
final boolean selected = selectionCount > 0;
firePropertyChange("hasSelectedRecords", !selected, selected);
firePropertyChange("selectionCount", -1, selectionCount);
}
public void forEachRecord(final Iterable<LayerRecord> records,
final Predicate<? super LayerRecord> filter, final Map<String, Boolean> orderBy,
final Consumer<? super LayerRecord> action) {
try {
if (Property.hasValue(records) && action != null) {
final List<LayerRecord> exportRecords = Lists.toArray(records);
Records.filterAndSort(exportRecords, filter, orderBy);
if (!exportRecords.isEmpty()) {
exportRecords.forEach(action);
}
}
} catch (final CancellationException e) {
}
}
public void forEachRecord(final Query query, final Consumer<? super LayerRecord> consumer) {
forEachRecordInternal(query, (record) -> {
final LayerRecord proxyRecord = newProxyLayerRecord(record);
consumer.accept(proxyRecord);
});
}
public void forEachRecordChanged(final Query query,
final Consumer<? super LayerRecord> consumer) {
final List<LayerRecord> records = getRecordsChanged();
query.forEachRecord(records, consumer);
}
protected void forEachRecordInternal(final Query query,
final Consumer<? super LayerRecord> consumer) {
}
@SuppressWarnings("unchecked")
protected <V extends LayerRecord> V getCachedRecord(final Identifier identifier) {
return (V)getRecordById(identifier);
}
@SuppressWarnings("unchecked")
protected <V extends LayerRecord> V getCachedRecord(final Record record) {
return (V)record;
}
protected final Label getCacheIdDeleted() {
return this.cacheIdDeleted;
}
public Label getCacheIdForm() {
return this.cacheIdForm;
}
protected final Label getCacheIdHighlighted() {
return this.cacheIdHighlighted;
}
public Label getCacheIdIndex() {
return this.cacheIdIndex;
}
protected final Label getCacheIdModified() {
return this.cacheIdModified;
}
protected final Label getCacheIdNew() {
return this.cacheIdNew;
}
public Set<Label> getCacheIds(LayerRecord record) {
record = getProxiedRecord(record);
if (isLayerRecord(record)) {
synchronized (getSync()) {
return getCacheIdsDo(record);
}
} else {
return Collections.emptySet();
}
}
protected Set<Label> getCacheIdsDo(final LayerRecord record) {
final Set<Label> cacheIds = new HashSet<>();
for (final Entry<Label, Collection<LayerRecord>> entry : this.recordsByCacheId.entrySet()) {
final Collection<LayerRecord> records = entry.getValue();
if (records.contains(record)) {
final Label cacheId = entry.getKey();
cacheIds.add(cacheId);
}
}
return cacheIds;
}
protected final Label getCacheIdSelected() {
return this.cacheIdSelected;
}
@Override
public Collection<Class<?>> getChildClasses() {
return Collections.<Class<?>> singleton(AbstractRecordLayerRenderer.class);
}
public Comparator<?> getComparator(final String fieldName) {
final FieldDefinition field = getFieldDefinition(fieldName);
if (field == null) {
return CompareUtil.INSTANCE;
} else {
final Class<?> typeClass = field.getTypeClass();
return CompareUtil.getComparator(typeClass);
}
}
@Override
public CoordinateSystem getCoordinateSystem() {
final GeometryFactory geometryFactory = getGeometryFactory();
if (geometryFactory == null) {
return null;
} else {
return geometryFactory.getCoordinateSystem();
}
}
public int getFieldColumnWidth(final String fieldName) {
return Maps.get(this.fieldColumnWidths, fieldName, -1);
}
public Map<String, Integer> getFieldColumnWidths() {
return this.fieldColumnWidths;
}
@Override
public List<String> getFieldNames() {
return new ArrayList<>(this.fieldNames);
}
public List<String> getFieldNamesSet() {
return getFieldNamesSet(this.fieldNamesSetName);
}
public List<String> getFieldNamesSet(final String fieldNamesSetName) {
if (Property.hasValue(fieldNamesSetName)) {
List<String> fieldNames = this.fieldNamesSets.get(fieldNamesSetName.toUpperCase());
if (Property.hasValue(fieldNames)) {
fieldNames = new ArrayList<>(fieldNames);
if (Property.hasValue(this.fieldNames)) {
fieldNames.retainAll(this.fieldNames);
}
return fieldNames;
}
}
return getFieldNames();
}
public String getFieldNamesSetName() {
return this.fieldNamesSetName;
}
public List<String> getFieldNamesSetNames() {
return new ArrayList<>(this.fieldNamesSetNames);
}
public Map<String, List<String>> getFieldNamesSets() {
final Map<String, List<String>> fieldNamesSets = new LinkedHashMap<>();
for (final String fieldNamesSetName : getFieldNamesSetNames()) {
final List<String> fieldNames = getFieldNamesSet(fieldNamesSetName);
fieldNamesSets.put(fieldNamesSetName, fieldNames);
}
return fieldNamesSets;
}
public Condition getFilter() {
if (Property.hasValue(this.where)) {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition == null) {
return Condition.ALL;
} else {
this.filter = QueryValue.parseWhere(recordDefinition, this.where);
this.where = null;
}
}
return this.filter;
}
public DataType getGeometryType() {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition == null) {
return null;
} else {
final FieldDefinition geometryField = recordDefinition.getGeometryField();
if (geometryField == null) {
return null;
} else {
return geometryField.getDataType();
}
}
}
public BoundingBox getHighlightedBoundingBox() {
final GeometryFactory geometryFactory = getGeometryFactory();
BoundingBox boundingBox = geometryFactory.newBoundingBoxEmpty();
for (final Record record : getHighlightedRecords()) {
final Geometry geometry = record.getGeometry();
boundingBox = boundingBox.expandToInclude(geometry);
}
return boundingBox;
}
public int getHighlightedCount() {
return getRecordCountCached(this.cacheIdHighlighted);
}
public Collection<LayerRecord> getHighlightedRecords() {
return getRecordsCached(this.cacheIdHighlighted);
}
@Override
public String getIdFieldName() {
return getRecordDefinition().getIdFieldName();
}
@SuppressWarnings("unchecked")
public Set<String> getIgnorePasteFieldNames() {
final Set<String> ignoreFieldNames = Sets
.newHash((Collection<String>)getProperty("ignorePasteFields"));
ignoreFieldNames.addAll(getRecordDefinition().getIdFieldNames());
return ignoreFieldNames;
}
public MenuFactory getMenuFactory(final LayerRecord record) {
return null;
}
public List<LayerRecord> getMergeableSelectedRecords() {
final GeometryFactory geometryFactory = getGeometryFactory();
if (geometryFactory == null) {
return new ArrayList<>();
} else {
final List<LayerRecord> selectedRecords = getSelectedRecords();
for (final ListIterator<LayerRecord> iterator = selectedRecords.listIterator(); iterator
.hasNext();) {
final LayerRecord record = iterator.next();
if (record == null || isDeleted(record)) {
iterator.remove();
} else {
Geometry geometry = record.getGeometry();
geometry = geometryFactory.geometry(LineString.class, geometry);
if (!(geometry instanceof LineString)) {
iterator.remove();
}
}
}
return selectedRecords;
}
}
/**
* Get a record containing the values of the two records if they can be
* merged. The new record is not a layer data object so would need to be
* added, likewise the old records are not removed so they would need to be
* deleted.
*
* @param point
* @param record1
* @param record2
* @return
*/
public Record getMergedRecord(final Point point, final Record record1, final Record record2) {
if (record1 == record2) {
return record1;
} else {
final String sourceIdFieldName = getIdFieldName();
final Object id1 = record1.getValue(sourceIdFieldName);
final Object id2 = record2.getValue(sourceIdFieldName);
int compare = 0;
if (id1 == null) {
if (id2 != null) {
compare = 1;
}
} else if (id2 == null) {
compare = -1;
} else {
compare = CompareUtil.compare(id1, id2);
}
if (compare == 0) {
final Geometry geometry1 = record1.getGeometry();
final Geometry geometry2 = record2.getGeometry();
final double length1 = geometry1.getLength();
final double length2 = geometry2.getLength();
if (length1 == length2) {
compare = Integer.compare(System.identityHashCode(record1),
System.identityHashCode(record2));
} else if (length1 > length2) {
compare = -1;
} else {
compare = 1;
}
}
if (compare > 0) {
return getMergedRecord(point, record2, record1);
} else {
final DirectionalFields property = DirectionalFields.getProperty(getRecordDefinition());
final Map<String, Object> newValues = property.getMergedMap(point, record1, record2);
newValues.remove(getIdFieldName());
return new ArrayRecord(getRecordDefinition(), newValues);
}
}
}
public Map<String, Object> getPasteNewValues(final Record sourceRecord) {
final RecordDefinition recordDefinition = getRecordDefinition();
final Set<String> ignoreFieldNames = getIgnorePasteFieldNames();
final Map<String, Object> newValues = new LinkedHashMap<>();
for (final String fieldName : recordDefinition.getFieldNames()) {
if (!ignoreFieldNames.contains(fieldName)) {
final Object value = sourceRecord.getValue(fieldName);
if (value != null) {
newValues.put(fieldName, value);
}
}
}
final FieldDefinition geometryFieldDefinition = recordDefinition.getGeometryField();
if (geometryFieldDefinition != null) {
final GeometryFactory geometryFactory = getGeometryFactory();
Geometry sourceGeometry = sourceRecord.getGeometry();
final String geometryFieldName = geometryFieldDefinition.getName();
if (sourceGeometry == null) {
final Object value = sourceRecord.getValue(geometryFieldName);
sourceGeometry = geometryFieldDefinition.toFieldValue(value);
}
Geometry geometry = geometryFieldDefinition.toFieldValue(sourceGeometry);
if (geometry == null) {
if (sourceGeometry != null) {
newValues.put(geometryFieldName, sourceGeometry);
}
} else {
geometry = geometry.convertGeometry(geometryFactory);
newValues.put(geometryFieldName, geometry);
}
}
return newValues;
}
protected Geometry getPasteRecordGeometry(final LayerRecord record, final boolean alert) {
try {
if (record == null) {
return null;
} else {
final RecordDefinition recordDefinition = getRecordDefinition();
final FieldDefinition geometryField = recordDefinition.getGeometryField();
if (geometryField != null) {
final MapPanel parentComponent = getMapPanel();
Geometry geometry = null;
DataType geometryDataType = null;
Class<?> layerGeometryClass = null;
final GeometryFactory geometryFactory = getGeometryFactory();
geometryDataType = geometryField.getDataType();
layerGeometryClass = geometryDataType.getJavaClass();
RecordReader reader = ClipboardUtil
.getContents(RecordReaderTransferable.DATA_OBJECT_READER_FLAVOR);
if (reader == null) {
final String string = ClipboardUtil.getContents(DataFlavor.stringFlavor);
if (Property.hasValue(string)) {
try {
geometry = geometryFactory.geometry(string);
geometry = geometryFactory.geometry(layerGeometryClass, geometry);
if (geometry != null) {
return geometry;
}
} catch (final Throwable e) {
}
final Resource resource = new ByteArrayResource("t.csv", string);
reader = RecordReader.newRecordReader(resource);
} else {
return null;
}
}
if (reader != null) {
try {
for (final Record sourceRecord : reader) {
if (geometry == null) {
final Geometry sourceGeometry = sourceRecord.getGeometry();
if (sourceGeometry == null) {
if (alert) {
JOptionPane.showMessageDialog(parentComponent,
"Clipboard does not contain a record with a geometry.", "Paste Geometry",
JOptionPane.ERROR_MESSAGE);
}
return null;
}
geometry = geometryFactory.geometry(layerGeometryClass, sourceGeometry);
if (geometry == null) {
if (alert) {
JOptionPane.showMessageDialog(parentComponent,
"Clipboard should contain a record with a " + geometryDataType + " not a "
+ sourceGeometry.getGeometryType() + ".",
"Paste Geometry", JOptionPane.ERROR_MESSAGE);
}
return null;
}
} else {
if (alert) {
JOptionPane.showMessageDialog(parentComponent,
"Clipboard contains more than one record. Copy a single record.",
"Paste Geometry", JOptionPane.ERROR_MESSAGE);
}
return null;
}
}
} finally {
FileUtil.closeSilent(reader);
}
if (geometry == null) {
if (alert) {
JOptionPane.showMessageDialog(parentComponent,
"Clipboard does not contain a record with a geometry.", "Paste Geometry",
JOptionPane.ERROR_MESSAGE);
}
} else if (geometry.isEmpty()) {
if (alert) {
JOptionPane.showMessageDialog(parentComponent,
"Clipboard contains an empty geometry.", "Paste Geometry",
JOptionPane.ERROR_MESSAGE);
}
return null;
} else {
return geometry;
}
}
}
return null;
}
} catch (final Throwable t) {
return null;
}
}
@Override
public PathName getPathName() {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition == null) {
return null;
} else {
return recordDefinition.getPathName();
}
}
protected LayerRecord getProxiedRecord(LayerRecord record) {
if (record instanceof AbstractProxyLayerRecord) {
final AbstractProxyLayerRecord proxy = (AbstractProxyLayerRecord)record;
record = proxy.getRecordProxied();
}
return record;
}
public List<LayerRecord> getProxiedRecords() {
return new ArrayList<>(this.proxiedRecords);
}
public final Query getQuery() {
final RecordDefinition recordDefinition = getRecordDefinition();
final Condition whereCondition = getFilter();
return new Query(recordDefinition, whereCondition);
}
public LayerRecord getRecord(final Identifier identifier) {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition != null) {
final List<FieldDefinition> idFieldDefinitions = recordDefinition.getIdFields();
if (idFieldDefinitions.isEmpty()) {
final Query query = new Query(recordDefinition, Q.equalId(idFieldDefinitions, identifier));
for (final LayerRecord record : getRecords(query)) {
return record;
}
}
}
return null;
}
public LayerRecord getRecord(final int row) {
throw new UnsupportedOperationException();
}
public LayerRecord getRecordById(final Identifier identifier) {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition != null) {
final List<FieldDefinition> idFieldDefinitions = recordDefinition.getIdFields();
if (!idFieldDefinitions.isEmpty()) {
final Query query = new Query(recordDefinition, Q.equalId(idFieldDefinitions, identifier));
for (final LayerRecord record : getRecords(query)) {
return record;
}
}
}
return null;
}
/**
* Get the record count including any pending changes.
*
* @return
*/
public int getRecordCount() {
return getRecordCountPersisted() + getRecordCountNew() - getRecordCountDeleted();
}
public int getRecordCount(final Query query) {
return 0;
}
protected int getRecordCountCached(final Label cacheId) {
final Collection<LayerRecord> cachedRecords = this.recordsByCacheId.get(cacheId);
if (cachedRecords == null) {
return 0;
} else {
return cachedRecords.size();
}
}
public int getRecordCountDeleted() {
return getRecordCountCached(this.cacheIdDeleted);
}
public int getRecordCountModified() {
return getRecordCountCached(this.cacheIdModified);
}
public int getRecordCountNew() {
return getRecordCountCached(this.cacheIdNew);
}
public int getRecordCountPersisted() {
return 0;
}
public int getRecordCountPersisted(final Query query) {
return getRecordCount(query);
}
@Override
public RecordDefinition getRecordDefinition() {
return this.recordDefinition;
}
@SuppressWarnings({
"unchecked"
})
@Override
public <R extends Record> RecordFactory<R> getRecordFactory() {
return (RecordFactory<R>)this.recordFactory;
}
public LayerRecordMenu getRecordMenu() {
return this.recordMenu;
}
public LayerRecordMenu getRecordMenu(final LayerRecord record) {
if (isLayerRecord(record)) {
LayerRecordMenu.setEventRecord(record);
return this.recordMenu;
}
return null;
}
protected LayerRecord getRecordProxied(final LayerRecord record) {
if (record instanceof AbstractProxyLayerRecord) {
final AbstractProxyLayerRecord proxyRecord = (AbstractProxyLayerRecord)record;
return proxyRecord.getRecordProxied();
} else {
return record;
}
}
public List<LayerRecord> getRecords() {
throw new UnsupportedOperationException();
}
public <R extends LayerRecord> List<R> getRecords(BoundingBox boundingBox) {
if (hasGeometryField()) {
boundingBox = convertBoundingBox(boundingBox);
if (Property.hasValue(boundingBox)) {
final List<R> records = getRecordsIndex(boundingBox);
return records;
}
}
return Collections.emptyList();
}
public <R extends LayerRecord> List<R> getRecords(Geometry geometry, final double distance) {
if (geometry == null || !hasGeometryField()) {
return new ArrayList<>();
} else {
geometry = convertGeometry(geometry);
return getRecordsIndex(geometry, distance);
}
}
public <R extends LayerRecord> List<R> getRecords(final Query query) {
final List<R> records = new ArrayList<>();
final Consumer<LayerRecord> action = (Consumer<LayerRecord>)(Consumer<R>)records::add;
forEachRecord(query, action);
return records;
}
public List<LayerRecord> getRecordsBackground(final BoundingBox boundingBox) {
return getRecords(boundingBox);
}
public <R extends LayerRecord> List<R> getRecordsCached(final Label cacheId) {
synchronized (getSync()) {
final List<R> records = new ArrayList<>();
final Collection<LayerRecord> cachedRecords = this.recordsByCacheId.get(cacheId);
if (cachedRecords != null) {
for (final LayerRecord record : cachedRecords) {
final R proxyRecord = newProxyLayerRecord(record);
records.add(proxyRecord);
}
}
return records;
}
}
public <R extends LayerRecord> List<R> getRecordsChanged() {
synchronized (getSync()) {
final List<R> records = new ArrayList<>();
records.addAll(getRecordsNew());
records.addAll(getRecordsModified());
records.addAll(getRecordsDeleted());
return records;
}
}
public <R extends LayerRecord> List<R> getRecordsDeleted() {
return getRecordsCached(this.cacheIdDeleted);
}
@SuppressWarnings({
"unchecked", "rawtypes"
})
protected <R extends LayerRecord> List<R> getRecordsIndex(final BoundingBox boundingBox) {
synchronized (getSync()) {
final List<R> records = (List)this.index.queryIntersects(boundingBox);
return records;
}
}
@SuppressWarnings({
"unchecked", "rawtypes"
})
protected <R extends LayerRecord> List<R> getRecordsIndex(final Geometry geometry,
final double distance) {
synchronized (getSync()) {
return (List)this.index.getRecordsDistance(geometry, distance);
}
}
public <R extends LayerRecord> Collection<R> getRecordsModified() {
return getRecordsCached(this.cacheIdModified);
}
public <R extends LayerRecord> List<R> getRecordsNew() {
return getRecordsCached(this.cacheIdNew);
}
/**
* Query the underlying record store to return those records that have been
* saved that match the query.
*
* @param query
* @return The records.
*/
public <R extends LayerRecord> List<R> getRecordsPersisted(final Query query) {
return getRecords(query);
}
protected List<LayerRecord> getRecordsVisible(final BoundingBox boundingBox) {
final List<LayerRecord> records = getRecords(boundingBox);
for (final Iterator<LayerRecord> iterator = records.iterator(); iterator.hasNext();) {
final LayerRecord layerRecord = iterator.next();
if (!isVisible(layerRecord) || isDeleted(layerRecord)) {
iterator.remove();
}
}
return records;
}
@Override
public BoundingBox getSelectedBoundingBox() {
BoundingBox boundingBox = super.getSelectedBoundingBox();
for (final Record record : getSelectedRecords()) {
final Geometry geometry = record.getGeometry();
boundingBox = boundingBox.expandToInclude(geometry);
}
return boundingBox;
}
public List<LayerRecord> getSelectedRecords() {
return getRecordsCached(this.cacheIdSelected);
}
public List<LayerRecord> getSelectedRecords(final BoundingBox boundingBox) {
final LayerRecordQuadTree index = getSelectedRecordsIndex();
return index.queryIntersects(boundingBox);
}
protected LayerRecordQuadTree getSelectedRecordsIndex() {
if (this.selectedRecordsIndex == null) {
final List<LayerRecord> selectedRecords = getSelectedRecords();
final LayerRecordQuadTree index = new LayerRecordQuadTree(getProject().getGeometryFactory(),
selectedRecords);
this.selectedRecordsIndex = index;
}
return this.selectedRecordsIndex;
}
public int getSelectionCount() {
return getRecordCountCached(this.cacheIdSelected);
}
public Collection<String> getSnapLayerPaths() {
return getProperty("snapLayers", Collections.<String> emptyList());
}
public Collection<String> getUserReadOnlyFieldNames() {
return Collections.unmodifiableSet(this.userReadOnlyFieldNames);
}
public Object getValidSearchValue(final FieldDefinition field, final Object fieldValue) {
try {
return field.toFieldValueException(fieldValue);
} catch (final Throwable t) {
return null;
}
}
public String getWhere() {
if (Property.isEmpty(this.filter)) {
return this.where;
} else {
return this.filter.toFormattedString();
}
}
public boolean hasFieldNamesSet(final String fieldNamesSetName) {
if (Property.hasValue(fieldNamesSetName)) {
final List<String> fieldNames = this.fieldNamesSets.get(fieldNamesSetName.toUpperCase());
if (Property.hasValue(fieldNames)) {
return true;
}
}
return false;
}
public boolean hasGeometryField() {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition == null) {
return false;
} else {
return recordDefinition.getGeometryField() != null;
}
}
protected boolean hasPermission(final String permission) {
if (this.recordDefinition == null) {
return true;
} else {
final Collection<String> permissions = this.recordDefinition.getProperty("permissions");
if (permissions == null) {
return true;
} else {
final boolean hasPermission = permissions.contains(permission);
return hasPermission;
}
}
}
@Override
protected boolean initializeDo() {
initRecordMenu();
return super.initializeDo();
}
protected LayerRecordMenu initRecordMenu() {
final LayerRecordMenu menu = new LayerRecordMenu(this);
this.recordMenu = menu;
if (this.recordDefinition != null) {
final RecordDefinition recordDefinition = getRecordDefinition();
final boolean hasGeometry = recordDefinition.hasGeometryField();
final Predicate<LayerRecord> modified = LayerRecord::isModified;
final Predicate<LayerRecord> notDeleted = ((Predicate<LayerRecord>)this::isDeleted).negate();
final Predicate<LayerRecord> modifiedOrDeleted = modified.or(LayerRecord::isDeleted);
final EnableCheck editableEnableCheck = this::isEditable;
menu.addGroup(0, "default");
menu.addGroup(1, "record");
menu.addGroup(2, "dnd");
final MenuFactory layerMenuFactory = MenuFactory.findMenu(this);
if (layerMenuFactory != null) {
menu.addComponentFactory("default", 0, new WrappedMenuFactory("Layer", layerMenuFactory));
}
menu.addMenuItem("record", "View/Edit Record", "table_edit", notDeleted, this::showForm);
if (hasGeometry) {
menu.addMenuItem("record", "Zoom to Record", "magnifier_zoom_selected", notDeleted,
this::zoomToRecord);
menu.addMenuItem("record", "Pan to Record", "pan_selected", notDeleted, (record) -> {
final MapPanel mapPanel = getMapPanel();
if (mapPanel != null) {
mapPanel.panToRecord(record);
}
});
final MenuFactory editMenu = new MenuFactory("Edit Record Operations");
editMenu.setEnableCheck(LayerRecordMenu.enableCheck(notDeleted));
final DataType geometryDataType = recordDefinition.getGeometryField().getDataType();
if (geometryDataType == DataTypes.LINE_STRING
|| geometryDataType == DataTypes.MULTI_LINE_STRING) {
if (DirectionalFields.getProperty(recordDefinition).hasDirectionalFields()) {
LayerRecordMenu.addMenuItem(editMenu, "geometry", LayerRecordForm.FLIP_RECORD_NAME,
LayerRecordForm.FLIP_RECORD_ICON, editableEnableCheck,
this::actionFlipRecordOrientation);
LayerRecordMenu.addMenuItem(editMenu, "geometry",
LayerRecordForm.FLIP_LINE_ORIENTATION_NAME,
LayerRecordForm.FLIP_LINE_ORIENTATION_ICON, editableEnableCheck,
this::actionFlipLineOrientation);
LayerRecordMenu.addMenuItem(editMenu, "geometry", LayerRecordForm.FLIP_FIELDS_NAME,
LayerRecordForm.FLIP_FIELDS_ICON, editableEnableCheck, this::actionFlipFields);
} else {
LayerRecordMenu.addMenuItem(editMenu, "geometry", "Flip Line Orientation", "flip_line",
editableEnableCheck, this::actionFlipLineOrientation);
}
}
menu.addComponentFactory("record", editMenu);
}
menu.addMenuItem("record", "Delete Record", "table_row_delete", LayerRecord::isDeletable,
this::deleteRecord);
menu.addMenuItem("record", "Revert Record", "arrow_revert", modifiedOrDeleted,
LayerRecord::revertChanges);
final Predicate<LayerRecord> hasModifiedEmptyFields = LayerRecord::isHasModifiedEmptyFields;
menu.addMenuItem("record", "Revert Empty Fields", "field_empty_revert",
hasModifiedEmptyFields, LayerRecord::revertEmptyFields);
menu.addMenuItem("dnd", "Copy Record", "page_copy", this::copyRecordToClipboard);
if (hasGeometry) {
menu.addMenuItem("dnd", "Paste Geometry", "geometry_paste", this::canPasteRecordGeometry,
this::pasteRecordGeometry);
}
}
return menu;
}
/**
* Cancel changes for one of the lists of changes {@link #deletedRecords},
* {@link #newRecords}, {@link #modifiedRecords}.
*
* @param cacheId
*/
private boolean internalCancelChanges(final Label cacheId) {
boolean cancelled = true;
for (final LayerRecord record : getRecordsCached(cacheId)) {
removeFromIndex(record);
try {
removeRecordFromCache(cacheId, record);
if (cacheId == this.cacheIdNew) {
removeRecordFromCache(record);
record.setState(RecordState.DELETED);
} else {
internalCancelChanges(record);
addToIndex(record);
}
} catch (final Throwable e) {
Logs.error(this, "Unable to cancel changes.\n" + record, e);
cancelled = false;
}
}
return cancelled;
}
/**
* Revert the values of the record to the last values loaded from the database
*
* @param record
*/
protected void internalCancelChanges(final LayerRecord record) {
if (record != null) {
record.cancelChanges();
}
}
protected boolean internalIsDeleted(final LayerRecord record) {
if (record == null) {
return false;
} else if (record.getState() == RecordState.DELETED) {
return true;
} else {
return isRecordCached(this.cacheIdDeleted, record);
}
}
/**
* Revert the values of the record to the last values loaded from the database
*
* @param record
*/
protected LayerRecord internalPostSaveChanges(final LayerRecord record) {
if (record != null) {
record.clearChanges();
return record;
}
return null;
}
protected boolean internalSaveChanges(final RecordLayerErrors errors, final LayerRecord record) {
final RecordState originalState = record.getState();
final LayerRecord layerRecord = getProxiedRecord(record);
final boolean saved = saveChangesDo(errors, layerRecord);
if (saved) {
postSaveChanges(originalState, layerRecord);
}
return saved;
}
public boolean isCanAddRecords() {
return !super.isReadOnly() && isEditable() && this.canAddRecords && hasPermission("INSERT");
}
public boolean isCanDeleteRecords() {
return !super.isReadOnly() && isEditable() && this.canDeleteRecords && hasPermission("DELETE");
}
public boolean isCanEditRecords() {
return !super.isReadOnly() && isEditable() && this.canEditRecords && hasPermission("UPDATE");
}
public boolean isCanMergeRecords() {
if (isCanAddRecords()) {
if (isCanDeleteRecords()) {
final DataType geometryType = getGeometryType();
if (DataTypes.POINT.equals(geometryType)) {
return false;
} else if (DataTypes.MULTI_POINT.equals(geometryType)) {
return false;
} else if (DataTypes.POLYGON.equals(geometryType)) {
return false;
} else if (DataTypes.MULTI_POLYGON.equals(geometryType)) {
return false;
}
final List<LayerRecord> mergeableSelectedRecords = getMergeableSelectedRecords();
if (mergeableSelectedRecords.size() > 1) {
return true;
}
}
}
return false;
}
public boolean isCanPasteRecords() {
if (isExists()) {
if (!this.canPasteRecords) {
return false;
} else if (super.isReadOnly()) {
return false;
} else if (!super.isEditable()) {
return false;
} else if (ClipboardUtil
.isDataFlavorAvailable(RecordReaderTransferable.DATA_OBJECT_READER_FLAVOR)) {
return true;
} else {
if (ClipboardUtil.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
final String string = ClipboardUtil.getContents(DataFlavor.stringFlavor);
if (Property.hasValue(string)) {
int lineIndex = string.indexOf('\n');
if (lineIndex == -1) {
lineIndex = string.indexOf('\r');
}
if (lineIndex != -1) {
final String line = string.substring(0, lineIndex).trim();
String fieldName;
final int tabIndex = line.indexOf('\t');
if (tabIndex != -1) {
fieldName = line.substring(0, tabIndex);
} else {
final int commaIndex = line.indexOf(',');
if (commaIndex != -1) {
fieldName = line.substring(0, commaIndex);
} else {
fieldName = line;
}
}
if (fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1);
}
if (fieldName.endsWith("\"")) {
fieldName = fieldName.substring(0, fieldName.length() - 1);
}
if (getRecordDefinition().hasField(fieldName)) {
return true;
}
}
}
}
}
}
return false;
}
@Override
public boolean isClonable() {
return true;
}
public boolean isDeleted(final LayerRecord record) {
return internalIsDeleted(record);
}
public boolean isFieldUserReadOnly(final String fieldName) {
return getUserReadOnlyFieldNames().contains(fieldName);
}
public boolean isHasCachedRecords(final Label cacheId) {
return getRecordCountCached(cacheId) > 0;
}
public boolean isHasChangedRecords() {
return isHasChanges();
}
@Override
public boolean isHasChanges() {
if (isEditable()) {
if (isHasCachedRecords(this.cacheIdNew)) {
return true;
} else if (isHasCachedRecords(this.cacheIdModified)) {
return true;
} else if (isHasCachedRecords(this.cacheIdDeleted)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
@Override
public boolean isHasGeometry() {
return isExists() && getGeometryFieldName() != null;
}
@Override
public boolean isHasSelectedRecords() {
return isExists() && isHasCachedRecords(this.cacheIdSelected);
}
public boolean isHasSelectedRecordsWithGeometry() {
return isHasGeometry() && isHasSelectedRecords();
}
public boolean isHidden(final LayerRecord record) {
if (isCanDeleteRecords() && isDeleted(record)) {
return true;
} else if (isSelectable() && isSelected(record)) {
return true;
} else {
return false;
}
}
public boolean isHighlighted(final LayerRecord record) {
return isRecordCached(this.cacheIdHighlighted, record);
}
public boolean isLayerRecord(final Record record) {
if (record == null) {
return false;
} else if (record.getRecordDefinition() == getRecordDefinition()) {
return true;
} else {
return false;
}
}
public boolean isModified(final LayerRecord record) {
return isRecordCached(this.cacheIdModified, record);
}
public boolean isNew(final LayerRecord record) {
return isRecordCached(this.cacheIdNew, record);
}
protected boolean isPostSaveRemoveCacheId(final Label cacheId) {
if (cacheId == this.cacheIdDeleted || cacheId == this.cacheIdNew
|| cacheId == this.cacheIdModified) {
return true;
} else {
return false;
}
}
@Override
public boolean isReadOnly() {
if (super.isReadOnly()) {
return true;
} else {
if (this.canAddRecords && hasPermission("INSERT")) {
return false;
} else if (this.canDeleteRecords && hasPermission("DELETE")) {
return false;
} else if (this.canEditRecords && hasPermission("UPDATE")) {
return false;
} else {
return true;
}
}
}
public boolean isRecordCached(final Label cacheId, LayerRecord record) {
record = getProxiedRecord(record);
if (isLayerRecord(record)) {
synchronized (getSync()) {
final Collection<LayerRecord> cachedRecords = this.recordsByCacheId.get(cacheId);
if (cachedRecords != null) {
return cachedRecords.contains(record);
}
}
}
return false;
}
public boolean isSelected(final LayerRecord record) {
return isRecordCached(this.cacheIdSelected, record);
}
public boolean isSnapToAllLayers() {
return this.snapToAllLayers;
}
public boolean isUseFieldTitles() {
return this.useFieldTitles;
}
public boolean isVisible(final LayerRecord record) {
if (isExists() && isVisible()) {
final AbstractRecordLayerRenderer renderer = getRenderer();
if (renderer == null || renderer.isVisible(record)) {
return true;
}
}
return false;
}
private void mergeSelectedRecords() {
if (isCanMergeRecords()) {
MergeRecordsDialog.showDialog(this);
}
}
protected LayerRecordForm newDefaultForm(final LayerRecord record) {
return new LayerRecordForm(this, record);
}
public LayerRecordForm newForm(final LayerRecord record) {
final String formFactoryExpression = getProperty(FORM_FACTORY_EXPRESSION);
if (Property.hasValue(formFactoryExpression)) {
try {
final SpelExpressionParser parser = new SpelExpressionParser();
final Expression expression = parser.parseExpression(formFactoryExpression);
final EvaluationContext context = new StandardEvaluationContext(this);
context.setVariable("object", record);
return expression.getValue(context, LayerRecordForm.class);
} catch (final Throwable e) {
Logs.error(this, "Unable to create form for " + this, e);
return null;
}
} else {
return newDefaultForm(record);
}
}
public LayerRecord newLayerRecord(final Map<String, ? extends Object> values) {
if (!isReadOnly() && isEditable() && isCanAddRecords()) {
final RecordFactory<LayerRecord> recordFactory = getRecordFactory();
final LayerRecord record = recordFactory.newRecord(getRecordDefinition());
record.setState(RecordState.INITIALIZING);
try {
if (values != null && !values.isEmpty()) {
record.setValuesByPath(values);
record.setIdentifier(null);
}
} finally {
record.setState(RecordState.NEW);
}
addRecordToCache(this.cacheIdNew, record);
fireRecordInserted(record);
fireHasChangedRecords();
return record;
} else {
return null;
}
}
protected LayerRecord newLayerRecord(final RecordDefinition recordDefinition) {
if (recordDefinition.equals(getRecordDefinition())) {
return new ArrayLayerRecord(this);
} else {
throw new IllegalArgumentException("Cannot create records for " + recordDefinition);
}
}
@Override
public TabbedValuePanel newPropertiesPanel() {
final TabbedValuePanel propertiesPanel = super.newPropertiesPanel();
newPropertiesPanelFields(propertiesPanel);
newPropertiesPanelFieldNamesSet(propertiesPanel);
newPropertiesPanelStyle(propertiesPanel);
newPropertiesPanelSnapping(propertiesPanel);
return propertiesPanel;
}
protected void newPropertiesPanelFieldNamesSet(final TabbedValuePanel propertiesPanel) {
final FieldNamesSetPanel panel = new FieldNamesSetPanel(this);
propertiesPanel.addTab("Field Sets", panel);
}
protected void newPropertiesPanelFields(final TabbedValuePanel propertiesPanel) {
final RecordDefinition recordDefinition = getRecordDefinition();
final BaseJTable fieldTable = RecordDefinitionTableModel.newTable(recordDefinition);
final BasePanel fieldPanel = new BasePanel(new BorderLayout());
fieldPanel.setPreferredSize(new Dimension(500, 400));
final JScrollPane fieldScroll = new JScrollPane(fieldTable);
fieldPanel.add(fieldScroll, BorderLayout.CENTER);
propertiesPanel.addTab("Fields", fieldPanel);
}
protected void newPropertiesPanelSnapping(final TabbedValuePanel propertiesPanel) {
final SnapLayersPanel panel = new SnapLayersPanel(this);
propertiesPanel.addTab("Snapping", panel);
}
protected void newPropertiesPanelStyle(final TabbedValuePanel propertiesPanel) {
if (getRenderer() != null) {
final LayerStylePanel stylePanel = new LayerStylePanel(this);
propertiesPanel.addTab("Style", "palette", stylePanel);
}
}
@Override
protected BasePanel newPropertiesTabGeneral(final TabbedValuePanel tabPanel) {
final BasePanel generalPanel = super.newPropertiesTabGeneral(tabPanel);
newPropertiesTabGeneralPanelFilter(generalPanel);
return generalPanel;
}
protected ValueField newPropertiesTabGeneralPanelFilter(final BasePanel parent) {
final ValueField filterPanel = new ValueField(this);
Borders.titled(filterPanel, "Filter");
final QueryFilterField field = new QueryFilterField(this, "where", getWhere());
filterPanel.add(field);
Property.addListener(field, "where", getBeanPropertyListener());
GroupLayouts.makeColumns(filterPanel, 1, true);
parent.add(filterPanel);
return filterPanel;
}
@SuppressWarnings("unchecked")
protected <R extends LayerRecord> R newProxyLayerRecord(final LayerRecord record) {
return (R)record;
}
protected <R extends LayerRecord> List<R> newProxyLayerRecords(
final Iterable<? extends LayerRecord> records) {
final List<R> proxyRecords = new ArrayList<>();
for (final LayerRecord record : records) {
final R proxyRecord = newProxyLayerRecord(record);
proxyRecords.add(proxyRecord);
}
return proxyRecords;
}
public JComponent newSearchField(final FieldDefinition fieldDefinition,
final CodeTable codeTable) {
if (fieldDefinition == null) {
return new TextField(20);
} else {
final String fieldName = fieldDefinition.getName();
return RecordLayerFields.newCompactField(this, fieldName, true);
}
}
public UndoableEdit newSetFieldUndo(final LayerRecord record, final String fieldName,
final Object oldValue, final Object newValue) {
return new SetRecordFieldValueUndo(record, fieldName, oldValue, newValue);
}
protected Map<String, Object> newSplitValues(final LayerRecord oldRecord,
final LineString oldLine, final Point splitPoint, final LineString newLine) {
final DirectionalFields directionalFields = DirectionalFields.getProperty(oldRecord);
final Map<String, Object> values1 = directionalFields.newSplitValues(oldRecord, oldLine,
splitPoint, newLine);
return values1;
}
public RecordLayerTablePanel newTablePanel(final Map<String, Object> config) {
final RecordLayerTable table = RecordLayerTableModel.newTable(this);
if (table == null) {
return null;
} else {
return new RecordLayerTablePanel(this, table, config);
}
}
@Override
protected Component newTableViewComponent(final Map<String, Object> config) {
return newTablePanel(config);
}
public void panToBoundingBox(final BoundingBox boundingBox) {
final MapPanel mapPanel = getMapPanel();
mapPanel.panToBoundingBox(boundingBox);
}
public void panToSelected() {
final BoundingBox selectedBoundingBox = getSelectedBoundingBox();
panToBoundingBox(selectedBoundingBox);
}
public void pasteRecordGeometry(final LayerRecord record) {
final Geometry geometry = getPasteRecordGeometry(record, true);
if (geometry != null) {
record.setGeometryValue(geometry);
}
}
public void pasteRecords() {
final List<LayerRecord> newRecords = new ArrayList<>();
try (
BaseCloseable eventsEnabled = eventsDisabled()) {
RecordReader reader = ClipboardUtil
.getContents(RecordReaderTransferable.DATA_OBJECT_READER_FLAVOR);
if (reader == null) {
final String string = ClipboardUtil.getContents(DataFlavor.stringFlavor);
if (Property.hasValue(string)) {
if (string.contains("\t")) {
final Resource tsvResource = new ByteArrayResource("t.tsv", string);
reader = RecordReader.newRecordReader(tsvResource);
} else {
final Resource resource = new ByteArrayResource("t.csv", string);
reader = RecordReader.newRecordReader(resource);
}
}
}
if (reader != null) {
for (final Record sourceRecord : reader) {
final Map<String, Object> newValues = getPasteNewValues(sourceRecord);
if (!newValues.isEmpty()) {
final LayerRecord newRecord = newLayerRecord(newValues);
if (newRecord != null) {
newRecords.add(newRecord);
}
}
}
}
} catch (final Throwable e) {
LoggingEventPanel.showDialog(getMapPanel(), "Unexpected error pasting records", e);
return;
}
RecordValidationDialog.validateRecords("Pasting Records", this, newRecords, (validator) -> {
// Success
// Save the valid records
final List<LayerRecord> validRecords = validator.getValidRecords();
if (!validRecords.isEmpty()) {
saveChanges(validRecords);
addSelectedRecords(validRecords);
zoomToRecords(validRecords);
showRecordsTable(RecordLayerTableModel.MODE_RECORDS_SELECTED);
firePropertyChange(RECORDS_INSERTED, null, validRecords);
}
// Delete any invalid records
final List<LayerRecord> invalidRecords = validator.getInvalidRecords();
if (!invalidRecords.isEmpty()) {
deleteRecords(invalidRecords);
}
}, (validator) -> {
// Cancel, delete all the records
deleteRecords(newRecords);
});
}
protected void postSaveChanges(final RecordState originalState, final LayerRecord record) {
postSaveDeletedRecord(record);
postSaveModifiedRecord(record);
postSaveNewRecord(record);
}
protected boolean postSaveDeletedRecord(final LayerRecord record) {
final boolean deleted;
synchronized (getSync()) {
deleted = removeRecordFromCache(this.cacheIdDeleted, record);
}
if (deleted) {
removeRecordFromCache(record);
removeFromIndex(record);
return true;
} else {
return false;
}
}
protected boolean postSaveModifiedRecord(final LayerRecord record) {
boolean removed;
synchronized (getSync()) {
removed = removeRecordFromCache(this.cacheIdModified, record);
}
if (removed) {
record.postSaveModified();
return true;
} else {
return false;
}
}
protected boolean postSaveNewRecord(final LayerRecord record) {
boolean isNew;
final boolean selected;
final boolean highlighted;
synchronized (getSync()) {
selected = isSelected(record);
highlighted = isHighlighted(record);
isNew = removeRecordFromCache(this.cacheIdNew, record);
}
if (isNew) {
removeRecordFromCache(record);
record.postSaveNew();
addToIndex(record);
setSelectedHighlighted(record, selected, highlighted);
}
return isNew;
}
protected void postSelectByBoundingBox(final List<LayerRecord> records) {
if (isHasSelectedRecordsWithGeometry()) {
showRecordsTable(RecordLayerTableModel.MODE_RECORDS_SELECTED);
}
if (!records.isEmpty()) {
firePropertyChange("selectedRecordsByBoundingBox", false, true);
}
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
super.propertyChange(event);
if (isExists()) {
final Object source = event.getSource();
final String propertyName = event.getPropertyName();
if (!"qaMessagesUpdated".equals(propertyName)) {
if (source instanceof LayerRecord) {
final LayerRecord record = (LayerRecord)source;
if (record.getLayer() == this) {
if (DataType.equal(propertyName, getGeometryFieldName())) {
final Geometry oldGeometry = (Geometry)event.getOldValue();
updateSpatialIndex(record, oldGeometry);
clearSelectedRecordsIndex();
}
}
}
}
}
}
protected void recordPasted(final LayerRecord newRecord) {
}
@Override
protected void refreshDo() {
setIndexRecords(null);
cleanCachedRecords();
}
@Override
protected void refreshPostDo() {
super.refreshPostDo();
fireRecordsChanged();
}
protected void removeForm(final LayerRecord record) {
final List<LayerRecord> records = Collections.singletonList(record);
removeForms(records);
cleanCachedRecords();
}
protected void removeForms(final Iterable<? extends LayerRecord> records) {
final List<LayerRecordForm> forms = new ArrayList<>();
final List<Window> windows = new ArrayList<>();
synchronized (this.formRecords) {
for (final LayerRecord record : records) {
final LayerRecord proxiedRecord = getRecordProxied(record);
final int index = proxiedRecord.indexOf(this.formRecords);
if (index == -1) {
} else {
removeRecordFromCache(this.cacheIdForm, proxiedRecord);
this.formRecords.remove(index);
final Component component = this.formComponents.remove(index);
if (component instanceof LayerRecordForm) {
final LayerRecordForm form = (LayerRecordForm)component;
forms.add(form);
}
final Window window = this.formWindows.remove(index);
if (window != null) {
windows.add(window);
}
}
}
}
if (!forms.isEmpty() && !windows.isEmpty()) {
Invoke.later(() -> {
for (final LayerRecordForm form : forms) {
form.destroy();
}
for (final Window window : windows) {
SwingUtil.dispose(window);
}
});
}
}
public boolean removeFromIndex(final BoundingBox boundingBox, final LayerRecord record) {
synchronized (getSync()) {
// Sync before to avoid deadlock if record calls layer.getSync() during
// remove
return this.index.removeRecord(record);
}
}
public void removeFromIndex(final Collection<? extends LayerRecord> records) {
for (final LayerRecord record : records) {
removeFromIndex(record);
}
}
public void removeFromIndex(final LayerRecord record) {
final Geometry geometry = record.getGeometry();
if (geometry != null && !geometry.isEmpty()) {
synchronized (getSync()) {
removeRecordFromCache(this.cacheIdIndex, record);
final BoundingBox boundingBox = geometry.getBoundingBox();
removeFromIndex(boundingBox, record);
}
}
}
protected void removeHighlightedRecord(final LayerRecord record) {
removeRecordFromCache(this.cacheIdHighlighted, record);
}
void removeProxiedRecord(final LayerRecord proxyRecord) {
if (proxyRecord != null) {
synchronized (proxyRecord) {
this.proxiedRecords.remove(proxyRecord);
}
}
}
void removeProxiedRecordIdentifier(final Identifier identifier) {
ShortCounter.deccrement(this.proxiedRecordIdentifiers, identifier);
}
protected boolean removeRecordFromCache(final Label cacheId, LayerRecord record) {
record = getProxiedRecord(record);
if (isLayerRecord(record)) {
synchronized (getSync()) {
return Maps.removeFromCollection(this.recordsByCacheId, cacheId, record);
}
}
return false;
}
protected boolean removeRecordFromCache(LayerRecord record) {
boolean removed = false;
record = getProxiedRecord(record);
synchronized (getSync()) {
if (isLayerRecord(record)) {
for (final Label cacheId : new ArrayList<>(this.recordsByCacheId.keySet())) {
removed |= removeRecordFromCache(cacheId, record);
}
}
}
return removed;
}
public int removeRecordsFromCache(final Label cacheId,
final Collection<? extends LayerRecord> records) {
synchronized (getSync()) {
int count = 0;
for (final LayerRecord record : records) {
if (removeRecordFromCache(cacheId, record)) {
count++;
}
}
cleanCachedRecords();
return count;
}
}
protected boolean removeSelectedRecord(final LayerRecord record) {
final boolean removed = removeRecordFromCache(this.cacheIdSelected, record);
removeHighlightedRecord(record);
clearSelectedRecordsIndex();
return removed;
}
public void replaceValues(final LayerRecord record, final Map<String, Object> values) {
record.setValues(values);
}
public void revertChanges(final LayerRecord record) {
synchronized (getSync()) {
if (isLayerRecord(record)) {
final boolean selected = isSelected(record);
final boolean highlighted = isHighlighted(record);
postSaveModifiedRecord(record);
if (removeRecordFromCache(this.cacheIdDeleted, record)) {
record.setState(RecordState.PERSISTED);
}
removeRecordFromCache(record);
setSelectedHighlighted(record, selected, highlighted);
cleanCachedRecords();
}
}
}
@Override
public boolean saveChanges() {
if (isExists()) {
final List<LayerRecord> allRecords = new ArrayList<>();
for (final Label cacheId : Arrays.asList(this.cacheIdDeleted, this.cacheIdModified,
this.cacheIdNew)) {
final List<LayerRecord> records = getRecordsCached(cacheId);
allRecords.addAll(records);
}
return saveChanges(allRecords);
} else {
return false;
}
}
public final boolean saveChanges(final Collection<? extends LayerRecord> records) {
try {
if (records.isEmpty()) {
return true;
} else {
// Includes two types of validation of record. The first checks field
// types before interacting with the record store. The second is any
// errors on the actual saving of data.
final Set<Boolean> allSaved = new HashSet<>();
RecordValidationDialog.validateRecords("Save Changes", //
this, //
records, (validator) -> {
// Success
// Save the valid records
final List<LayerRecord> validRecords = validator.getValidRecords();
if (!validRecords.isEmpty()) {
final RecordLayerErrors errors = new RecordLayerErrors("Saving Changes", this);
try (
BaseCloseable eventsEnabled = eventsDisabled()) {
for (final LayerRecord record : validRecords) {
synchronized (this.getSync()) {
try {
final boolean saved = internalSaveChanges(errors, record);
if (!saved) {
errors.addRecord(record, "Unknown error");
}
} catch (final Throwable t) {
errors.addRecord(record, t);
}
}
}
cleanCachedRecords();
} finally {
if (!errors.showErrorDialog()) {
allSaved.add(false);
}
}
fireRecordsChanged();
}
final List<LayerRecord> invalidRecords = validator.getInvalidRecords();
if (!invalidRecords.isEmpty()) {
allSaved.add(false);
}
}, (validator) -> {
allSaved.add(false);
});
return allSaved.isEmpty();
}
} finally {
fireSelected();
fireHasChangedRecords();
}
}
public final boolean saveChanges(final LayerRecord... records) {
final List<LayerRecord> list = Arrays.asList(records);
return saveChanges(list);
}
public final boolean saveChanges(final LayerRecord record) {
// Includes two types of validation of record. The first checks field
// types before interacting with the record store. The second is any
// errors on the actual saving of data.
final Set<Boolean> allSaved = new HashSet<>();
RecordValidationDialog.validateRecords("Save Changes", //
this, //
record, (validator) -> {
// Success
// Save the valid records
final List<LayerRecord> validRecords = validator.getValidRecords();
if (!validRecords.isEmpty()) {
final RecordLayerErrors errors = new RecordLayerErrors("Saving Changes", this);
try (
BaseCloseable eventsEnabled = eventsDisabled()) {
synchronized (this.getSync()) {
try {
final boolean saved = internalSaveChanges(errors, record);
if (!saved) {
errors.addRecord(record, "Unknown error");
}
} catch (final Throwable t) {
errors.addRecord(record, t);
}
}
cleanCachedRecords();
record.fireRecordUpdated();
} finally {
if (!errors.showErrorDialog()) {
allSaved.add(false);
}
}
}
final List<LayerRecord> invalidRecords = validator.getInvalidRecords();
if (!invalidRecords.isEmpty()) {
allSaved.add(false);
}
}, (validator) -> {
allSaved.add(false);
});
return allSaved.isEmpty();
}
@Override
protected boolean saveChangesDo() {
throw new UnsupportedOperationException();
}
protected boolean saveChangesDo(final RecordLayerErrors errors, final LayerRecord record) {
return true;
}
public void setCanAddRecords(final boolean canAddRecords) {
this.canAddRecords = canAddRecords;
firePropertyChange("canAddRecords", !isCanAddRecords(), isCanAddRecords());
}
public void setCanDeleteRecords(final boolean canDeleteRecords) {
if (this.canDeleteRecords != canDeleteRecords) {
this.canDeleteRecords = canDeleteRecords;
firePropertyChange("canDeleteRecords", !isCanDeleteRecords(), isCanDeleteRecords());
}
}
public void setCanEditRecords(final boolean canEditRecords) {
this.canEditRecords = canEditRecords;
firePropertyChange("canEditRecords", !isCanEditRecords(), isCanEditRecords());
}
public void setCanPasteRecords(final boolean canPasteRecords) {
this.canPasteRecords = canPasteRecords;
}
@Override
public void setEditable(final boolean editable) {
Invoke.background("Set Editable " + this, () -> {
if (editable == false) {
firePropertyChange("preEditable", false, true);
final boolean hasChanges = isHasChanges();
if (hasChanges) {
final Integer result = Invoke.andWait(() -> {
return JOptionPane.showConfirmDialog(JOptionPane.getRootFrame(),
"The layer has unsaved changes. Click Yes to save changes. Click No to discard changes. Click Cancel to continue editing.",
"Save Changes", JOptionPane.YES_NO_CANCEL_OPTION);
});
synchronized (getSync()) {
if (result == JOptionPane.YES_OPTION) {
if (!saveChanges()) {
return;
}
} else if (result == JOptionPane.NO_OPTION) {
cancelChanges();
} else {
// Don't allow state change if cancelled
return;
}
}
}
}
synchronized (this.getSync()) {
super.setEditable(editable);
setCanAddRecords(this.canAddRecords);
setCanDeleteRecords(this.canDeleteRecords);
setCanEditRecords(this.canEditRecords);
}
});
}
public void setFieldColumnWidth(final String fieldName, final int columnWidth) {
this.fieldColumnWidths.put(fieldName, columnWidth);
}
public void setFieldColumnWidths(final Map<String, ? extends Number> fieldColumnWidths) {
this.fieldColumnWidths.clear();
for (final Entry<String, ? extends Number> entry : fieldColumnWidths.entrySet()) {
final String fieldName = entry.getKey();
final Number widthNumber = entry.getValue();
if (Property.hasValuesAll(fieldName, widthNumber)) {
final int width = widthNumber.intValue();
this.fieldColumnWidths.put(fieldName, width);
}
}
}
public void setFieldNamesSetName(final String fieldNamesSetName) {
final String oldValue = this.fieldNamesSetName;
if (Property.hasValue(fieldNamesSetName)) {
this.fieldNamesSetName = fieldNamesSetName;
} else {
this.fieldNamesSetName = ALL;
}
firePropertyChange("fieldNamesSetName", oldValue, this.fieldNamesSetName);
}
public void setFieldNamesSets(final Map<String, List<String>> fieldNamesSets) {
final List<String> allFieldNames = this.fieldNames;
this.fieldNamesSetNames.clear();
this.fieldNamesSetNames.add("All");
this.fieldNamesSets.clear();
if (fieldNamesSets != null) {
for (final Entry<String, List<String>> entry : fieldNamesSets.entrySet()) {
final String name = entry.getKey();
if (Property.hasValue(name)) {
final String upperName = name.toUpperCase();
final Collection<String> names = entry.getValue();
if (Property.hasValue(names)) {
final Set<String> fieldNames = new LinkedHashSet<>(names);
if (ALL.equalsIgnoreCase(name)) {
if (Property.hasValue(allFieldNames)) {
fieldNames.addAll(allFieldNames);
}
} else {
boolean found = false;
for (final String name2 : this.fieldNamesSetNames) {
if (name.equalsIgnoreCase(name2)) {
found = true;
Logs.error(this,
"Duplicate field set name " + name + "=" + name2 + " for layer " + getPath());
}
}
if (!found) {
this.fieldNamesSetNames.add(name);
}
}
if (Property.hasValue(allFieldNames)) {
fieldNames.retainAll(allFieldNames);
}
this.fieldNamesSets.put(upperName, new ArrayList<>(fieldNames));
}
}
}
}
getFieldNamesSet(ALL);
firePropertyChange("fieldNamesSets", null, this.fieldNamesSets);
}
public void setFilter(final Condition filter) {
final Object oldValue = this.filter;
this.where = null;
this.filter = filter;
firePropertyChange("filter", oldValue, this.filter);
}
@Override
protected boolean setGeometryFactoryDo(final GeometryFactory geometryFactory) {
this.index.setGeometryFactory(geometryFactory);
return super.setGeometryFactoryDo(geometryFactory);
}
public void setHighlightedRecords(final Collection<LayerRecord> highlightedRecords) {
synchronized (getSync()) {
clearCachedRecords(this.cacheIdHighlighted);
addHighlightedRecords(highlightedRecords);
}
}
protected void setIndexRecords(final List<LayerRecord> records) {
synchronized (getSync()) {
if (hasGeometryField()) {
final GeometryFactory geometryFactory = getGeometryFactory();
final LayerRecordQuadTree index = new LayerRecordQuadTree(geometryFactory);
final Label cacheIdIndex = getCacheIdIndex();
clearCachedRecords(cacheIdIndex);
if (records != null) {
for (final LayerRecord record : records) {
if (record.hasGeometry()) {
addRecordToCache(cacheIdIndex, record);
index.addRecord(record.newRecordProxy());
}
}
}
cleanCachedRecords();
final List<LayerRecord> newRecords = getRecordsNew();
index.addRecords(newRecords);
this.index = index;
}
}
}
@Override
public void setProperties(final Map<String, ? extends Object> properties) {
if (!properties.containsKey("style")) {
final GeometryStyleRenderer renderer = getRenderer();
if (renderer != null) {
renderer.setStyle(GeometryStyle.newStyle());
}
}
super.setProperties(properties);
final Predicate<Record> predicate = AbstractRecordLayerRenderer.getFilter(this, properties);
if (predicate instanceof RecordDefinitionSqlFilter) {
final RecordDefinitionSqlFilter sqlFilter = (RecordDefinitionSqlFilter)predicate;
setWhere(sqlFilter.getQuery());
}
if (this.fieldNamesSets.isEmpty()) {
setFieldNamesSets(null);
}
}
protected void setRecordDefinition(final RecordDefinition recordDefinition) {
this.recordDefinition = recordDefinition;
if (recordDefinition != null) {
final FieldDefinition geometryField = recordDefinition.getGeometryField();
GeometryFactory geometryFactory;
if (geometryField == null) {
geometryFactory = null;
setVisible(false);
setSelectSupported(false);
setRenderer(null);
} else {
geometryFactory = recordDefinition.getGeometryFactory();
}
setGeometryFactory(geometryFactory);
final String iconName = recordDefinition.getIconName();
final Icon icon = Icons.getIcon(iconName);
setIcon(icon);
this.fieldNames = recordDefinition.getFieldNames();
List<String> allFieldNames = this.fieldNamesSets.get(ALL.toUpperCase());
if (Property.hasValue(allFieldNames)) {
final Set<String> mergedFieldNames = new LinkedHashSet<>(allFieldNames);
mergedFieldNames.addAll(this.fieldNames);
mergedFieldNames.retainAll(this.fieldNames);
allFieldNames = new ArrayList<>(mergedFieldNames);
} else {
allFieldNames = new ArrayList<>(this.fieldNames);
}
this.fieldNamesSets.put(ALL.toUpperCase(), allFieldNames);
setWhere(this.where);
}
}
protected void setRecordFactory(final RecordFactory<? extends LayerRecord> recordFactory) {
this.recordFactory = recordFactory;
}
protected void setSelectedHighlighted(final LayerRecord record, final boolean selected,
final boolean highlighted) {
if (selected) {
addSelectedRecord(record);
if (highlighted) {
addHighlightedRecord(record);
}
}
}
public void setSelectedRecords(final BoundingBox boundingBox) {
if (isSelectable()) {
final List<LayerRecord> records = getRecordsVisible(boundingBox);
setSelectedRecords(records);
postSelectByBoundingBox(records);
}
}
public void setSelectedRecords(final Collection<LayerRecord> selectedRecords) {
final List<LayerRecord> oldSelectedRecords = getSelectedRecords();
synchronized (getSync()) {
clearCachedRecords(this.cacheIdSelected);
for (final LayerRecord record : selectedRecords) {
addSelectedRecord(record);
}
for (final LayerRecord record : getHighlightedRecords()) {
if (!isSelected(record)) {
removeHighlightedRecord(record);
}
}
}
final List<LayerRecord> newSelectedRecords = getSelectedRecords();
firePropertyChange(RECORDS_SELECTED, oldSelectedRecords, newSelectedRecords);
fireSelected();
}
public void setSelectedRecords(final LayerRecord... selectedRecords) {
setSelectedRecords(Arrays.asList(selectedRecords));
}
public void setSelectedRecords(final Query query) {
final List<LayerRecord> records = getRecords(query);
setSelectedRecords(records);
}
public void setSelectedRecordsById(final Identifier id) {
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition != null) {
final String idFieldName = recordDefinition.getIdFieldName();
if (idFieldName == null) {
clearSelectedRecords();
} else {
final Query query = Query.equal(recordDefinition, idFieldName, id);
setSelectedRecords(query);
}
}
}
public void setSnapLayerPaths(final Collection<String> snapLayerPaths) {
if (snapLayerPaths == null || snapLayerPaths.isEmpty()) {
removeProperty("snapLayers");
} else {
setProperty("snapLayers", new TreeSet<>(snapLayerPaths));
}
}
public void setSnapToAllLayers(final boolean snapToAllLayers) {
this.snapToAllLayers = snapToAllLayers;
}
@SuppressWarnings("unchecked")
public void setStyle(Object style) {
if (style instanceof Map) {
final Map<String, Object> map = (Map<String, Object>)style;
style = MapObjectFactory.toObject(map);
}
if (style instanceof AbstractRecordLayerRenderer) {
final AbstractRecordLayerRenderer renderer = (AbstractRecordLayerRenderer)style;
setRenderer(renderer);
} else {
Logs.error(this, "Cannot create renderer for: " + style);
}
}
public void setUseFieldTitles(final boolean useFieldTitles) {
this.useFieldTitles = useFieldTitles;
}
public void setUserReadOnlyFieldNames(final Collection<String> userReadOnlyFieldNames) {
this.userReadOnlyFieldNames = new LinkedHashSet<>(userReadOnlyFieldNames);
}
public void setWhere(final String where) {
final RecordDefinition recordDefinition = getRecordDefinition();
this.where = where;
if (recordDefinition != null) {
final Object oldValue = this.filter;
this.filter = QueryValue.parseWhere(recordDefinition, where);
firePropertyChange("filter", oldValue, this.filter);
}
}
public LayerRecord showAddForm(final Map<String, Object> values) {
if (isCanAddRecords()) {
final LayerRecord newRecord = newLayerRecord(values);
final LayerRecordForm form = newForm(newRecord);
if (form == null) {
return null;
} else {
try {
form.setAddRecord(newRecord);
if (form.showAddDialog()) {
return form.getAddRecord();
} else {
return null;
}
} finally {
form.setAddRecord(null);
}
}
} else {
final Window window = SwingUtil.getActiveWindow();
JOptionPane.showMessageDialog(window,
"Adding records is not enabled for the " + getPath()
+ " layer. If possible make the layer editable",
"Cannot Add Record", JOptionPane.ERROR_MESSAGE);
return null;
}
}
public void showForm(final LayerRecord record) {
showForm(record, null);
}
public void showForm(final LayerRecord record, final String fieldName) {
Invoke.later(() -> {
if (record != null && !record.isDeleted()) {
final LayerRecord proxiedRecord = getRecordProxied(record);
final int index;
Window window;
synchronized (this.formRecords) {
index = proxiedRecord.indexOf(this.formRecords);
if (index == -1) {
window = null;
} else {
window = this.formWindows.get(index);
}
}
Component form = null;
if (window == null) {
form = newForm(record);
if (form != null) {
final String title = LayerRecordForm.getTitle(record);
final Window parent = SwingUtil.getActiveWindow();
window = new BaseDialog(parent, title);
window.add(form);
window.pack();
if (form instanceof LayerRecordForm) {
final LayerRecordForm recordForm = (LayerRecordForm)form;
window.addWindowListener(recordForm);
if (record.getState() != RecordState.NEW) {
if (!isCanEditRecords()) {
recordForm.setEditable(false);
}
}
}
SwingUtil.autoAdjustPosition(window);
synchronized (this.formRecords) {
if (proxiedRecord.isDeleted()) {
window.dispose();
return;
} else {
this.formRecords.add(proxiedRecord);
this.formComponents.add(form);
this.formWindows.add(window);
}
}
window.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(final WindowEvent e) {
removeForm(record);
}
@Override
public void windowClosing(final WindowEvent e) {
removeForm(record);
}
});
SwingUtil.setVisible(window, true);
window.requestFocus();
if (proxiedRecord.isDeleted()) {
window.setVisible(false);
}
}
} else {
SwingUtil.setVisible(window, true);
window.requestFocus();
final Component component = window.getComponent(0);
if (component instanceof JScrollPane) {
final JScrollPane scrollPane = (JScrollPane)component;
form = scrollPane.getComponent(0);
}
}
if (form instanceof LayerRecordForm) {
final LayerRecordForm recordForm = (LayerRecordForm)form;
recordForm.setFieldFocussed(fieldName);
}
}
});
}
public void showRecordsTable() {
showRecordsTable(null);
}
public void showRecordsTable(final String fieldFilterMode) {
final Map<String, Object> config = Maps.newLinkedHash("fieldFilterMode", fieldFilterMode);
showTableView(config);
}
public List<LayerRecord> splitRecord(final LayerRecord record,
final CloseLocation mouseLocation) {
final Geometry geometry = mouseLocation.getGeometry();
if (geometry instanceof LineString) {
final LineString line = (LineString)geometry;
final int[] vertexId = mouseLocation.getVertexId();
final Point point = mouseLocation.getViewportPoint();
final Point convertedPoint = point.newGeometry(getGeometryFactory());
final LineString line1;
final LineString line2;
final int vertexCount = line.getVertexCount();
if (vertexId == null) {
final int vertexIndex = mouseLocation.getSegmentId()[0];
line1 = line.subLine(null, 0, vertexIndex + 1, convertedPoint);
line2 = line.subLine(convertedPoint, vertexIndex + 1, vertexCount - vertexIndex - 1, null);
} else {
final int pointIndex = vertexId[0];
if (pointIndex == 0) {
return Collections.singletonList(record);
} else if (vertexCount - pointIndex < 2) {
return Collections.singletonList(record);
} else {
line1 = line.subLine(pointIndex + 1);
line2 = line.subLine(null, pointIndex, vertexCount - pointIndex, null);
}
}
if (line1 == null || line2 == null) {
return Collections.singletonList(record);
}
return splitRecord(record, line, convertedPoint, line1, line2);
}
return Arrays.asList(record);
}
/** Perform the actual split. */
protected List<LayerRecord> splitRecord(final LayerRecord record, final LineString line,
final Point point, final LineString line1, final LineString line2) {
if (line1.getLength() == 0) {
if (line2.getLength() == 0) {
return Collections.singletonList(record);
} else {
record.setGeometryValue(line2);
saveChanges(record);
return Collections.singletonList(record);
}
} else if (line2.getLength() == 0) {
record.setGeometryValue(line1);
saveChanges(record);
return Collections.singletonList(record);
} else {
final Map<String, Object> values1 = newSplitValues(record, line, point, line1);
final LayerRecord record1 = newLayerRecord(values1);
final Map<String, Object> values2 = newSplitValues(record, line, point, line2);
final LayerRecord record2 = newLayerRecord(values2);
addSelectedRecords(record1, record2);
deleteRecord(record);
saveChanges(record, record1, record2);
return Arrays.asList(record1, record2);
}
}
public List<LayerRecord> splitRecord(final LayerRecord record, final Point point) {
final Geometry geometry = record.getGeometry();
if (geometry instanceof LineString) {
final LineString line = (LineString)geometry;
final List<LineString> lines = line.split(point);
if (lines.size() == 2) {
final LineString line1 = lines.get(0);
final LineString line2 = lines.get(1);
return splitRecord(record, line, point, line1, line2);
} else {
return Collections.singletonList(record);
}
} else {
return Collections.singletonList(record);
}
}
@Override
public MapEx toMap() {
final MapEx map = super.toMap();
if (!super.isReadOnly()) {
addToMap(map, "canAddRecords", this.canAddRecords);
addToMap(map, "canDeleteRecords", this.canDeleteRecords);
addToMap(map, "canEditRecords", this.canEditRecords);
addToMap(map, "canPasteRecords", this.canPasteRecords);
addToMap(map, "snapToAllLayers", this.snapToAllLayers);
}
addToMap(map, "fieldNamesSetName", this.fieldNamesSetName, ALL);
addToMap(map, "fieldNamesSets", getFieldNamesSets());
addToMap(map, "fieldColumnWidths", getFieldColumnWidths());
addToMap(map, "useFieldTitles", this.useFieldTitles);
map.remove("filter");
String where;
if (Property.isEmpty(this.filter)) {
where = this.where;
} else {
where = this.filter.toFormattedString();
}
if (Property.hasValue(where)) {
final RecordDefinitionSqlFilter filter = new RecordDefinitionSqlFilter(this, where);
addToMap(map, "filter", filter);
}
return map;
}
public void unHighlightRecords(final Collection<? extends LayerRecord> records) {
removeRecordsFromCache(this.cacheIdHighlighted, records);
fireHighlighted();
}
public void unHighlightRecords(final LayerRecord... records) {
unHighlightRecords(Arrays.asList(records));
}
public void unSelectRecords(final BoundingBox boundingBox) {
if (isSelectable()) {
final List<LayerRecord> records = getRecordsVisible(boundingBox);
unSelectRecords(records);
if (isHasSelectedRecordsWithGeometry()) {
showRecordsTable(RecordLayerTableModel.MODE_RECORDS_SELECTED);
}
}
}
public void unSelectRecords(final Collection<? extends LayerRecord> records) {
final List<LayerRecord> removedRecords = new ArrayList<>();
synchronized (getSync()) {
for (final LayerRecord record : records) {
if (removeSelectedRecord(record)) {
removedRecords.add(record);
}
}
unHighlightRecords(records);
}
if (!removedRecords.isEmpty()) {
firePropertyChange(RECORDS_SELECTED, removedRecords, null);
fireSelected();
}
}
public void unSelectRecords(final LayerRecord... records) {
unSelectRecords(Arrays.asList(records));
}
protected void updateRecordState(final LayerRecord record) {
final RecordState state = record.getState();
if (state == RecordState.MODIFIED) {
addModifiedRecord(record);
} else if (state == RecordState.PERSISTED) {
postSaveModifiedRecord(record);
fireHasChangedRecords();
}
}
protected void updateSpatialIndex(final LayerRecord record, final Geometry oldGeometry) {
if (oldGeometry != null) {
final BoundingBox oldBoundingBox = oldGeometry.getBoundingBox();
if (removeFromIndex(oldBoundingBox, record)) {
addToIndex(record);
}
}
}
public void zoomToBoundingBox(BoundingBox boundingBox) {
if (!BoundingBoxUtil.isEmpty(boundingBox)) {
final Project project = getProject();
final GeometryFactory geometryFactory = project.getGeometryFactory();
boundingBox = boundingBox.convert(geometryFactory);
boundingBox = boundingBox.expandPercent(0.1);
project.setViewBoundingBox(boundingBox);
}
}
public void zoomToGeometry(final Geometry geometry) {
if (geometry != null) {
final BoundingBox boundingBox = geometry.getBoundingBox();
zoomToBoundingBox(boundingBox);
}
}
public void zoomToHighlighted() {
final BoundingBox boundingBox = getHighlightedBoundingBox();
zoomToBoundingBox(boundingBox);
}
public void zoomToRecord(final Record record) {
if (record != null) {
final Geometry geometry = record.getGeometry();
zoomToGeometry(geometry);
}
}
public void zoomToRecords(final List<? extends LayerRecord> records) {
BoundingBox boundingBox = BoundingBox.empty();
for (final Record record : records) {
boundingBox = boundingBox.expandToInclude(record);
}
zoomToBoundingBox(boundingBox);
}
public void zoomToSelected() {
final BoundingBox selectedBoundingBox = getSelectedBoundingBox();
zoomToBoundingBox(selectedBoundingBox);
}
}