/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package de.cismet.cismap.commons.gui.featureinfopanel;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.simplify.TopologyPreservingSimplifier;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperPrintManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.util.JRLoader;
import org.apache.log4j.Logger;
import org.deegree.model.spatialschema.GeometryException;
import org.deegree.model.spatialschema.JTSAdapter;
import org.openide.util.NbBundle;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import de.cismet.cismap.commons.ServiceLayer;
import de.cismet.cismap.commons.features.DefaultFeatureServiceFeature;
import de.cismet.cismap.commons.features.Feature;
import de.cismet.cismap.commons.features.FeatureServiceFeature;
import de.cismet.cismap.commons.features.PermissionProvider;
import de.cismet.cismap.commons.features.PureNewFeature;
import de.cismet.cismap.commons.features.WMSFeature;
import de.cismet.cismap.commons.featureservice.AbstractFeatureService;
import de.cismet.cismap.commons.featureservice.FeatureServiceAttribute;
import de.cismet.cismap.commons.gui.MappingComponent;
import de.cismet.cismap.commons.gui.attributetable.AttributeTable;
import de.cismet.cismap.commons.gui.attributetable.AttributeTableFactory;
import de.cismet.cismap.commons.gui.attributetable.AttributeTableRuleSet;
import de.cismet.cismap.commons.gui.attributetable.FeatureLockerFactory;
import de.cismet.cismap.commons.gui.attributetable.FeatureLockingInterface;
import de.cismet.cismap.commons.gui.attributetable.LockAlreadyExistsException;
import de.cismet.cismap.commons.gui.featureinfowidget.FeatureInfoWidget;
import de.cismet.cismap.commons.gui.layerwidget.ActiveLayerModel;
import de.cismet.cismap.commons.gui.layerwidget.LayerCombobox;
import de.cismet.cismap.commons.gui.layerwidget.LayerFilter;
import de.cismet.cismap.commons.gui.layerwidget.ThemeLayerWidget;
import de.cismet.cismap.commons.gui.layerwidget.ZoomToFeaturesWorker;
import de.cismet.cismap.commons.gui.piccolo.eventlistener.GetFeatureInfoClickDetectionListener;
import de.cismet.cismap.commons.interaction.GetFeatureInfoListener;
import de.cismet.cismap.commons.interaction.events.ActiveLayerEvent;
import de.cismet.cismap.commons.interaction.events.GetFeatureInfoEvent;
import de.cismet.cismap.commons.interaction.events.MapClickedEvent;
import de.cismet.cismap.commons.raster.wms.WMSServiceLayer;
import de.cismet.cismap.commons.rasterservice.MapService;
import de.cismet.cismap.commons.tools.FeatureTools;
import de.cismet.tools.gui.CellSpecificRenderedTable;
import de.cismet.tools.gui.DefaultPopupMenuListener;
import de.cismet.tools.gui.StaticSwingTools;
import de.cismet.tools.gui.WaitingDialogThread;
/**
* DOCUMENT ME!
*
* @author therter
* @version $Revision$, $Date$
*/
public class FeatureInfoPanel extends javax.swing.JPanel {
//~ Static fields/initializers ---------------------------------------------
private static final Logger LOG = Logger.getLogger(FeatureInfoPanel.class);
private static final String REPORT_URL = "/de/cismet/cismap/commons/gui/featureinfopanel/InfoTableTemplate.jasper";
//~ Instance fields --------------------------------------------------------
private ActiveLayerModel layerModel;
private MappingComponent mappingComonent;
private ThemeLayerWidget themeLayerWidget;
private LayerFilterTreeModel model;
private FeatureInfoWidget featureInfo;
private DefaultPopupMenuListener popupMenuListener;
private List<FeatureServiceFeature> lockedFeatures = new ArrayList<FeatureServiceFeature>();
private AttribueTableModel currentTableModel;
private Map<Feature, Object> lockMap = new HashMap<Feature, Object>();
private TreeSet<DefaultFeatureServiceFeature> modifiedFeature = new TreeSet<DefaultFeatureServiceFeature>();
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTree jtFeatures;
private de.cismet.cismap.commons.gui.layerwidget.LayerCombobox layerCombobox1;
private javax.swing.JMenuItem miEdit;
private javax.swing.JMenuItem miPrint;
private javax.swing.JMenuItem miZoom;
private javax.swing.JPopupMenu popupMenu;
private javax.swing.JScrollPane sbAttributes;
private org.jdesktop.swingx.JXTable tabAttributes;
// End of variables declaration//GEN-END:variables
//~ Constructors -----------------------------------------------------------
/**
* Creates new form FeatureInfoPanel.
*/
public FeatureInfoPanel() {
this(null, null);
}
/**
* Creates new form FeatureInfoPanel.
*
* @param mappingComonent DOCUMENT ME!
* @param themeLayerWidget DOCUMENT ME!
*/
public FeatureInfoPanel(final MappingComponent mappingComonent, final ThemeLayerWidget themeLayerWidget) {
this.layerModel = (ActiveLayerModel)mappingComonent.getMappingModel();
this.mappingComonent = mappingComonent;
this.themeLayerWidget = themeLayerWidget;
initComponents();
popupMenuListener = new DefaultPopupMenuListener(popupMenu);
jtFeatures.addMouseListener(popupMenuListener);
featureInfo = new FeatureInfoWidget();
jtFeatures.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
jtFeatures.setCellRenderer(new DefaultTreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(final JTree tree,
final Object value,
final boolean sel,
final boolean expanded,
final boolean leaf,
final int row,
final boolean hasFocus) {
final Component c = super.getTreeCellRendererComponent(
tree,
value,
sel,
expanded,
leaf,
row,
hasFocus);
if (c instanceof JLabel) {
final JLabel cl = (JLabel)c;
final JLabel lab = new JLabel(cl.getText(), cl.getIcon(), cl.getHorizontalAlignment());
lab.setBackground(cl.getBackground());
lab.setBorder(cl.getBorder());
lab.setForeground(cl.getForeground());
if (sel) {
lab.setBackground(backgroundSelectionColor);
lab.setOpaque(true);
}
if ((value instanceof MapService) || value.equals(jtFeatures.getModel().getRoot())) {
if (expanded) {
lab.setIcon(openIcon);
} else {
lab.setIcon(closedIcon);
}
} else if ((value instanceof WMSGetFeatureInfoDescription) || (value instanceof WMSFeature)) {
lab.setIcon(
new ImageIcon(
getClass().getResource(
"/de/cismet/cismap/commons/gui/layerwidget/res/layerOverlaywms.png")));
} else if (value instanceof DefaultFeatureServiceFeature) {
final DefaultFeatureServiceFeature f = (DefaultFeatureServiceFeature)value;
lab.setIcon(
f.getLayerProperties().getFeatureService().getLayerIcon(
ServiceLayer.LAYER_ENABLED_VISIBLE));
if (f.isEditable()) {
final Font boldFont = lab.getFont().deriveFont(Font.BOLD);
lab.setFont(boldFont);
} else {
final Font plainFont = lab.getFont().deriveFont(Font.PLAIN);
lab.setFont(plainFont);
}
}
return lab;
} else {
c.setBackground(backgroundSelectionColor);
}
return c;
}
});
model = new LayerFilterTreeModel(layerModel, mappingComonent);
model.setLayerFilter((LayerFilter)layerCombobox1.getSelectedItem());
layerCombobox1.getModel().addListDataListener(new ListDataListener() {
@Override
public void intervalAdded(final ListDataEvent e) {
}
@Override
public void intervalRemoved(final ListDataEvent e) {
}
@Override
public void contentsChanged(final ListDataEvent e) {
expandAll(new TreePath(model.getRoot()));
}
});
jtFeatures.setModel(model);
mappingComonent.addGetFeatureInfoListener(new GetFeatureInfoListener() {
@Override
public void getFeatureInfoRequest(final GetFeatureInfoEvent evt) {
final boolean successful = contentChanged();
if (successful) {
model.init(evt.getFeatures());
expandAll(new TreePath(model.getRoot()));
}
}
});
tabAttributes.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
int col = tabAttributes.getTableHeader().getColumnModel().getColumnIndexAtX(e.getX());
col = tabAttributes.convertColumnIndexToModel(col);
final FeatureServiceFeature fsf = currentTableModel.getFeature();
if ((fsf != null) && !fsf.isEditable() && (col == 1) && (currentTableModel.tableRuleSet != null)) {
int row = tabAttributes.rowAtPoint(e.getPoint());
row = tabAttributes.convertRowIndexToModel(row);
final Object value = currentTableModel.getValueAt(row, col);
final String columnName = currentTableModel.getAttributeNameForRow(row);
currentTableModel.tableRuleSet.mouseClicked(
fsf,
columnName,
value,
e.getClickCount());
}
}
});
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param parent DOCUMENT ME!
*/
private void expandAll(final TreePath parent) {
final Object lastComponent = parent.getLastPathComponent();
final int childCount = model.getChildCount(lastComponent);
jtFeatures.expandPath(parent);
for (int i = 0; i < childCount; ++i) {
if (!model.isLeaf(model.getChild(lastComponent, i))) {
final TreePath newPath = parent.pathByAddingChild(model.getChild(lastComponent, i));
expandAll(newPath);
}
}
}
/**
* DOCUMENT ME!
*/
public void showAllFeatures() {
for (int i = 0; i < layerCombobox1.getItemCount(); ++i) {
if (layerCombobox1.getItemAt(i).getClass().getName().endsWith("AllLayersFilter")) {
layerCombobox1.setSelectedIndex(i);
break;
}
}
}
/**
* This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The
* content of this method is always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
popupMenu = new javax.swing.JPopupMenu();
miZoom = new javax.swing.JMenuItem();
miPrint = new javax.swing.JMenuItem();
miEdit = new javax.swing.JMenuItem();
layerCombobox1 = new LayerCombobox(layerModel, themeLayerWidget);
jLabel1 = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
jtFeatures = new javax.swing.JTree();
sbAttributes = new javax.swing.JScrollPane();
tabAttributes = new CellSpecificRenderedTable();
miZoom.setText(org.openide.util.NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.miZoom.text")); // NOI18N
miZoom.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(final java.awt.event.ActionEvent evt) {
miZoomActionPerformed(evt);
}
});
popupMenu.add(miZoom);
miPrint.setText(org.openide.util.NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.miPrint.text")); // NOI18N
miPrint.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(final java.awt.event.ActionEvent evt) {
miPrintActionPerformed(evt);
}
});
popupMenu.add(miPrint);
miEdit.setText(org.openide.util.NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.miEdit.text")); // NOI18N
miEdit.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(final java.awt.event.ActionEvent evt) {
miEditActionPerformed(evt);
}
});
popupMenu.add(miEdit);
setLayout(new java.awt.GridBagLayout());
layerCombobox1.addItemListener(new java.awt.event.ItemListener() {
@Override
public void itemStateChanged(final java.awt.event.ItemEvent evt) {
layerCombobox1ItemStateChanged(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(10, 0, 0, 5);
add(layerCombobox1, gridBagConstraints);
jLabel1.setText(org.openide.util.NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.jLabel1.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(10, 5, 0, 15);
add(jLabel1, gridBagConstraints);
jtFeatures.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
@Override
public void valueChanged(final javax.swing.event.TreeSelectionEvent evt) {
jtFeaturesValueChanged(evt);
}
});
jScrollPane1.setViewportView(jtFeatures);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 0.33;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
add(jScrollPane1, gridBagConstraints);
tabAttributes.setModel(new javax.swing.table.DefaultTableModel(
new Object[][] {},
new String[] {}));
sbAttributes.setViewportView(tabAttributes);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
add(sbAttributes, gridBagConstraints);
} // </editor-fold>//GEN-END:initComponents
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
private void layerCombobox1ItemStateChanged(final java.awt.event.ItemEvent evt) { //GEN-FIRST:event_layerCombobox1ItemStateChanged
if (evt.getStateChange() == ItemEvent.SELECTED) {
model.setLayerFilter((LayerFilter)evt.getItem());
expandAll(new TreePath(model.getRoot()));
}
} //GEN-LAST:event_layerCombobox1ItemStateChanged
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
private void jtFeaturesValueChanged(final javax.swing.event.TreeSelectionEvent evt) { //GEN-FIRST:event_jtFeaturesValueChanged
final TreePath tp = jtFeatures.getSelectionPath();
createPopupMenu();
if (tp == null) {
// there is nothing selected at the moment
tabAttributes.setModel(new DefaultTableModel(0, 0));
return;
}
final Object selectedComp = tp.getLastPathComponent();
if (selectedComp instanceof DefaultFeatureServiceFeature) {
final DefaultFeatureServiceFeature selectedFeature = (DefaultFeatureServiceFeature)selectedComp;
currentTableModel = new AttribueTableModel(selectedFeature);
tabAttributes.setModel(currentTableModel);
enableAttributeTable(true);
Geometry highlightingGeometry = selectedFeature.getGeometry();
if (highlightingGeometry.getCoordinates().length > 500) {
highlightingGeometry = TopologyPreservingSimplifier.simplify(highlightingGeometry, 30);
}
final Feature highligtingFeature = new PureNewFeature(highlightingGeometry);
mappingComonent.highlightFeature(highligtingFeature, 1500);
} else if (selectedComp instanceof WMSGetFeatureInfoDescription) {
// the default wms mechanism should be used
enableAttributeTable(false);
final WMSGetFeatureInfoDescription description = (WMSGetFeatureInfoDescription)selectedComp;
final ActiveLayerEvent e = new ActiveLayerEvent();
mappingComonent.highlightFeature(description, 1500);
description.getLayer().setLayerQuerySelected(true);
e.setLayer(description.getLayer());
featureInfo.layerAdded(e);
featureInfo.clickedOnMap(new MapClickedEvent(
GetFeatureInfoClickDetectionListener.FEATURE_INFO_MODE,
description.getpInputEvent()));
} else {
enableAttributeTable(true);
tabAttributes.setModel(new DefaultTableModel(0, 0));
}
} //GEN-LAST:event_jtFeaturesValueChanged
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
private void miZoomActionPerformed(final java.awt.event.ActionEvent evt) { //GEN-FIRST:event_miZoomActionPerformed
final TreePath[] tps = jtFeatures.getSelectionPaths();
final List<Feature> featureList = new ArrayList<Feature>();
for (final TreePath tp : tps) {
final Object o = tp.getLastPathComponent();
if (o instanceof Feature) {
final Feature fsf = (Feature)o;
featureList.add(fsf);
} else if (o instanceof MapService) {
for (int i = 0; i < model.getChildCount(o); ++i) {
final Object featureObject = model.getChild(o, i);
if (featureObject instanceof Feature) {
featureList.add((Feature)featureObject);
}
}
}
}
final ZoomToFeaturesWorker worker = new ZoomToFeaturesWorker(featureList.toArray(
new Feature[featureList.size()]),
10);
worker.execute();
} //GEN-LAST:event_miZoomActionPerformed
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
private void miEditActionPerformed(final java.awt.event.ActionEvent evt) { //GEN-FIRST:event_miEditActionPerformed
final TreePath[] tps = jtFeatures.getSelectionPaths();
for (final TreePath tp : tps) {
final Object o = tp.getLastPathComponent();
final List<DefaultFeatureServiceFeature> allFeatures = new ArrayList<DefaultFeatureServiceFeature>();
boolean setEditable = true;
if (o instanceof AbstractFeatureService) {
for (int i = 0; i < model.getChildCount(o); ++i) {
final Object child = model.getChild(o, i);
if (child instanceof DefaultFeatureServiceFeature) {
allFeatures.add((DefaultFeatureServiceFeature)child);
if (((DefaultFeatureServiceFeature)child).isEditable()) {
setEditable = false;
}
}
}
} else if (o instanceof DefaultFeatureServiceFeature) {
allFeatures.add((DefaultFeatureServiceFeature)o);
setEditable = !((DefaultFeatureServiceFeature)o).isEditable();
}
for (final DefaultFeatureServiceFeature feature : allFeatures) {
if ((feature != null) && !feature.isEditable() && setEditable) {
// start edit mode
final FeatureServiceFeature fsf = (FeatureServiceFeature)feature;
startEditMode(fsf);
} else if ((feature != null) && feature.isEditable() && !setEditable) {
// stop edit mode
final DefaultFeatureServiceFeature f = (DefaultFeatureServiceFeature)feature;
stopEditMode(f);
}
}
}
repaint();
createPopupMenu();
} //GEN-LAST:event_miEditActionPerformed
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
private void miPrintActionPerformed(final java.awt.event.ActionEvent evt) { //GEN-FIRST:event_miPrintActionPerformed
final TreePath tps = jtFeatures.getSelectionPath();
final WaitingDialogThread<JasperPrint> wdt = new WaitingDialogThread<JasperPrint>(StaticSwingTools
.getParentFrame(this),
true,
NbBundle.getMessage(
AttributeTable.class,
"AttributeTable.butPrintActionPerformed.WaitingDialogThread"),
null,
500) {
@Override
protected JasperPrint doInBackground() throws Exception {
final Object o = tps.getLastPathComponent();
final DefaultFeatureServiceFeature feature = (DefaultFeatureServiceFeature)o;
final JRDataSource ds = new FeaturePanelAttributeSource(new AttribueTableModel(feature));
final Map<String, Object> map = new HashMap<String, Object>();
map.put("ds", ds);
map.put("title", feature.toString());
map.put(
"key",
NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.jtFeaturesValueChanged.name"));
map.put(
"value",
NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.jtFeaturesValueChanged.value"));
final JasperReport jasperReport = (JasperReport)JRLoader.loadObject(getClass().getResourceAsStream(
REPORT_URL));
return JasperFillManager.fillReport(jasperReport, map, ds);
}
@Override
protected void done() {
try {
final JasperPrint jasperPrint = get();
JasperPrintManager.printReport(jasperPrint, true);
} catch (Exception e) {
LOG.error("Error while creating report", e);
}
}
};
wdt.start();
} //GEN-LAST:event_miPrintActionPerformed
/**
* DOCUMENT ME!
*
* @param fsf DOCUMENT ME!
*/
private void startEditMode(final FeatureServiceFeature fsf) {
if (fsf.getLayerProperties().getFeatureService().isEditable()) {
if (fsf instanceof PermissionProvider) {
if (!((PermissionProvider)fsf).hasWritePermissions()) {
JOptionPane.showMessageDialog(
this,
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.makeFeatureEditable.noPermissions.text"),
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.makeFeatureEditable.noPermissions.title"),
JOptionPane.ERROR_MESSAGE);
return;
}
}
final FeatureLockingInterface locker = FeatureLockerFactory.getInstance()
.getLockerForFeatureService(fsf.getLayerProperties().getFeatureService());
try {
if (locker != null) {
lockMap.put(fsf, locker.lock(fsf, false));
if (!lockedFeatures.contains(fsf)) {
lockedFeatures.add(fsf);
// ((DefaultFeatureServiceFeature)fsf).addPropertyChangeListener(model);
}
}
fsf.setEditable(true);
} catch (LockAlreadyExistsException ex) {
JOptionPane.showMessageDialog(
FeatureInfoPanel.this,
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.miEditActionPerformed().lockexists.message",
fsf.getId(),
ex.getLockMessage()),
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.miEditActionPerformed().lockexists.title"),
JOptionPane.ERROR_MESSAGE);
} catch (Exception ex) {
LOG.error("Error while locking feature.", ex);
JOptionPane.showMessageDialog(
FeatureInfoPanel.this,
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.miEditActionPerformed().exception.message",
ex.getMessage()),
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.miEditActionPerformed().exception.title"),
JOptionPane.ERROR_MESSAGE);
}
}
}
/**
* DOCUMENT ME!
*
* @param f DOCUMENT ME!
*/
private void stopEditMode(final DefaultFeatureServiceFeature f) {
try {
final FeatureLockingInterface locker = FeatureLockerFactory.getInstance()
.getLockerForFeatureService(f.getLayerProperties().getFeatureService());
final AttributeTableRuleSet tableRuleSet = f.getLayerProperties().getAttributeTableRuleSet();
if ((tableRuleSet != null) && !tableRuleSet.prepareForSave(lockedFeatures)) {
return;
}
if ((tableRuleSet != null) && modifiedFeature.contains(f)) {
tableRuleSet.beforeSave(f);
}
// stop the cell renderer, if it is active
if ((tabAttributes.getEditingColumn() != -1) && (tabAttributes.getEditingRow() != -1)) {
tabAttributes.getCellEditor(tabAttributes.getEditingRow(),
tabAttributes.getEditingColumn()).stopCellEditing();
}
if (modifiedFeature.contains(f)) {
f.saveChanges();
}
f.setEditable(false);
if (locker != null) {
final Object lockingObject = lockMap.get(f);
if (lockingObject != null) {
locker.unlock(lockingObject);
lockMap.remove(f);
}
}
lockedFeatures.remove(f);
modifiedFeature.remove(f);
if ((tableRuleSet != null) && modifiedFeature.contains(f)) {
tableRuleSet.afterSave(null);
}
if (f.getLayerProperties().getFeatureService() != null) {
f.getLayerProperties().getFeatureService().retrieve(true);
}
} catch (Exception e) {
LOG.error("Error while saving feature", e);
}
}
/**
* Checks, if the FeatureInfoPanel contains unsaved changes and let the user decide, whether the content should be
* saved or not.
*
* @return false, if the close operation should be aborted
*/
private boolean contentChanged() {
if (!modifiedFeature.isEmpty() || !lockedFeatures.isEmpty()) {
FeatureServiceFeature feature;
if (!modifiedFeature.isEmpty()) {
feature = modifiedFeature.first();
} else {
feature = lockedFeatures.get(0);
}
final int ans = JOptionPane.showConfirmDialog(
FeatureInfoPanel.this,
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.contentChanged().text",
feature.getLayerProperties().getFeatureService().getName()),
NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.contentChanged().title"),
JOptionPane.YES_NO_CANCEL_OPTION);
if (ans == JOptionPane.YES_OPTION) {
saveFeatureChanges(feature);
} else if (ans == JOptionPane.NO_OPTION) {
unlockAll();
} else {
return false;
}
}
return true;
}
/**
* Should be invoked, before the FeatureInfoPanel is closed. It checks, if there are unsaved changes.
*
* @return false, if the close operation should be aborted
*/
public boolean dispose() {
final boolean successful = contentChanged();
if (successful) {
unlockAll();
model.init(new ArrayList<Feature>());
}
return successful;
}
/**
* Save all changed features of the given service.
*
* @param f service all changed features of this service will be saved.
*/
private void saveFeatureChanges(final FeatureServiceFeature f) {
final List<DefaultFeatureServiceFeature> savedFeatureList = new ArrayList<DefaultFeatureServiceFeature>();
if (f instanceof DefaultFeatureServiceFeature) {
final DefaultFeatureServiceFeature feature = (DefaultFeatureServiceFeature)f;
stopEditMode(feature);
savedFeatureList.add(feature);
}
for (final DefaultFeatureServiceFeature fe : savedFeatureList) {
lockMap.remove(fe);
}
}
/**
* unlocks all locked objects of the given service.
*/
private void unlockAll() {
boolean allLocksRemoved = true;
final List<DefaultFeatureServiceFeature> unlockedFeatureList = new ArrayList<DefaultFeatureServiceFeature>();
for (final Feature f : lockMap.keySet()) {
if (f instanceof DefaultFeatureServiceFeature) {
final DefaultFeatureServiceFeature feature = (DefaultFeatureServiceFeature)f;
final FeatureLockingInterface locker = FeatureLockerFactory.getInstance()
.getLockerForFeatureService(feature.getLayerProperties().getFeatureService());
if (locker != null) {
try {
locker.unlock(lockMap.get(feature));
feature.setEditable(false);
unlockedFeatureList.add(feature);
} catch (Exception e) {
LOG.error("Locking object can't be removed.", e);
allLocksRemoved = false;
}
} else {
LOG.error("No suitable locker object found");
allLocksRemoved = false;
}
}
}
if (!allLocksRemoved) {
JOptionPane.showMessageDialog(
FeatureInfoPanel.this,
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.unlockAll().message"),
NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.unlockAll().title"),
JOptionPane.ERROR_MESSAGE);
}
for (final DefaultFeatureServiceFeature f : unlockedFeatureList) {
lockMap.remove(f);
lockedFeatures.remove(f);
}
modifiedFeature.clear();
}
/**
* Creates the context menu of the FeatureInfoPanel.
*/
private void createPopupMenu() {
final TreePath tp = jtFeatures.getSelectionPath();
if (tp == null) {
// no element selected
return;
}
final Object c = tp.getLastPathComponent();
popupMenu.removeAll();
popupMenu.add(miZoom);
popupMenu.add(miPrint);
if (c instanceof DefaultFeatureServiceFeature) {
if ((((DefaultFeatureServiceFeature)c).getLayerProperties().getFeatureService() != null)
&& ((DefaultFeatureServiceFeature)c).getLayerProperties().getFeatureService().isEditable()) {
if (lockedFeatures.contains((DefaultFeatureServiceFeature)c) || lockedFeatures.isEmpty()) {
popupMenu.add(miEdit);
}
if (((DefaultFeatureServiceFeature)c).isEditable()) {
miEdit.setText(NbBundle.getMessage(
FeatureInfoPanel.class,
"FeatureInfoPanel.miEdit.text.editable"));
} else {
miEdit.setText(NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.miEdit.text"));
}
}
}
}
/**
* DOCUMENT ME!
*
* @param enable DOCUMENT ME!
*/
private void enableAttributeTable(final boolean enable) {
sbAttributes.getViewport().remove(tabAttributes);
sbAttributes.getViewport().remove(featureInfo);
if (enable) {
sbAttributes.getViewport().add(tabAttributes);
} else {
sbAttributes.getViewport().add(featureInfo);
}
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public FeatureServiceFeature getSelectedFeature() {
final AttribueTableModel tabModel = (AttribueTableModel)tabAttributes.getModel();
if (tabModel != null) {
return tabModel.getFeature();
}
return null;
}
//~ Inner Classes ----------------------------------------------------------
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
private class LayerFilterTreeModel implements TreeModel {
//~ Instance fields ----------------------------------------------------
List<Feature> lastFeatures;
private final MappingComponent mappingComponent;
private final ActiveLayerModel layerModel;
private LayerFilter filter;
private final String root = NbBundle.getMessage(
LayerFilterTreeModel.class,
"FeatureInfoPanel.LayerFilterTreeModel.root");
private final Map<MapService, List<Feature>> data = new HashMap<MapService, List<Feature>>();
private final List<MapService> orderedDataKeys = new ArrayList<MapService>();
private final List<TreeModelListener> listener = new ArrayList<TreeModelListener>();
//~ Constructors -------------------------------------------------------
/**
* Creates a new LayerFilterTreeModel object.
*
* @param layerModel DOCUMENT ME!
* @param mappingComponent DOCUMENT ME!
*/
public LayerFilterTreeModel(final ActiveLayerModel layerModel, final MappingComponent mappingComponent) {
this.layerModel = layerModel;
this.mappingComponent = mappingComponent;
}
//~ Methods ------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param service DOCUMENT ME!
* @param f DOCUMENT ME!
* @param tp DOCUMENT ME!
*/
public void featureExchange(final MapService service, final Feature f, final TreePath tp) {
final List<Feature> featureList = data.get(service);
int index = featureList.indexOf(f);
if (index != -1) {
featureList.set(index, f);
}
index = lastFeatures.indexOf(f);
if (index != -1) {
lastFeatures.set(index, f);
}
for (final TreeModelListener l : listener) {
l.treeStructureChanged(new TreeModelEvent(this, tp));
}
}
/**
* DOCUMENT ME!
*
* @param filter DOCUMENT ME!
*/
public void setLayerFilter(final LayerFilter filter) {
this.filter = filter;
if (lastFeatures != null) {
init(lastFeatures);
}
}
/**
* DOCUMENT ME!
*
* @param features DOCUMENT ME!
*/
private void init(final List<Feature> features) {
final TreeMap<Integer, MapService> treeMap = layerModel.getMapServices();
final List<MapService> allowedMapServices = new ArrayList<MapService>();
final List<AbstractFeatureService> allowedFeatureServices = new ArrayList<AbstractFeatureService>();
lastFeatures = features;
data.clear();
orderedDataKeys.clear();
for (final Integer serviceKey : treeMap.keySet()) {
final MapService service = treeMap.get(serviceKey);
if (filter.isLayerAllowed(service)) {
if (service instanceof AbstractFeatureService) {
allowedFeatureServices.add((AbstractFeatureService)service);
} else {
allowedMapServices.add(service);
}
}
}
if ((features.size() == 1) && !(features.get(0) instanceof WMSFeature)) {
final Feature f = features.get(0);
final AbstractFeatureService service = ((FeatureServiceFeature)f).getLayerProperties()
.getFeatureService();
if ((f instanceof FeatureServiceFeature) && !allowedMapServices.contains(service)) {
if (filter.isLayerAllowed(service)) {
allowedFeatureServices.add(service);
}
}
}
for (final Feature feature : features) {
MapService service = null;
if (feature instanceof WMSGetFeatureInfoDescription) {
final WMSGetFeatureInfoDescription gfid = (WMSGetFeatureInfoDescription)feature;
service = getMapServiceOfWMSServiceLayer(allowedMapServices, gfid.getService());
} else if (feature instanceof WMSFeature) {
final WMSFeature wmsFeature = (WMSFeature)feature;
service = getMapServiceOfWMSServiceLayer(allowedMapServices, wmsFeature.getWMSServiceLayer());
} else if (feature instanceof FeatureServiceFeature) {
service = getFeatureServiceOfFeature(allowedFeatureServices, (FeatureServiceFeature)feature);
}
if (service != null) {
List<Feature> featureList = data.get(service);
if (featureList == null) {
featureList = new ArrayList<Feature>();
data.put(service, featureList);
orderedDataKeys.add(service);
}
featureList.add(feature);
}
}
// sort the services in the same order they are sorted in the mapping model
Collections.sort(orderedDataKeys, new Comparator<MapService>() {
Map<MapService, Integer> serviceOrderMap = new HashMap<MapService, Integer>();
{
for (final Integer serviceOrder : treeMap.keySet()) {
final MapService service = treeMap.get(serviceOrder);
serviceOrderMap.put(service, serviceOrder);
}
}
@Override
public int compare(final MapService o1, final MapService o2) {
final Integer order1 = serviceOrderMap.get(o1);
final Integer order2 = serviceOrderMap.get(o2);
if ((order1 == null) && (order2 == null)) {
return 0;
} else if (order1 == null) {
return 1;
} else if (order2 == null) {
return -1;
} else {
return -1 * order1.compareTo(order2);
}
}
});
fireTreeStructureChanged();
}
/**
* DOCUMENT ME!
*
* @param serviceList DOCUMENT ME!
* @param feature DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private AbstractFeatureService getFeatureServiceOfFeature(final List<AbstractFeatureService> serviceList,
final FeatureServiceFeature feature) {
for (final AbstractFeatureService service : serviceList) {
if (service.getLayerProperties() == feature.getLayerProperties()) {
return service;
}
}
return null;
}
/**
* DOCUMENT ME!
*
* @param serviceList DOCUMENT ME!
* @param featureService DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private MapService getMapServiceOfWMSServiceLayer(final List<MapService> serviceList,
final WMSServiceLayer featureService) {
for (final MapService service : serviceList) {
if (service == featureService) {
return service;
}
}
return null;
}
@Override
public Object getRoot() {
return root;
}
@Override
public Object getChild(final Object parent, final int index) {
if (parent == root) {
// a MapService instance will be returned
return orderedDataKeys.get(index);
} else if (parent instanceof MapService) {
// a feature instance will be returned
return data.get((MapService)parent).get(index);
} else {
// should never happen
return null;
}
}
@Override
public int getChildCount(final Object parent) {
if (parent == root) {
return orderedDataKeys.size();
} else if (parent instanceof MapService) {
return data.get((MapService)parent).size();
} else {
return 0;
}
}
@Override
public boolean isLeaf(final Object node) {
if (node == root) {
return orderedDataKeys.isEmpty();
} else if (node instanceof MapService) {
return data.get((MapService)node).isEmpty();
} else {
return true;
}
}
@Override
public void valueForPathChanged(final TreePath path, final Object newValue) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int getIndexOfChild(final Object parent, final Object child) {
if (parent == root) {
// a MapService instance will be returned
return orderedDataKeys.indexOf(child);
} else if (parent instanceof MapService) {
// a feature instance will be returned
return data.get((MapService)parent).indexOf(child);
} else {
// should never happen
LOG.error("parent is of type " + parent.getClass().getName() + ". This should never happen");
return -1;
}
}
@Override
public void addTreeModelListener(final TreeModelListener l) {
listener.add(l);
}
@Override
public void removeTreeModelListener(final TreeModelListener l) {
listener.remove(l);
}
/**
* DOCUMENT ME!
*/
private void fireTreeStructureChanged() {
for (final TreeModelListener l : listener) {
l.treeStructureChanged(new TreeModelEvent(this, new Object[] { root }));
}
}
}
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
class AttribueTableModel implements TableModel {
//~ Instance fields ----------------------------------------------------
private final DefaultFeatureServiceFeature feature;
private final AttributeTableRuleSet tableRuleSet;
private String[] attributeAlias;
private String[] attributeNames;
private Map<String, FeatureServiceAttribute> featureServiceAttributes;
private List<String> orderedFeatureServiceAttributes;
private final List<TableModelListener> listener = new ArrayList<TableModelListener>();
//~ Constructors -------------------------------------------------------
/**
* Creates a new AttribueTableModel object.
*
* @param feature DOCUMENT ME!
*/
public AttribueTableModel(final DefaultFeatureServiceFeature feature) {
this.feature = feature;
tableRuleSet = feature.getLayerProperties().getAttributeTableRuleSet();
initModel();
initTable();
}
//~ Methods ------------------------------------------------------------
/**
* DOCUMENT ME!
*/
private void initModel() {
if (feature instanceof WMSFeature) {
final HashMap<String, Object> props = feature.getProperties();
orderedFeatureServiceAttributes = new ArrayList<String>(props.keySet());
featureServiceAttributes = new HashMap<String, FeatureServiceAttribute>();
for (final String key : props.keySet()) {
final FeatureServiceAttribute attr = new FeatureServiceAttribute(key, "String", false);
featureServiceAttributes.put(key, attr);
}
} else {
final AbstractFeatureService service = feature.getLayerProperties().getFeatureService();
orderedFeatureServiceAttributes = service.getOrderedFeatureServiceAttributes();
featureServiceAttributes = service.getFeatureServiceAttributes();
}
fillHeaderArrays();
}
/**
* DOCUMENT ME!
*/
private void initTable() {
((CellSpecificRenderedTable)tabAttributes).removeAllCellEditors();
((CellSpecificRenderedTable)tabAttributes).removeAllCellRenderers();
final AttributeTableCellRenderer defaultRenderer = new AttributeTableCellRenderer();
tabAttributes.setDefaultRenderer(Object.class, defaultRenderer);
if (tableRuleSet != null) {
for (int i = 0; i < getRowCount(); ++i) {
final String columnName;
columnName = attributeNames[i];
final TableCellEditor editor = tableRuleSet.getCellEditor(columnName);
final TableCellRenderer renderer = tableRuleSet.getCellRenderer(columnName);
if (editor != null) {
((CellSpecificRenderedTable)tabAttributes).addCellEditor(1, i, editor);
}
if (renderer != null) {
((CellSpecificRenderedTable)tabAttributes).addCellRenderer(1, i, renderer);
}
}
}
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public FeatureServiceFeature getFeature() {
return feature;
}
/**
* DOCUMENT ME!
*/
private void fillHeaderArrays() {
int index = 0;
attributeNames = new String[attributeCount()];
attributeAlias = new String[attributeCount()];
for (final String attributeName : orderedFeatureServiceAttributes) {
final FeatureServiceAttribute fsa = featureServiceAttributes.get(attributeName);
if ((fsa == null) || fsa.isVisible()) {
attributeNames[index] = attributeName;
String aliasName = attributeName;
if ((fsa != null) && !fsa.getAlias().equals("")) {
final String alias = fsa.getAlias();
if (alias != null) {
aliasName = alias;
}
}
if (aliasName.startsWith("app:")) {
attributeAlias[index++] = aliasName.substring(4);
} else {
attributeAlias[index++] = aliasName;
}
}
}
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private int attributeCount() {
int count = 0;
for (final String key : orderedFeatureServiceAttributes) {
final FeatureServiceAttribute fsa = featureServiceAttributes.get(key);
if (fsa.isVisible()) {
++count;
}
}
return count;
}
@Override
public boolean isCellEditable(final int row, final int column) {
if (column == 1) {
if (row < attributeNames.length) {
if (tableRuleSet != null) {
return feature.isEditable() && tableRuleSet.isColumnEditable(attributeNames[row])
&& feature.getLayerProperties().getFeatureService().isEditable();
} else {
return feature.isEditable() && feature.getLayerProperties().getFeatureService().isEditable();
}
}
}
return false;
}
@Override
public int getRowCount() {
return attributeNames.length;
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(final int columnIndex) {
if (columnIndex == 0) {
return NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.jtFeaturesValueChanged.name");
} else {
return NbBundle.getMessage(FeatureInfoPanel.class, "FeatureInfoPanel.jtFeaturesValueChanged.value");
}
}
@Override
public Class<?> getColumnClass(final int columnIndex) {
return Object.class;
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
Object value;
if (columnIndex == 0) {
value = attributeAlias[rowIndex];
} else {
value = feature.getProperty(attributeNames[rowIndex]);
}
if (value instanceof Geometry) {
value = ((Geometry)value).getGeometryType();
} else if (value instanceof org.deegree.model.spatialschema.Geometry) {
final org.deegree.model.spatialschema.Geometry geom = ((org.deegree.model.spatialschema.Geometry)value);
try {
value = JTSAdapter.export(geom).getGeometryType();
} catch (GeometryException e) {
LOG.error("Error while transforming deegree geometry to jts geometry.", e);
}
}
return value;
}
/**
* DOCUMENT ME!
*
* @param rowIndex DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public String getAttributeNameForRow(final int rowIndex) {
return attributeNames[rowIndex];
}
@Override
public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
try {
final String attrName = attributeNames[rowIndex];
final FeatureServiceAttribute attr = featureServiceAttributes.get(attrName);
final Class cl = FeatureTools.getClass(attr);
Object valueWithType = FeatureTools.convertObjectToClass(aValue, cl);
if (tableRuleSet != null) {
valueWithType = tableRuleSet.afterEdit(
feature,
attrName,
-1,
feature.getProperty(attrName),
aValue);
}
feature.setProperty(attrName, valueWithType);
if (!modifiedFeature.contains(feature)) {
modifiedFeature.add(feature);
}
} catch (Exception e) {
LOG.error("Cannot determine the required object type", e);
}
}
@Override
public void addTableModelListener(final TableModelListener l) {
listener.add(l);
}
@Override
public void removeTableModelListener(final TableModelListener l) {
listener.remove(l);
}
/**
* DOCUMENT ME!
*/
private void fireContentsChanged() {
final TableModelEvent e = new TableModelEvent(this);
for (final TableModelListener tmp : listener) {
tmp.tableChanged(e);
}
}
/**
* DOCUMENT ME!
*
* @param e DOCUMENT ME!
*/
private void fireContentsChanged(final TableModelEvent e) {
for (final TableModelListener tmp : listener) {
tmp.tableChanged(e);
}
}
//~ Inner Classes ------------------------------------------------------
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
private class AttributeTableCellRenderer extends DefaultTableCellRenderer {
//~ Instance fields ------------------------------------------------
private DecimalFormat format;
//~ Constructors ---------------------------------------------------
/**
* Creates a new AttributeTableCellRenderer object.
*/
public AttributeTableCellRenderer() {
format = new DecimalFormat();
format.setGroupingUsed(false);
}
//~ Methods --------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param table DOCUMENT ME!
* @param value DOCUMENT ME!
* @param isSelected DOCUMENT ME!
* @param hasFocus DOCUMENT ME!
* @param row DOCUMENT ME!
* @param column DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
@Override
public Component getTableCellRendererComponent(final JTable table,
final Object value,
final boolean isSelected,
final boolean hasFocus,
final int row,
final int column) {
Object formattedValue = value;
if (column == 1) {
// convert values only in the data column
final String key = attributeNames[row];
final FeatureServiceAttribute attr = featureServiceAttributes.get(key);
final Class cl = FeatureTools.getClass(attr);
if (value != null) {
if (Date.class.isAssignableFrom(cl) && (value instanceof Date)) {
formattedValue = DateFormat.getDateInstance().format((Date)value);
} else if (Double.class.isAssignableFrom(cl) && (value instanceof Double)) {
formattedValue = format.format(value);
}
}
}
final Component c = super.getTableCellRendererComponent(
table,
formattedValue,
isSelected,
hasFocus,
row,
column);
if (feature.isEditable()) {
if (row < attributeNames.length) {
if (tableRuleSet != null) {
if (!tableRuleSet.isColumnEditable(attributeNames[row])) {
final JLabel lab = new JLabel(((JLabel)c).getText(),
((JLabel)c).getIcon(),
((JLabel)c).getHorizontalAlignment());
lab.setBackground(((JLabel)c).getBackground());
lab.setForeground(Color.LIGHT_GRAY);
lab.setOpaque(true);
return lab;
}
}
}
}
return c;
}
}
}
}