/*************************************************** * * 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.features; import com.vividsolutions.jts.geom.Geometry; import org.h2.jdbc.JdbcClob; import java.awt.Color; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedReader; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import de.cismet.cismap.commons.featureservice.H2AttributeTableRuleSet; import de.cismet.cismap.commons.interaction.CismapBroker; import de.cismet.cismap.commons.util.SelectionManager; import de.cismet.cismap.linearreferencing.tools.StationEditorInterface; /** * DOCUMENT ME! * * @author therter * @version $Revision$, $Date$ */ public class JDBCFeature extends DefaultFeatureServiceFeature implements ModifiableFeature { //~ Static fields/initializers --------------------------------------------- // caches the last feature properties private static final Object sync = new Object(); private static final String DELETE_STATEMENT = "DELETE FROM \"%1s\" WHERE \"%2s\" = %3s;"; //~ Instance fields -------------------------------------------------------- private Map<String, StationEditorInterface> stations = null; private Color backgroundColor; private final JDBCFeatureInfo featureInfo; private boolean modified = false; //~ Constructors ----------------------------------------------------------- /** * Creates a new ShapeFeature object. * * @param shapeInfo typename DOCUMENT ME! * @param styles DOCUMENT ME! */ public JDBCFeature(final JDBCFeatureInfo shapeInfo, final List<org.deegree.style.se.unevaluated.Style> styles) { setSLDStyles(styles); // super.style = styles; this.featureInfo = shapeInfo; } //~ Methods ---------------------------------------------------------------- @Override public void setEditable(final boolean editable) { final boolean oldEditableStatus = isEditable(); super.setEditable(editable); if (oldEditableStatus != editable) { modified = false; if (!editable && (stations != null)) { for (final String key : stations.keySet()) { stations.get(key).dispose(); } stations.clear(); } else { CismapBroker.getInstance().getMappingComponent().getFeatureCollection().unholdFeature(this); CismapBroker.getInstance().getMappingComponent().getFeatureCollection().removeFeature(this); } if (editable) { final H2AttributeTableRuleSet tableRuleSet = (H2AttributeTableRuleSet)getLayerProperties() .getAttributeTableRuleSet(); if (!((tableRuleSet.getAllLinRefInfos() != null) && !tableRuleSet.getAllLinRefInfos().isEmpty())) { CismapBroker.getInstance().getMappingComponent().getFeatureCollection().addFeature(this); CismapBroker.getInstance().getMappingComponent().getFeatureCollection().holdFeature(this); SelectionManager.getInstance().addSelectedFeatures(Collections.nCopies(1, this)); setBackgroundColor(new Color(255, 91, 0)); } } } } @Override public HashMap getProperties() { if (existProperties()) { return super.getProperties(); } LinkedHashMap<String, Object> container = null; final int id = getId(); try { container = featureInfo.getPropertiesFromCache(id); if (container != null) { return container; } else { container = new LinkedHashMap<String, Object>(); } ResultSet rs = null; final PreparedStatement ps = featureInfo.getPropertiesStatement(); ps.setInt(1, id); rs = ps.executeQuery(); if (rs.next()) { final int count = rs.getMetaData().getColumnCount(); for (int i = 0; i < count; ++i) { container.put(rs.getMetaData().getColumnName(i + 1), getPrepareObject(rs.getObject(i + 1))); } } featureInfo.addPropertiesToCache(id, container); } catch (final Exception e) { logger.error("Cannot read properties from the database.", e); } return container; } /** * DOCUMENT ME! * * @param propertyName DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public Object getProperty(final String propertyName) { if (existProperties()) { return super.getProperties().get(propertyName); } Object result; final int id = getId(); final String cacheId = id + "@" + propertyName; result = featureInfo.getPropertyFromCache(cacheId); if (result == null) { try { final PreparedStatement ps = featureInfo.getPreparedStatementForProperty(propertyName); ps.setInt(1, id); final ResultSet rs = ps.executeQuery(); if (rs.next()) { result = getPrepareObject(rs.getObject(1)); } featureInfo.addPropertyToCache(cacheId, result); } catch (final Exception e) { logger.error("Cannot read property from the database.", e); } } return result; } /** * DOCUMENT ME! * * @param o DOCUMENT ME! * * @return DOCUMENT ME! */ private Object getPrepareObject(final Object o) { if (o instanceof JdbcClob) { try { final BufferedReader r = new BufferedReader(((JdbcClob)o).getCharacterStream()); String tmp; final StringBuilder resultString = new StringBuilder(); while ((tmp = r.readLine()) != null) { resultString.append(tmp).append('\n'); } return resultString.toString(); } catch (Exception e) { logger.error("Error while reading clob", e); return null; } } else { return o; } } /** * DOCUMENT ME! * * @param propertyName DOCUMENT ME! * @param propertyValue DOCUMENT ME! */ @Override public void setProperty(final String propertyName, final Object propertyValue) { if (!existProperties()) { super.setProperties(getProperties()); } super.addProperty(propertyName, propertyValue); featureInfo.clearCache(); if (isEditable()) { modified = true; } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ private boolean existProperties() { return !super.getProperties().isEmpty(); } @Override public FeatureServiceFeature saveChanges() throws Exception { saveChangesWithoutReload(); return this; } @Override public void saveChangesWithoutReload() throws Exception { if (!existProperties()) { // no changes return; } final String checkSql = "SELECT \"id\" FROM \"%1s\" WHERE \"id\" = %2s"; final Statement st = featureInfo.getConnection().createStatement(); try { final String sql = String.format(checkSql, featureInfo.getTableName(), getId()); final ResultSet rs = st.executeQuery(sql); final boolean alreadyExists = ((rs != null) && rs.next()); if (rs != null) { rs.close(); } if (alreadyExists) { updateFeature(st); } else { addFeature(st); } } finally { if (st != null) { st.close(); } } } /** * DOCUMENT ME! * * @param st DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ private void updateFeature(final Statement st) throws Exception { final HashMap map = super.getProperties(); final StringBuilder update = new StringBuilder("UPDATE \""); update.append(featureInfo.getTableName()).append("\" SET "); boolean first = true; for (final Object name : map.keySet()) { final Object value = map.get(name); if (!first) { update.append(", "); } else { first = false; } String valueString; if ((value instanceof String) || (value instanceof Geometry)) { valueString = "'" + value + "'"; } else { valueString = String.valueOf(value); } update.append("\"").append(name).append("\"").append(" = ").append(valueString); } update.append(" WHERE \"id\" = ").append(getId()); st.executeUpdate(update.toString()); super.getProperties().clear(); } /** * DOCUMENT ME! * * @param st DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ private void addFeature(final Statement st) throws Exception { final HashMap map = super.getProperties(); final String insertSql = "INSERT INTO \"%1s\" (%2s) VALUES (%3s)"; final List<String> attributes = new ArrayList<String>(); final List<String> values = new ArrayList<String>(); for (final Object name : map.keySet()) { final Object value = map.get(name); String valueString; if ((value instanceof String) || (value instanceof Geometry) || (value instanceof java.sql.Timestamp)) { valueString = "'" + value + "'"; } else { valueString = value.toString(); } attributes.add("\"" + String.valueOf(name) + "\""); values.add(valueString); } attributes.add("\"" + String.valueOf("id") + "\""); values.add(String.valueOf(getId())); final String query = String.format( insertSql, featureInfo.getTableName(), listToString(attributes), listToString(values)); st.executeUpdate(query); super.getProperties().clear(); } /** * DOCUMENT ME! * * @param attributes DOCUMENT ME! * * @return DOCUMENT ME! */ private String listToString(final List<String> attributes) { boolean firstElement = true; final StringBuilder sb = new StringBuilder(); for (final String element : attributes) { if (firstElement) { firstElement = false; } else { sb.append(","); } sb.append(element); } return sb.toString(); } @Override public void delete() throws Exception { final String deleteStat = String.format( DELETE_STATEMENT, featureInfo.getTableName(), featureInfo.getIdField(), getId()); final Statement st = featureInfo.getConnection().createStatement(); st.executeUpdate(deleteStat); } /** * DOCUMENT ME! */ @Override public void undoAll() { super.getProperties().clear(); } /** * DOCUMENT ME! * * @param properties DOCUMENT ME! */ @Override public void setProperties(final HashMap properties) { // nothing to do } /** * DOCUMENT ME! * * @param colName DOCUMENT ME! * * @return DOCUMENT ME! */ public Object getStationEditor(final String colName) { if (stations != null) { return stations.get(colName); } else { return null; } } /** * DOCUMENT ME! * * @param colName DOCUMENT ME! * @param editor DOCUMENT ME! */ public void setStationEditor(final String colName, final StationEditorInterface editor) { if (stations == null) { stations = new HashMap<String, StationEditorInterface>(); } stations.put(colName, editor); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public PropertyChangeListener getPropertyChangeListener() { return new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { for (final String name : stations.keySet()) { setProperty(name, stations.get(name).getValue()); firePropertyChange(name, evt.getOldValue(), evt.getNewValue()); } } }; } /** * DOCUMENT ME! * * @param propertyName DOCUMENT ME! * @param property DOCUMENT ME! */ @Override public void addProperty(final String propertyName, final Object property) { // nothing to do } /** * DOCUMENT ME! * * @param map DOCUMENT ME! */ @Override public void addProperties(final Map<String, Object> map) { // nothing to do } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public Geometry getGeometry() { if (existProperties()) { return (Geometry)super.getProperty(featureInfo.getGeoField()); } else { return getOriginalGeometry(); } } /** * Provides the geometry from the database. The content of the property container will be ignored. * * @return The geometry that is currently saved within the database */ private Geometry getOriginalGeometry() { Geometry g = null; g = featureInfo.getGeometryFromCache(getId()); if (g != null) { return g; } ResultSet rs = null; try { final PreparedStatement ps = featureInfo.getGeometryStatement(); ps.setInt(1, getId()); rs = ps.executeQuery(); if (rs.next()) { g = (Geometry)rs.getObject(1); g.setSRID(featureInfo.getSrid()); } } catch (final Exception e) { logger.error("Cannot read geometry from the database.", e); } finally { if (rs != null) { try { rs.close(); } catch (SQLException ex) { // nothing to do } } } featureInfo.addGeometryToCache(getId(), g); return g; } /** * DOCUMENT ME! * * @param geom DOCUMENT ME! */ @Override public void setGeometry(final Geometry geom) { if (!existProperties()) { super.setProperties(getProperties()); } final Geometry oldGeom = getGeometry(); if (((oldGeom == null) != (geom == null)) || ((oldGeom != null) && (geom != null) && !oldGeom.equalsExact(geom))) { // the old geometry and the new geometry are different featureInfo.clearCache(); super.addProperty(featureInfo.getGeoField(), geom); if (isEditable()) { modified = true; } } } @Override public boolean equals(final Object obj) { if (obj instanceof JDBCFeature) { final JDBCFeature other = (JDBCFeature)obj; if ((getId() != -1) || (other.getId() != -1)) { return featureInfo.getTableName().equals(other.featureInfo.getTableName()) && (getId() == other.getId()); } else { return obj == other; } } return false; } @Override public int hashCode() { int hash = 7; hash = (41 * hash) + this.getId(); hash = (41 * hash) + (((this.featureInfo != null) && (this.featureInfo.getTableName() != null)) ? this.featureInfo.getTableName().hashCode() : 0); return hash; } /** * DOCUMENT ME! * * @return the backgroundColor */ public Color getBackgroundColor() { return backgroundColor; } /** * DOCUMENT ME! * * @param backgroundColor the backgroundColor to set */ public void setBackgroundColor(final Color backgroundColor) { this.backgroundColor = backgroundColor; } @Override public boolean isFeatureChanged() { final Geometry geom = getGeometry(); final Geometry backupGeometry = getOriginalGeometry(); if (((backupGeometry == null) != (geom == null)) || ((backupGeometry != null) && (geom != null) && !backupGeometry.equalsExact(geom))) { // The geometry will not changed with the setGeometry() method, but also within the geometry object itself. return true; } else { return modified; } } }